Dell EMC: PowerMax - Configurable SRDF snapshots

Add volume type extra spec boolean property
'powermax:disable_protected_snap'. Enabled when set to: `'<is> True'`,
`'True'`, `'true'`, or True, otherwise the property is disabled by
default.

When set to True, snapshot won't be replicated and won't
match the source volume type, it will be a regular device
no matter what.

If the property is not enabled in the volume type extra_specs, then
there is no change from the current behavior.

Implements blueprint powermax-protected-snap-config

Change-Id: Iafa44dcf0e8f46749b5ef37f0b8d341e8253a3bd
This commit is contained in:
Bryan Neumann 2024-02-29 09:32:58 -05:00
parent 20fe5b51f6
commit 7e38339201
7 changed files with 88 additions and 16 deletions

View File

@ -450,6 +450,7 @@ class PowerMaxData(object):
rep_extra_specs['sync_retries'] = 200
rep_extra_specs['rdf_group_label'] = rdf_group_name_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[utils.PORTGROUPNAME] = port_group_name_f
rep_extra_specs3 = deepcopy(rep_extra_specs)

View File

@ -105,6 +105,7 @@ class PowerMaxReplicationTest(test.TestCase):
extra_specs[utils.PORTGROUPNAME] = self.data.port_group_name_f
extra_specs[utils.IS_RE] = True
extra_specs[utils.FORCE_VOL_EDIT] = True
extra_specs[utils.DISABLE_PROTECTED_SNAP] = False
rep_config = self.data.rep_config_sync
rep_config = deepcopy(self.data.rep_config_sync)
rep_config[utils.RDF_CONS_EXEMPT] = False

View File

@ -1574,7 +1574,8 @@ class PowerMaxCommon(object):
if rep_config.get(utils.METROBIAS):
extra_specs[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
def _get_replicated_volume_backend_id(self, volume):
@ -2074,14 +2075,23 @@ class PowerMaxCommon(object):
return volume_name
array = extra_specs[utils.ARRAY]
if self.utils.is_replication_enabled(extra_specs):
self._validate_rdfg_status(array, extra_specs)
dps = self.utils.is_protected_snap_disabled(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
self._remove_vol_and_cleanup_replication(
array, device_id, volume_name, extra_specs, volume)
# Remove from any storage groups and cleanup replication
self._remove_vol_and_cleanup_replication(
array, device_id, volume_name, extra_specs, volume)
self._delete_from_srp(
array, device_id, volume_name, extra_specs)
return volume_name
@ -2898,6 +2908,10 @@ class PowerMaxCommon(object):
create_snap, copy_mode, rep_extra_specs = False, False, dict()
volume_dict = self.rest.get_volume(array, source_device_id)
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:
copy_mode = True
__, rep_extra_specs, __, __ = (
@ -5778,6 +5792,9 @@ class PowerMaxCommon(object):
bias = True if rep_config.get(utils.METROBIAS) else False
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
do_disable_compression = self.utils.is_compression_disabled(
extra_specs)
@ -7800,3 +7817,20 @@ class PowerMaxCommon(object):
'dev_ident': dev_id_from_identifier})
self.rest.rename_volume(
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"

View File

@ -134,9 +134,10 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
4.4.1 - Report trim/discard support
4.5.0 - Add PowerMax v4 support
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
# ThirdPartySystems wiki
@ -166,6 +167,9 @@ class PowerMaxFCDriver(san.SanDriver, driver.FibreChannelDriver):
def check_for_setup_error(self):
pass
def _init_vendor_properties(self):
return self.common.get_vendor_properties(self)
def create_volume(self, volume):
"""Creates a PowerMax/VMAX volume.

View File

@ -139,9 +139,10 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
4.4.1 - Report trim/discard support
4.5.0 - Add PowerMax v4 support
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
# ThirdPartySystems wiki
@ -171,6 +172,9 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
def check_for_setup_error(self):
pass
def _init_vendor_properties(self):
return self.common.get_vendor_properties(self)
def create_volume(self, volume):
"""Creates a PowerMax/VMAX volume.
@ -255,7 +259,6 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
:param context: the context
:param volume_id: the volume id
"""
pass
def initialize_connection(self, volume, connector):
"""Initializes the connection and returns connection info.
@ -461,15 +464,15 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
data['driver_version'] = self.VERSION
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).
Renames the Volume to match the expected name for the volume.
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.
:param self: reference to class
@ -477,7 +480,7 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
:param external_ref: reference to the existing volume
: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):
"""Export PowerMax/VMAX volume from Cinder.
@ -550,10 +553,10 @@ class PowerMaxISCSIDriver(san.SanISCSIDriver):
return self.common.get_manageable_snapshots(marker, limit, offset,
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.
:param ctxt: context
:param context: context
:param volume: the volume object including the volume_type_id
:param new_type: the new volume type.
:param diff: difference between old and new volume types.

View File

@ -140,6 +140,7 @@ REST_API_CONNECT_TIMEOUT = 'rest_api_connect_timeout'
REST_API_READ_TIMEOUT = 'rest_api_read_timeout'
REST_API_CONNECT_TIMEOUT_KEY = 'RestAPIConnectTimeout'
REST_API_READ_TIMEOUT_KEY = 'RestAPIReadTimeout'
DISABLE_PROTECTED_SNAP = 'powermax:disable_protected_snap'
# Array Models, Service Levels & Workloads
VMAX_HYBRID_MODELS = ['VMAX100K', 'VMAX200K', 'VMAX400K']
@ -530,6 +531,15 @@ class PowerMaxUtils(object):
host_list = host.split('+')
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):
"""Check is compression is to be disabled.

View File

@ -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.