From ee2d3189bda913161c9ac3dc11cf09c60bf7f022 Mon Sep 17 00:00:00 2001 From: odonos12 Date: Mon, 11 May 2020 11:10:17 +0100 Subject: [PATCH] PowerMax Driver - Create vol suspend fix & DeviceID check Moved exception handling from _create_volume into create rdf/non-rdf vol helper methods. Added RDFG resume attempts to exception handling of RDF enabled vol creates and deletes. Added device ID check before return from volume create to verify returned device ID against device label on PowerMAX. Change-Id: I3a0640e5f68bcfe4031562f1a6a2485ba4763e91 Closes-Bug: 1877976 --- .../dell_emc/powermax/test_powermax_common.py | 118 +++++++- .../powermax/test_powermax_replication.py | 35 +++ .../drivers/dell_emc/powermax/common.py | 283 ++++++++++++------ 3 files changed, 332 insertions(+), 104 deletions(-) diff --git a/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_common.py b/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_common.py index e7556180068..16f600cc202 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_common.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_common.py @@ -1173,6 +1173,26 @@ class PowerMaxCommonTest(test.TestCase): self.assertEqual(ref_response, (volume_dict, rep_update, rep_info_dict)) + @mock.patch.object(rest.PowerMaxRest, 'find_volume_device_id', + return_value=tpd.PowerMaxData.device_id2) + @mock.patch.object( + common.PowerMaxCommon, '_create_non_replicated_volume', + return_value=deepcopy(tpd.PowerMaxData.provider_location)) + @mock.patch.object(rest.PowerMaxRest, 'get_volume', + return_value=tpd.PowerMaxData.volume_details[0]) + def test_create_volume_update_returning_device_id( + self, mck_get, mck_create, mck_find): + volume = self.data.test_volume + volume_name = '1' + volume_size = self.data.test_volume.size + extra_specs = self.data.extra_specs + ref_response = (self.data.provider_location2, dict(), dict()) + volume_dict, rep_update, rep_info_dict = ( + self.common._create_volume( + volume, volume_name, volume_size, extra_specs)) + self.assertEqual(ref_response, + (volume_dict, rep_update, rep_info_dict)) + def test_create_volume_success_next_gen(self): volume = self.data.test_volume volume_name = '1' @@ -1197,7 +1217,7 @@ class PowerMaxCommonTest(test.TestCase): @mock.patch.object(provision.PowerMaxProvision, 'create_volume_from_sg', side_effect=exception.VolumeBackendAPIException('')) @mock.patch.object(common.PowerMaxCommon, - '_cleanup_volume_create_post_failure') + '_cleanup_non_rdf_volume_create_post_failure') @mock.patch.object(rest.PowerMaxRest, 'delete_storage_group') def test_create_volume_failed(self, mck_del, mck_cleanup, mck_create): volume = self.data.test_volume @@ -1225,41 +1245,111 @@ class PowerMaxCommonTest(test.TestCase): extra_specs) mck_del.assert_called_once() - @mock.patch.object(common.PowerMaxCommon, '_delete_from_srp') @mock.patch.object(common.PowerMaxCommon, 'cleanup_rdf_device_pair') @mock.patch.object( rest.PowerMaxRest, 'is_vol_in_rep_session', return_value=('', '', [ {utils.RDF_GROUP_NO: tpd.PowerMaxData.rdf_group_no_1}])) - def test_cleanup_volume_create_post_failure_rdf_enabled( - self, mck_in, mck_clean, mck_del): + @mock.patch.object(rest.PowerMaxRest, 'srdf_resume_replication') + @mock.patch.object(utils.PowerMaxUtils, 'get_default_storage_group_name', + return_value=tpd.PowerMaxData.storagegroup_name_f) + @mock.patch.object(common.PowerMaxCommon, 'prepare_replication_details', + return_value=('', tpd.PowerMaxData.rep_extra_specs, + '', '',)) + def test_cleanup_rdf_volume_create_post_failure_sync( + self, mck_prep, mck_sg, mck_resume, mck_sess, mck_clean): array = self.data.array volume = self.data.test_volume volume_name = self.data.test_volume.name extra_specs = deepcopy(self.data.extra_specs_rep_enabled) extra_specs[utils.REP_CONFIG] = self.data.rep_config_sync + extra_specs['rep_mode'] = utils.REP_SYNC devices = [self.data.device_id] - self.common._cleanup_volume_create_post_failure( + self.common._cleanup_rdf_volume_create_post_failure( volume, volume_name, extra_specs, devices) - mck_in.assert_called_once_with(array, self.data.device_id) + mck_prep.assert_called_once_with(extra_specs) + mck_sg.assert_called_once_with( + extra_specs['srp'], extra_specs['slo'], extra_specs['workload'], + False, True, extra_specs['rep_mode']) + mck_resume.assert_called_once_with( + array, self.data.storagegroup_name_f, self.data.rdf_group_no_1, + self.data.rep_extra_specs) + mck_sess.assert_called_once_with(array, self.data.device_id) + mck_clean.assert_called_once_with( + array, self.data.rdf_group_no_1, self.data.device_id, extra_specs) + + @mock.patch.object(common.PowerMaxCommon, 'cleanup_rdf_device_pair') + @mock.patch.object( + rest.PowerMaxRest, 'is_vol_in_rep_session', return_value=('', '', [ + {utils.RDF_GROUP_NO: tpd.PowerMaxData.rdf_group_no_1}])) + @mock.patch.object(rest.PowerMaxRest, 'srdf_resume_replication') + @mock.patch.object(utils.PowerMaxUtils, 'get_rdf_management_group_name', + return_value=tpd.PowerMaxData.storagegroup_name_f) + @mock.patch.object(common.PowerMaxCommon, 'prepare_replication_details', + return_value=('', tpd.PowerMaxData.rep_extra_specs, + '', '',)) + def test_cleanup_rdf_volume_create_post_failure_non_sync( + self, mck_prep, mck_mgmt, mck_resume, mck_sess, mck_clean): + array = self.data.array + volume = self.data.test_volume + volume_name = self.data.test_volume.name + extra_specs = deepcopy(self.data.extra_specs_rep_enabled) + extra_specs[utils.REP_CONFIG] = self.data.rep_config_async + extra_specs['rep_mode'] = utils.REP_ASYNC + devices = [self.data.device_id] + self.common._cleanup_rdf_volume_create_post_failure( + volume, volume_name, extra_specs, devices) + mck_prep.assert_called_once_with(extra_specs) + mck_mgmt.assert_called_once_with(extra_specs[utils.REP_CONFIG]) + mck_resume.assert_called_once_with( + array, self.data.storagegroup_name_f, self.data.rdf_group_no_1, + self.data.rep_extra_specs) + mck_sess.assert_called_once_with(array, self.data.device_id) mck_clean.assert_called_once_with( array, self.data.rdf_group_no_1, self.data.device_id, extra_specs) - mck_del.assert_called_once_with( - array, self.data.device_id, volume_name, extra_specs) @mock.patch.object(common.PowerMaxCommon, '_delete_from_srp') @mock.patch.object(masking.PowerMaxMasking, 'remove_and_reset_members') - @mock.patch.object( - rest.PowerMaxRest, 'is_vol_in_rep_session', return_value=('', '', '')) - def test_cleanup_volume_create_post_failure_rdf_disabled( - self, mck_in, mck_remove, mck_del): + @mock.patch.object(rest.PowerMaxRest, 'is_vol_in_rep_session', + return_value=('', '', False)) + @mock.patch.object(rest.PowerMaxRest, 'srdf_resume_replication') + @mock.patch.object(utils.PowerMaxUtils, 'get_rdf_management_group_name', + return_value=tpd.PowerMaxData.storagegroup_name_f) + @mock.patch.object(common.PowerMaxCommon, 'prepare_replication_details', + return_value=('', tpd.PowerMaxData.rep_extra_specs, + '', '',)) + def test_cleanup_rdf_volume_create_post_failure_pre_rdf_establish( + self, mck_prep, mck_mgmt, mck_resume, mck_sess, mck_rem, mck_del): + array = self.data.array + volume = self.data.test_volume + volume_name = self.data.test_volume.name + extra_specs = deepcopy(self.data.extra_specs_rep_enabled) + extra_specs[utils.REP_CONFIG] = self.data.rep_config_sync + extra_specs['rep_mode'] = utils.REP_ASYNC + devices = [self.data.device_id] + self.common._cleanup_rdf_volume_create_post_failure( + volume, volume_name, extra_specs, devices) + mck_prep.assert_called_once_with(extra_specs) + mck_mgmt.assert_called_once_with(extra_specs[utils.REP_CONFIG]) + mck_resume.assert_called_once_with( + array, self.data.storagegroup_name_f, self.data.rdf_group_no_1, + self.data.rep_extra_specs) + mck_sess.assert_called_once_with(array, self.data.device_id) + mck_rem.assert_called_once_with(array, volume, self.data.device_id, + volume_name, extra_specs, False) + mck_del.assert_called_once_with(array, self.data.device_id, + volume_name, extra_specs) + + @mock.patch.object(common.PowerMaxCommon, '_delete_from_srp') + @mock.patch.object(masking.PowerMaxMasking, 'remove_and_reset_members') + def test_cleanup_non_rdf_volume_create_post_failure( + self, mck_remove, mck_del): array = self.data.array volume = self.data.test_volume volume_name = self.data.test_volume.name extra_specs = self.data.extra_specs devices = [self.data.device_id] - self.common._cleanup_volume_create_post_failure( + self.common._cleanup_non_rdf_volume_create_post_failure( volume, volume_name, extra_specs, devices) - mck_in.assert_called_once_with(array, self.data.device_id) mck_remove.assert_called_once_with( array, volume, self.data.device_id, volume_name, extra_specs, False) diff --git a/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_replication.py b/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_replication.py index 341d22b8b3c..9695db90c24 100644 --- a/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_replication.py +++ b/cinder/tests/unit/volume/drivers/dell_emc/powermax/test_powermax_replication.py @@ -1173,6 +1173,41 @@ class PowerMaxReplicationTest(test.TestCase): mck_resume.assert_called_once_with( array, rdf_mgmt_grp, rdf_group_no, rep_extra_specs) + @mock.patch.object(rest.PowerMaxRest, 'srdf_resume_replication') + @mock.patch.object( + rest.PowerMaxRest, 'srdf_remove_device_pair_from_storage_group', + side_effect=exception.CinderException) + @mock.patch.object( + rest.PowerMaxRest, 'get_storage_groups_from_volume', + return_value=[tpd.PowerMaxData.storagegroup_name_f]) + @mock.patch.object( + rest.PowerMaxRest, 'get_rdf_pair_volume', + return_value=tpd.PowerMaxData.rdf_group_vol_details) + @mock.patch.object( + common.PowerMaxCommon, '_get_replication_extra_specs', + return_value=tpd.PowerMaxData.rep_extra_specs) + @mock.patch.object( + common.PowerMaxCommon, 'get_rdf_details', + return_value=(tpd.PowerMaxData.rdf_group_no_1, + tpd.PowerMaxData.remote_array)) + def test_cleanup_rdf_device_pair_attempt_resume_on_exception( + self, mck_rdf, mck_rep, mck_pair, mck_sg, mck_rem, mck_resume): + array = self.data.array + rdf_group_no = self.data.rdf_group_no_1 + device_id = self.data.device_id + extra_specs = deepcopy(self.data.rep_extra_specs) + extra_specs[utils.REP_MODE] = utils.REP_SYNC + extra_specs[utils.REP_CONFIG] = self.data.rep_config_sync + rep_extra_specs = self.common._get_replication_extra_specs( + extra_specs, extra_specs[utils.REP_CONFIG]) + self.assertRaises( + exception.CinderException, + self.common.cleanup_rdf_device_pair, array, rdf_group_no, + device_id, extra_specs) + mck_resume.assert_called_once_with( + array, self.data.storagegroup_name_f, rdf_group_no, + rep_extra_specs, False) + @mock.patch.object( rest.PowerMaxRest, 'get_num_vols_in_sg', return_value=1) @mock.patch.object( diff --git a/cinder/volume/drivers/dell_emc/powermax/common.py b/cinder/volume/drivers/dell_emc/powermax/common.py index 03033c4743c..8b99d597c6f 100644 --- a/cinder/volume/drivers/dell_emc/powermax/common.py +++ b/cinder/volume/drivers/dell_emc/powermax/common.py @@ -1995,12 +1995,11 @@ class PowerMaxCommon(object): def _create_volume(self, volume, volume_name, volume_size, extra_specs): """Create a volume. - :param volume_name: the volume + :param volume: the volume :param volume_name: the volume name :param volume_size: the volume size :param extra_specs: extra specifications :returns: volume_dict, rep_update, rep_info_dict --dict - :raises: VolumeBackendAPIException: """ # Set Create Volume options is_re, rep_mode, storagegroup_name = False, None, None @@ -2043,33 +2042,64 @@ class PowerMaxCommon(object): extra_specs[utils.WORKLOAD], extra_specs, do_disable_compression, is_re, rep_mode) + if not is_re: + volume_dict = self._create_non_replicated_volume( + array, volume, volume_name, storagegroup_name, + volume_size, extra_specs) + else: + volume_dict, rep_update, rep_info_dict = ( + self._create_replication_enabled_volume( + array, volume, volume_name, volume_size, extra_specs, + storagegroup_name, rep_mode)) + + # Compare volume ID against identifier on array. Update if needed. + # This can occur in cases where multiple edits are occurring at once. + found_device_id = self.rest.find_volume_device_id(array, volume_name) + returning_device_id = volume_dict['device_id'] + if found_device_id != returning_device_id: + volume_dict['device_id'] = found_device_id + + return volume_dict, rep_update, rep_info_dict + + @coordination.synchronized("emc-nonrdf-vol-{storagegroup_name}-{array}") + def _create_non_replicated_volume( + self, array, volume, volume_name, storagegroup_name, volume_size, + extra_specs): + """Create a volume without replication enabled + + :param array: the primary array -- string + :param volume: the volume -- dict + :param volume_name: the volume name -- string + :param storagegroup_name: the storage group name -- string + :param volume_size: the volume size -- string + :param extra_specs: extra specifications -- dict + :return: volume_dict -- dict + :raises: VolumeBackendAPIException: + """ existing_devices = self.rest.get_volumes_in_storage_group( array, storagegroup_name) - try: - if not is_re: - volume_dict = self.provision.create_volume_from_sg( - array, volume_name, storagegroup_name, - volume_size, extra_specs, rep_info=None) - else: - volume_dict, rep_update, rep_info_dict = ( - self._create_replication_enabled_volume( - array, volume, volume_name, volume_size, extra_specs, - storagegroup_name, rep_mode)) - except Exception: - if storagegroup_name: + volume_dict = self.provision.create_volume_from_sg( + array, volume_name, storagegroup_name, + volume_size, extra_specs, rep_info=None) + return volume_dict + except Exception as e: + try: + # Attempt cleanup of storage group post exception. updated_devices = set(self.rest.get_volumes_in_storage_group( array, storagegroup_name)) devices_to_delete = [device for device in updated_devices if device not in existing_devices] if devices_to_delete: - self._cleanup_volume_create_post_failure( + self._cleanup_non_rdf_volume_create_post_failure( volume, volume_name, extra_specs, devices_to_delete) elif not existing_devices: self.rest.delete_storage_group(array, storagegroup_name) - raise - - return volume_dict, rep_update, rep_info_dict + finally: + # Pass actual exception that was raised now that cleanup + # attempt is finished. Mainly VolumeBackendAPIException raised + # from error status codes returned from the various REST jobs. + raise e @coordination.synchronized('emc-rdf-vol-{storagegroup_name}-{array}') def _create_replication_enabled_volume( @@ -2085,6 +2115,7 @@ class PowerMaxCommon(object): :param storagegroup_name: the storage group name :param rep_mode: the replication mode :returns: volume_dict, rep_update, rep_info_dict --dict + :raises: VolumeBackendAPIException: """ def _is_first_vol_in_replicated_sg(): vol_dict = dict() @@ -2114,35 +2145,55 @@ class PowerMaxCommon(object): return first_vol, rep_ex_specs, vol_dict - is_first_volume, rep_extra_specs, volume_info_dict = ( - _is_first_vol_in_replicated_sg()) + existing_devices = self.rest.get_volumes_in_storage_group( + array, storagegroup_name) + try: + is_first_volume, rep_extra_specs, volume_info_dict = ( + _is_first_vol_in_replicated_sg()) - if not is_first_volume: - self._validate_rdfg_status(array, extra_specs) - __, rep_extra_specs, rep_info_dict, __ = ( - self.prepare_replication_details(extra_specs)) - volume_info_dict = self.provision.create_volume_from_sg( - array, volume_name, storagegroup_name, - volume_size, extra_specs, rep_info_dict) + if not is_first_volume: + self._validate_rdfg_status(array, extra_specs) + __, rep_extra_specs, rep_info_dict, __ = ( + self.prepare_replication_details(extra_specs)) + volume_info_dict = self.provision.create_volume_from_sg( + array, volume_name, storagegroup_name, + volume_size, extra_specs, rep_info_dict) - rep_vol_dict = deepcopy(volume_info_dict) - rep_vol_dict.update({'device_uuid': volume_name, - 'storage_group': storagegroup_name, - 'size': volume_size}) + rep_vol_dict = deepcopy(volume_info_dict) + rep_vol_dict.update({'device_uuid': volume_name, + 'storage_group': storagegroup_name, + 'size': volume_size}) - remote_device_id = self.get_and_set_remote_device_uuid( - extra_specs, rep_extra_specs, rep_vol_dict) - rep_vol_dict.update({'remote_device_id': remote_device_id}) - rep_update, rep_info_dict = self.gather_replication_updates( - extra_specs, rep_extra_specs, rep_vol_dict) + remote_device_id = self.get_and_set_remote_device_uuid( + extra_specs, rep_extra_specs, rep_vol_dict) + rep_vol_dict.update({'remote_device_id': remote_device_id}) + rep_update, rep_info_dict = self.gather_replication_updates( + extra_specs, rep_extra_specs, rep_vol_dict) - if rep_mode in [utils.REP_ASYNC, utils.REP_METRO]: - self._add_volume_to_rdf_management_group( - array, volume_info_dict['device_id'], volume_name, - rep_extra_specs['array'], remote_device_id, - extra_specs) + if rep_mode in [utils.REP_ASYNC, utils.REP_METRO]: + self._add_volume_to_rdf_management_group( + array, volume_info_dict['device_id'], volume_name, + rep_extra_specs['array'], remote_device_id, + extra_specs) - return volume_info_dict, rep_update, rep_info_dict + return volume_info_dict, rep_update, rep_info_dict + except Exception as e: + try: + # Attempt cleanup of rdfg & storage group post exception + updated_devices = set(self.rest.get_volumes_in_storage_group( + array, storagegroup_name)) + devices_to_delete = [device for device in updated_devices + if device not in existing_devices] + if devices_to_delete: + self._cleanup_rdf_volume_create_post_failure( + volume, volume_name, extra_specs, devices_to_delete) + elif not existing_devices: + self.rest.delete_storage_group(array, storagegroup_name) + finally: + # Pass actual exception that was raised now that cleanup + # attempt is finished. Mainly VolumeBackendAPIException raised + # from error status codes returned from the various REST jobs. + raise e def _set_vmax_extra_specs(self, extra_specs, pool_record): """Set the PowerMax/VMAX extra specs. @@ -2385,49 +2436,66 @@ class PowerMaxCommon(object): self.rest.srdf_suspend_replication( array, rdf_mgmt_sg, rdf_group_no, rep_extra_specs) + try: + # 3. Check vol doesnt live in any SGs outside OpenStack managed SGs + if rdf_mgmt_sg and rdf_mgmt_sg in vol_sg_list: + vol_sg_list.remove(rdf_mgmt_sg) + if len(vol_sg_list) > 1: + exception_message = (_( + "There is more than one storage group associated with " + "device %(dev)s not including RDF management groups. " + "Please check device is not member of non-OpenStack " + "managed storage groups") % {'dev': device_id}) + LOG.error(exception_message) + raise exception.VolumeBackendAPIException(exception_message) + else: + vol_src_sg = vol_sg_list[0] - # 3. Check vol does not live in any SGs outside OpenStack managed SGs - if rdf_mgmt_sg and rdf_mgmt_sg in vol_sg_list: - vol_sg_list.remove(rdf_mgmt_sg) - if len(vol_sg_list) > 1: - exception_message = (_( - "There is more than one storage group associated with device " - "%(dev)s not including RDF management groups. Please check " - "device is not member of non-OpenStack managed storage " - "groups") % {'dev': device_id}) - LOG.error(exception_message) - raise exception.VolumeBackendAPIException(exception_message) - else: - vol_src_sg = vol_sg_list[0] - - # 4. Remove device from SG and delete RDFG device pair - self.rest.srdf_remove_device_pair_from_storage_group( - array, vol_src_sg, rep_extra_specs['array'], device_id, - rep_extra_specs) - - # 5. Remove the volume from any additional SGs - if rdf_mgmt_sg: - self.rest.remove_vol_from_sg( - array, rdf_mgmt_sg, device_id, extra_specs) - self.rest.remove_vol_from_sg( - remote_array, rdf_mgmt_sg, remote_device_id, rep_extra_specs) - - # 6. Delete the r2 volume - self.rest.delete_volume(remote_array, remote_device_id) - - # 7. Delete the SGs if there are no volumes remaining - self._cleanup_rdf_storage_groups_post_r2_delete( - array, remote_array, vol_src_sg, rdf_mgmt_sg, rdf_mgmt_cleanup) - - # 8. Resume replication if RDFG still contains volumes - if resume_replication: - self.rest.srdf_resume_replication( - array, rdf_mgmt_sg, rep_extra_specs['rdf_group_no'], + # 4. Remove device from SG and delete RDFG device pair + self.rest.srdf_remove_device_pair_from_storage_group( + array, vol_src_sg, rep_extra_specs['array'], device_id, rep_extra_specs) - LOG.info('Remote device %(dev)s deleted from RDF Group %(grp)s', - {'dev': remote_device_id, - 'grp': rep_extra_specs['rdf_group_label']}) + # 5. Remove the volume from any additional SGs + if rdf_mgmt_sg: + self.rest.remove_vol_from_sg( + array, rdf_mgmt_sg, device_id, extra_specs) + self.rest.remove_vol_from_sg( + remote_array, rdf_mgmt_sg, remote_device_id, + rep_extra_specs) + + # 6. Delete the r2 volume + self.rest.delete_volume(remote_array, remote_device_id) + + # 7. Delete the SGs if there are no volumes remaining + self._cleanup_rdf_storage_groups_post_r2_delete( + array, remote_array, vol_src_sg, rdf_mgmt_sg, rdf_mgmt_cleanup) + + # 8. Resume replication if RDFG still contains volumes + if resume_replication: + self.rest.srdf_resume_replication( + array, rdf_mgmt_sg, rep_extra_specs['rdf_group_no'], + rep_extra_specs) + + LOG.info('Remote device %(dev)s deleted from RDF Group %(grp)s', + {'dev': remote_device_id, + 'grp': rep_extra_specs['rdf_group_label']}) + except Exception as e: + # Attempt to resume SRDF groups after exception to avoid leaving + # them in a suspended state. + try: + if rdf_mgmt_sg: + self.rest.srdf_resume_replication( + array, rdf_mgmt_sg, rdf_group_no, rep_extra_specs, + False) + elif len(vol_sg_list) == 1: + self.rest.srdf_resume_replication( + array, vol_sg_list[0], rdf_group_no, rep_extra_specs, + False) + except Exception: + LOG.debug('Could not resume SRDF group after exception ' + 'during cleanup_rdf_device_pair.') + raise e def _cleanup_rdf_storage_groups_post_r2_delete( self, array, remote_array, sg_name, rdf_mgmt_sg, rdf_mgmt_cleanup): @@ -6090,9 +6158,9 @@ class PowerMaxCommon(object): return replication_update, rep_info_dict - def _cleanup_volume_create_post_failure( + def _cleanup_non_rdf_volume_create_post_failure( self, volume, volume_name, extra_specs, device_ids): - """Delete lingering volumes that exist in an SG post exception. + """Delete lingering volumes that exist in an non-RDF SG post exception. :param volume: Cinder volume -- Volume :param volume_name: Volume name -- str @@ -6101,17 +6169,52 @@ class PowerMaxCommon(object): """ array = extra_specs[utils.ARRAY] for device_id in device_ids: - __, __, rdf_group = self.rest.is_vol_in_rep_session( + self.masking.remove_and_reset_members( + array, volume, device_id, volume_name, extra_specs, False) + self._delete_from_srp( + array, device_id, volume_name, extra_specs) + + def _cleanup_rdf_volume_create_post_failure( + self, volume, volume_name, extra_specs, device_ids): + """Delete lingering volumes that exist in an RDF SG post exception. + + :param volume: Cinder volume -- Volume + :param volume_name: Volume name -- str + :param extra_specs: Volume extra specs -- dict + :param device_ids: Devices ids to be deleted -- list + """ + __, rep_extra_specs, __, __ = self.prepare_replication_details( + extra_specs) + array = extra_specs[utils.ARRAY] + srp = extra_specs['srp'] + slo = extra_specs['slo'] + workload = extra_specs['workload'] + do_disable_compression = self.utils.is_compression_disabled( + extra_specs) + rep_mode = extra_specs['rep_mode'] + rdf_group = rep_extra_specs['rdf_group_no'] + rep_config = extra_specs[utils.REP_CONFIG] + + if rep_mode is utils.REP_SYNC: + storagegroup_name = self.utils.get_default_storage_group_name( + srp, slo, workload, do_disable_compression, True, rep_mode) + else: + storagegroup_name = self.utils.get_rdf_management_group_name( + rep_config) + + self.rest.srdf_resume_replication( + array, storagegroup_name, rdf_group, rep_extra_specs) + for device_id in device_ids: + __, __, vol_is_rdf = self.rest.is_vol_in_rep_session( array, device_id) - if rdf_group: - rdf_group_no = rdf_group[0][utils.RDF_GROUP_NO] - self.cleanup_rdf_device_pair(array, rdf_group_no, device_id, + if vol_is_rdf: + self.cleanup_rdf_device_pair(array, rdf_group, device_id, extra_specs) else: self.masking.remove_and_reset_members( array, volume, device_id, volume_name, extra_specs, False) - self._delete_from_srp( - array, device_id, volume_name, extra_specs) + self._delete_from_srp( + array, device_id, volume_name, extra_specs) def _validate_rdfg_status(self, array, extra_specs): """Validate RDF group states before and after various operations