diff --git a/cinder/tests/unit/volume/drivers/dell_emc/vmax/test_vmax.py b/cinder/tests/unit/volume/drivers/dell_emc/vmax/test_vmax.py index 5f1b40a8c2e..10ab8d488de 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/vmax/test_vmax.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/vmax/test_vmax.py @@ -3072,10 +3072,17 @@ class VMAXProvisionTest(test.TestCase): device_id = self.data.device_id new_size = '3' extra_specs = self.data.extra_specs - with mock.patch.object(self.provision.rest, 'extend_volume'): + with mock.patch.object(self.provision.rest, 'extend_volume' + ) as mock_ex: self.provision.extend_volume(array, device_id, new_size, extra_specs) - self.provision.rest.extend_volume.assert_called_once_with( + mock_ex.assert_called_once_with( + array, device_id, new_size, extra_specs) + mock_ex.reset_mock() + # Pass in rdf group + self.provision.extend_volume(array, device_id, new_size, + extra_specs, self.data.rdf_group_no) + mock_ex.assert_called_once_with( array, device_id, new_size, extra_specs) def test_get_srp_pool_stats(self): @@ -3167,6 +3174,15 @@ class VMAXProvisionTest(test.TestCase): target_device = self.data.device_id2 rdf_group_name = self.data.rdf_group_name rep_extra_specs = self.data.rep_extra_specs + # State is suspended + self.provision.break_rdf_relationship( + array, device_id, target_device, + rdf_group_name, rep_extra_specs, "Suspended") + mock_mod.assert_not_called() + mock_del.assert_called_once_with( + array, device_id, rdf_group_name) + mock_del.reset_mock() + # State is synchronized self.provision.break_rdf_relationship( array, device_id, target_device, rdf_group_name, rep_extra_specs, "Synchronized") diff --git a/cinder/volume/drivers/dell_emc/vmax/common.py b/cinder/volume/drivers/dell_emc/vmax/common.py index d2eb234c27b..b2cab11cf57 100644 --- a/cinder/volume/drivers/dell_emc/vmax/common.py +++ b/cinder/volume/drivers/dell_emc/vmax/common.py @@ -2931,9 +2931,9 @@ class VMAXCommon(object): """Extend a replication-enabled volume. Cannot extend volumes in a synchronization pair where the source - and/or target arrays are running HyperMax versions < 5978, or for - Metro-enabled volumes. Must first break the relationship, extend - them separately, then recreate the pair. + and/or target arrays are running HyperMax versions < 5978. Must first + break the relationship, extend them separately, then recreate the + pair. Extending Metro protected volumes is not supported. :param array: the array serial number :param volume: the volume objcet :param device_id: the volume device id @@ -2949,8 +2949,7 @@ class VMAXCommon(object): __, remote_array = self.get_rdf_details(array) if self.rest.is_next_gen_array(remote_array): ode_replication = True - if (self.utils.is_metro_device(self.rep_config, extra_specs) - and not self.allow_delete_metro): + if self.utils.is_metro_device(self.rep_config, extra_specs): allow_extend = False if allow_extend is True or ode_replication is True: try: @@ -2960,18 +2959,16 @@ class VMAXCommon(object): array, volume, device_id)) rep_extra_specs = self._get_replication_extra_specs( extra_specs, self.rep_config) + lock_rdf_group = rdf_group if not ode_replication: # Volume must be removed from replication (storage) group # before the replication relationship can be ended (cannot # have a mix of replicated and non-replicated volumes as - # the SRDF groups become unmanageable), but - # leave the vol in metro management group for now - metro_grp = self.utils.get_async_rdf_managed_grp_name( - self.rep_config) if self.utils.is_metro_device( - self.rep_config, rep_extra_specs) else None + # the SRDF groups become unmanageable) + lock_rdf_group = None self.masking.remove_and_reset_members( array, volume, device_id, volume_name, - extra_specs, False, async_grp=metro_grp) + extra_specs, False) # Repeat on target side self.masking.remove_and_reset_members( @@ -2979,17 +2976,9 @@ class VMAXCommon(object): rep_extra_specs, False) LOG.info("Breaking replication relationship...") - if self.utils.is_metro_device( - self.rep_config, rep_extra_specs): - rep_extra_specs['allow_del_metro'] = ( - self.allow_delete_metro) - self._cleanup_metro_target( - array, device_id, target_device, - rdf_group, rep_extra_specs) - else: - self.provision.break_rdf_relationship( - array, device_id, target_device, rdf_group, - rep_extra_specs, pair_state) + self.provision.break_rdf_relationship( + array, device_id, target_device, rdf_group, + rep_extra_specs, pair_state) # Extend the target volume LOG.info("Extending target volume...") @@ -2997,13 +2986,14 @@ class VMAXCommon(object): r2_size = self.rest.get_size_of_device_on_array( remote_array, target_device) if int(r2_size) < int(new_size): - self.provision.extend_volume(remote_array, target_device, - new_size, rep_extra_specs) + self.provision.extend_volume( + remote_array, target_device, new_size, + rep_extra_specs, lock_rdf_group) # Extend the source volume LOG.info("Extending source volume...") self.provision.extend_volume( - array, device_id, new_size, extra_specs) + array, device_id, new_size, extra_specs, lock_rdf_group) if not ode_replication: # Re-create replication relationship @@ -3026,9 +3016,9 @@ class VMAXCommon(object): else: exception_message = (_( - "Extending a replicated volume is not " - "permitted on this backend. Please contact " - "your administrator.")) + "Extending a replicated volume is not permitted on this " + "backend. Please contact your administrator. Note that " + "you cannot extend SRDF/Metro protected volumes.")) LOG.error(exception_message) raise exception.VolumeBackendAPIException(data=exception_message) diff --git a/cinder/volume/drivers/dell_emc/vmax/provision.py b/cinder/volume/drivers/dell_emc/vmax/provision.py index 4ab26b44dd0..7d994aa3346 100644 --- a/cinder/volume/drivers/dell_emc/vmax/provision.py +++ b/cinder/volume/drivers/dell_emc/vmax/provision.py @@ -344,20 +344,29 @@ class VMAXProvision(object): list_volume_pairs=list_device_pairs) self.delete_volume_snap(array, snap_name, source_devices) - def extend_volume(self, array, device_id, new_size, extra_specs): + def extend_volume(self, array, device_id, new_size, extra_specs, + rdf_group=None): """Extend a volume. :param array: the array serial number :param device_id: the volume device id :param new_size: the new size (GB) :param extra_specs: the extra specifications + :param rdf_group: the rdf group number, if required :returns: status_code """ start_time = time.time() - self.rest.extend_volume(array, device_id, new_size, extra_specs) - LOG.debug("Extend VMAX volume took: %(delta)s H:MM:SS.", - {'delta': self.utils.get_time_delta(start_time, - time.time())}) + if rdf_group: + @coordination.synchronized('emc-rg-{rdf_group}') + def _extend_replicated_volume(rdf_group): + self.rest.extend_volume(array, device_id, + new_size, extra_specs) + _extend_replicated_volume(rdf_group) + else: + self.rest.extend_volume(array, device_id, new_size, extra_specs) + LOG.debug("Extend VMAX volume took: %(delta)s H:MM:SS.", + {'delta': self.utils.get_time_delta(start_time, + time.time())}) def get_srp_pool_stats(self, array, array_info): """Get the srp capacity stats. @@ -495,8 +504,11 @@ class VMAXProvision(object): self.rest.wait_for_rdf_consistent_state( array, device_id, target_device, rep_extra_specs, state) - self.rest.modify_rdf_device_pair( - array, device_id, rdf_group, rep_extra_specs, suspend=True) + if state.lower() == utils.RDF_SUSPENDED_STATE: + LOG.info("RDF pair is already suspended") + else: + self.rest.modify_rdf_device_pair( + array, device_id, rdf_group, rep_extra_specs, suspend=True) self.delete_rdf_pair(array, device_id, rdf_group, target_device, rep_extra_specs)