Merge "Dell EMC: PowerMax - Configurable SRDF snapshots"
This commit is contained in:
commit
08835cab08
@ -450,6 +450,7 @@ class PowerMaxData(object):
|
|||||||
rep_extra_specs['sync_retries'] = 200
|
rep_extra_specs['sync_retries'] = 200
|
||||||
rep_extra_specs['rdf_group_label'] = rdf_group_name_1
|
rep_extra_specs['rdf_group_label'] = rdf_group_name_1
|
||||||
rep_extra_specs['rdf_group_no'] = rdf_group_no_1
|
rep_extra_specs['rdf_group_no'] = rdf_group_no_1
|
||||||
|
rep_extra_specs[utils.DISABLE_PROTECTED_SNAP] = False
|
||||||
rep_extra_specs2 = deepcopy(rep_extra_specs)
|
rep_extra_specs2 = deepcopy(rep_extra_specs)
|
||||||
rep_extra_specs2[utils.PORTGROUPNAME] = port_group_name_f
|
rep_extra_specs2[utils.PORTGROUPNAME] = port_group_name_f
|
||||||
rep_extra_specs3 = deepcopy(rep_extra_specs)
|
rep_extra_specs3 = deepcopy(rep_extra_specs)
|
||||||
|
@ -105,6 +105,7 @@ class PowerMaxReplicationTest(test.TestCase):
|
|||||||
extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
|
extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
|
||||||
extra_specs[utils.IS_RE] = True
|
extra_specs[utils.IS_RE] = True
|
||||||
extra_specs[utils.FORCE_VOL_EDIT] = True
|
extra_specs[utils.FORCE_VOL_EDIT] = True
|
||||||
|
extra_specs[utils.DISABLE_PROTECTED_SNAP] = False
|
||||||
rep_config = self.data.rep_config_sync
|
rep_config = self.data.rep_config_sync
|
||||||
rep_config = deepcopy(self.data.rep_config_sync)
|
rep_config = deepcopy(self.data.rep_config_sync)
|
||||||
rep_config[utils.RDF_CONS_EXEMPT] = False
|
rep_config[utils.RDF_CONS_EXEMPT] = False
|
||||||
|
@ -1574,7 +1574,8 @@ class PowerMaxCommon(object):
|
|||||||
if rep_config.get(utils.METROBIAS):
|
if rep_config.get(utils.METROBIAS):
|
||||||
extra_specs[utils.METROBIAS] = (
|
extra_specs[utils.METROBIAS] = (
|
||||||
rep_config[utils.METROBIAS])
|
rep_config[utils.METROBIAS])
|
||||||
|
extra_specs[utils.DISABLE_PROTECTED_SNAP] =\
|
||||||
|
self.utils.is_protected_snap_disabled(extra_specs)
|
||||||
return extra_specs, qos_specs
|
return extra_specs, qos_specs
|
||||||
|
|
||||||
def _get_replicated_volume_backend_id(self, volume):
|
def _get_replicated_volume_backend_id(self, volume):
|
||||||
@ -2074,14 +2075,23 @@ class PowerMaxCommon(object):
|
|||||||
return volume_name
|
return volume_name
|
||||||
|
|
||||||
array = extra_specs[utils.ARRAY]
|
array = extra_specs[utils.ARRAY]
|
||||||
if self.utils.is_replication_enabled(extra_specs):
|
dps = self.utils.is_protected_snap_disabled(extra_specs)
|
||||||
self._validate_rdfg_status(array, extra_specs)
|
# If a volume is not replicated and has the
|
||||||
|
# powermax:disable_protected_snap set to True,
|
||||||
|
# then clean up the volume without replication cleanup.
|
||||||
|
if dps and volume.replication_status is None:
|
||||||
|
self.masking.remove_and_reset_members(
|
||||||
|
array, volume, device_id, volume_name, extra_specs, False)
|
||||||
|
self._cleanup_device_retry(array, device_id, extra_specs)
|
||||||
|
else:
|
||||||
|
if self.utils.is_replication_enabled(extra_specs):
|
||||||
|
self._validate_rdfg_status(array, extra_specs)
|
||||||
|
|
||||||
self._cleanup_device_retry(array, device_id, extra_specs)
|
self._cleanup_device_retry(array, device_id, extra_specs)
|
||||||
|
|
||||||
# Remove from any storage groups and cleanup replication
|
# Remove from any storage groups and cleanup replication
|
||||||
self._remove_vol_and_cleanup_replication(
|
self._remove_vol_and_cleanup_replication(
|
||||||
array, device_id, volume_name, extra_specs, volume)
|
array, device_id, volume_name, extra_specs, volume)
|
||||||
self._delete_from_srp(
|
self._delete_from_srp(
|
||||||
array, device_id, volume_name, extra_specs)
|
array, device_id, volume_name, extra_specs)
|
||||||
return volume_name
|
return volume_name
|
||||||
@ -2898,6 +2908,10 @@ class PowerMaxCommon(object):
|
|||||||
create_snap, copy_mode, rep_extra_specs = False, False, dict()
|
create_snap, copy_mode, rep_extra_specs = False, False, dict()
|
||||||
volume_dict = self.rest.get_volume(array, source_device_id)
|
volume_dict = self.rest.get_volume(array, source_device_id)
|
||||||
replication_enabled = self.utils.is_replication_enabled(extra_specs)
|
replication_enabled = self.utils.is_replication_enabled(extra_specs)
|
||||||
|
if self.utils.is_protected_snap_disabled(extra_specs):
|
||||||
|
extra_specs.pop(utils.IS_RE, None)
|
||||||
|
replication_enabled = False
|
||||||
|
|
||||||
if replication_enabled:
|
if replication_enabled:
|
||||||
copy_mode = True
|
copy_mode = True
|
||||||
__, rep_extra_specs, __, __ = (
|
__, rep_extra_specs, __, __ = (
|
||||||
@ -5778,6 +5792,9 @@ class PowerMaxCommon(object):
|
|||||||
bias = True if rep_config.get(utils.METROBIAS) else False
|
bias = True if rep_config.get(utils.METROBIAS) else False
|
||||||
rep_extra_specs[utils.METROBIAS] = bias
|
rep_extra_specs[utils.METROBIAS] = bias
|
||||||
|
|
||||||
|
rep_extra_specs[utils.DISABLE_PROTECTED_SNAP] =\
|
||||||
|
self.utils.is_protected_snap_disabled(extra_specs)
|
||||||
|
|
||||||
# If disable compression is set, check if target array is all flash
|
# If disable compression is set, check if target array is all flash
|
||||||
do_disable_compression = self.utils.is_compression_disabled(
|
do_disable_compression = self.utils.is_compression_disabled(
|
||||||
extra_specs)
|
extra_specs)
|
||||||
@ -7800,3 +7817,20 @@ class PowerMaxCommon(object):
|
|||||||
'dev_ident': dev_id_from_identifier})
|
'dev_ident': dev_id_from_identifier})
|
||||||
self.rest.rename_volume(
|
self.rest.rename_volume(
|
||||||
array, dev_id_from_identifier, None)
|
array, dev_id_from_identifier, None)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_vendor_properties(self):
|
||||||
|
"""Retrieves the vendor properties for the powermax driver.
|
||||||
|
|
||||||
|
:param self: The object instance.
|
||||||
|
:return: A tuple containing the properties dictionary and the
|
||||||
|
driver name.
|
||||||
|
"""
|
||||||
|
properties = {}
|
||||||
|
self._set_property(
|
||||||
|
properties,
|
||||||
|
utils.DISABLE_PROTECTED_SNAP,
|
||||||
|
"Disable protected snap",
|
||||||
|
_("Prevent protected snap being created on SRDF device."),
|
||||||
|
"boolean")
|
||||||
|
return properties, "powermax"
|
||||||
|
@ -134,9 +134,10 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
|
|||||||
4.4.1 - Report trim/discard support
|
4.4.1 - Report trim/discard support
|
||||||
4.5.0 - Add PowerMax v4 support
|
4.5.0 - Add PowerMax v4 support
|
||||||
4.5.1 - Add active/active compliance
|
4.5.1 - Add active/active compliance
|
||||||
|
4.5.2 - Add 'disable_protected_snap' option
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "4.5.1"
|
VERSION = "4.5.2"
|
||||||
SUPPORTS_ACTIVE_ACTIVE = True
|
SUPPORTS_ACTIVE_ACTIVE = True
|
||||||
|
|
||||||
# ThirdPartySystems wiki
|
# ThirdPartySystems wiki
|
||||||
@ -166,6 +167,9 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
|
|||||||
def check_for_setup_error(self):
|
def check_for_setup_error(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _init_vendor_properties(self):
|
||||||
|
return self.common.get_vendor_properties(self)
|
||||||
|
|
||||||
def create_volume(self, volume):
|
def create_volume(self, volume):
|
||||||
"""Creates a PowerMax/VMAX volume.
|
"""Creates a PowerMax/VMAX volume.
|
||||||
|
|
||||||
|
@ -139,9 +139,10 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
|
|||||||
4.4.1 - Report trim/discard support
|
4.4.1 - Report trim/discard support
|
||||||
4.5.0 - Add PowerMax v4 support
|
4.5.0 - Add PowerMax v4 support
|
||||||
4.5.1 - Add active/active compliance
|
4.5.1 - Add active/active compliance
|
||||||
|
4.5.2 - Add 'disable_protected_snap' option
|
||||||
"""
|
"""
|
||||||
|
|
||||||
VERSION = "4.5.1"
|
VERSION = "4.5.2"
|
||||||
SUPPORTS_ACTIVE_ACTIVE = True
|
SUPPORTS_ACTIVE_ACTIVE = True
|
||||||
|
|
||||||
# ThirdPartySystems wiki
|
# ThirdPartySystems wiki
|
||||||
@ -171,6 +172,9 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
|
|||||||
def check_for_setup_error(self):
|
def check_for_setup_error(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def _init_vendor_properties(self):
|
||||||
|
return self.common.get_vendor_properties(self)
|
||||||
|
|
||||||
def create_volume(self, volume):
|
def create_volume(self, volume):
|
||||||
"""Creates a PowerMax/VMAX volume.
|
"""Creates a PowerMax/VMAX volume.
|
||||||
|
|
||||||
@ -255,7 +259,6 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
|
|||||||
:param context: the context
|
:param context: the context
|
||||||
:param volume_id: the volume id
|
:param volume_id: the volume id
|
||||||
"""
|
"""
|
||||||
pass
|
|
||||||
|
|
||||||
def initialize_connection(self, volume, connector):
|
def initialize_connection(self, volume, connector):
|
||||||
"""Initializes the connection and returns connection info.
|
"""Initializes the connection and returns connection info.
|
||||||
@ -461,15 +464,15 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
|
|||||||
data['driver_version'] = self.VERSION
|
data['driver_version'] = self.VERSION
|
||||||
self._stats = data
|
self._stats = data
|
||||||
|
|
||||||
def manage_existing(self, volume, external_ref):
|
def manage_existing(self, volume, existing_ref):
|
||||||
"""Manages an existing PowerMax/VMAX Volume (import to Cinder).
|
"""Manages an existing PowerMax/VMAX Volume (import to Cinder).
|
||||||
|
|
||||||
Renames the Volume to match the expected name for the volume.
|
Renames the Volume to match the expected name for the volume.
|
||||||
Also need to consider things like QoS, Emulation, account/tenant.
|
Also need to consider things like QoS, Emulation, account/tenant.
|
||||||
"""
|
"""
|
||||||
return self.common.manage_existing(volume, external_ref)
|
return self.common.manage_existing(volume, existing_ref)
|
||||||
|
|
||||||
def manage_existing_get_size(self, volume, external_ref):
|
def manage_existing_get_size(self, volume, existing_ref):
|
||||||
"""Return size of an existing PowerMax/VMAX volume to manage_existing.
|
"""Return size of an existing PowerMax/VMAX volume to manage_existing.
|
||||||
|
|
||||||
:param self: reference to class
|
:param self: reference to class
|
||||||
@ -477,7 +480,7 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
|
|||||||
:param external_ref: reference to the existing volume
|
:param external_ref: reference to the existing volume
|
||||||
:returns: size of the volume in GB
|
:returns: size of the volume in GB
|
||||||
"""
|
"""
|
||||||
return self.common.manage_existing_get_size(volume, external_ref)
|
return self.common.manage_existing_get_size(volume, existing_ref)
|
||||||
|
|
||||||
def unmanage(self, volume):
|
def unmanage(self, volume):
|
||||||
"""Export PowerMax/VMAX volume from Cinder.
|
"""Export PowerMax/VMAX volume from Cinder.
|
||||||
@ -550,10 +553,10 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
|
|||||||
return self.common.get_manageable_snapshots(marker, limit, offset,
|
return self.common.get_manageable_snapshots(marker, limit, offset,
|
||||||
sort_keys, sort_dirs)
|
sort_keys, sort_dirs)
|
||||||
|
|
||||||
def retype(self, ctxt, volume, new_type, diff, host):
|
def retype(self, context, volume, new_type, diff, host):
|
||||||
"""Migrate volume to another host using retype.
|
"""Migrate volume to another host using retype.
|
||||||
|
|
||||||
:param ctxt: context
|
:param context: context
|
||||||
:param volume: the volume object including the volume_type_id
|
:param volume: the volume object including the volume_type_id
|
||||||
:param new_type: the new volume type.
|
:param new_type: the new volume type.
|
||||||
:param diff: difference between old and new volume types.
|
:param diff: difference between old and new volume types.
|
||||||
|
@ -140,6 +140,7 @@ REST_API_CONNECT_TIMEOUT = 'rest_api_connect_timeout'
|
|||||||
REST_API_READ_TIMEOUT = 'rest_api_read_timeout'
|
REST_API_READ_TIMEOUT = 'rest_api_read_timeout'
|
||||||
REST_API_CONNECT_TIMEOUT_KEY = 'RestAPIConnectTimeout'
|
REST_API_CONNECT_TIMEOUT_KEY = 'RestAPIConnectTimeout'
|
||||||
REST_API_READ_TIMEOUT_KEY = 'RestAPIReadTimeout'
|
REST_API_READ_TIMEOUT_KEY = 'RestAPIReadTimeout'
|
||||||
|
DISABLE_PROTECTED_SNAP = 'powermax:disable_protected_snap'
|
||||||
|
|
||||||
# Array Models, Service Levels & Workloads
|
# Array Models, Service Levels & Workloads
|
||||||
VMAX_HYBRID_MODELS = ['VMAX100K', 'VMAX200K', 'VMAX400K']
|
VMAX_HYBRID_MODELS = ['VMAX100K', 'VMAX200K', 'VMAX400K']
|
||||||
@ -530,6 +531,15 @@ class PowerMaxUtils(object):
|
|||||||
host_list = host.split('+')
|
host_list = host.split('+')
|
||||||
return host_list[-1]
|
return host_list[-1]
|
||||||
|
|
||||||
|
def is_protected_snap_disabled(self, extra_specs):
|
||||||
|
"""Check is the disable_protected_snap flag set.
|
||||||
|
|
||||||
|
:param extra_specs: extra specifications :returns: boolean
|
||||||
|
"""
|
||||||
|
if extra_specs.get(DISABLE_PROTECTED_SNAP, False) in IS_TRUE:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def is_compression_disabled(self, extra_specs):
|
def is_compression_disabled(self, extra_specs):
|
||||||
"""Check is compression is to be disabled.
|
"""Check is compression is to be disabled.
|
||||||
|
|
||||||
|
@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
Dell EMC PowerMax driver: Added SRDF ``powermax:disable_protected_snap``
|
||||||
|
volume-type extra-spec property for the purpose of avoiding
|
||||||
|
overconsumption on both source and target storage arrays.
|
||||||
|
|
||||||
|
An operator may enable this functionality by creating a specific volume
|
||||||
|
type with the property::
|
||||||
|
|
||||||
|
"powermax:disable_protected_snap": "<is> True"
|
||||||
|
|
||||||
|
When disabled (which is the default and current behavior), a
|
||||||
|
replicated source volume will be protected with a snapshot of the
|
||||||
|
same volume type.
|
||||||
|
|
||||||
|
When enabled, snapshots of replicated source volumes will be treated
|
||||||
|
as regular, non-replicated devices.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user