From 00c3ca6768c3f2a8fb925197c26b9dbc2866896f Mon Sep 17 00:00:00 2001 From: Tina Date: Wed, 11 Mar 2015 20:34:36 -0400 Subject: [PATCH] Create consistgroup from cgsnapshot support in VNX driver Changed VNX Cinder driver to support creating a consistency group from a consistency group snapshot and refactored the migration lun code for reusing the code in create cg from cgsnapshot feature. Change-Id: Id8f35b3e427262faf8c29506dfdc166403aca6db Implements: blueprint create-cg-from-cg-snapshot-vnx --- cinder/tests/unit/test_emc_vnxdirect.py | 235 ++++++++++++++++++- cinder/volume/drivers/emc/emc_cli_fc.py | 10 + cinder/volume/drivers/emc/emc_cli_iscsi.py | 10 + cinder/volume/drivers/emc/emc_vnx_cli.py | 250 +++++++++++++++++++-- 4 files changed, 478 insertions(+), 27 deletions(-) diff --git a/cinder/tests/unit/test_emc_vnxdirect.py b/cinder/tests/unit/test_emc_vnxdirect.py index 2e16e7cf8..8fb7cdf9b 100644 --- a/cinder/tests/unit/test_emc_vnxdirect.py +++ b/cinder/tests/unit/test_emc_vnxdirect.py @@ -17,6 +17,7 @@ import re import mock from oslo_concurrency import processutils +import six from cinder import exception from cinder import test @@ -547,6 +548,14 @@ class EMCVNXCLIDriverTestData(object): return ('storagegroup', '-removehlu', '-hlu', hlu, '-gname', gname, '-o') + def SNAP_COPY_CMD(self, src_snap, snap_name): + return ('snap', '-copy', '-id', src_snap, '-name', snap_name, + '-ignoreMigrationCheck', '-ignoreDeduplicationCheck') + + def ALLOW_READWRITE_ON_SNAP_CMD(self, snap_name): + return ('snap', '-modify', '-id', snap_name, + '-allowReadWrite', 'yes', '-allowAutoDelete', 'yes') + provisioning_values = { 'thin': ['-type', 'Thin'], 'thick': ['-type', 'NonThin'], @@ -590,9 +599,15 @@ class EMCVNXCLIDriverTestData(object): return ('storagepool', '-list', '-name', storage_pool, '-fastcache') - def CREATE_CONSISTENCYGROUP_CMD(self, cg_name): - return ('-np', 'snap', '-group', '-create', - '-name', cg_name, '-allowSnapAutoDelete', 'no') + def CREATE_CONSISTENCYGROUP_CMD(self, cg_name, members=None): + create_cmd = ('-np', 'snap', '-group', '-create', + '-name', cg_name, '-allowSnapAutoDelete', 'no') + + if not members: + return create_cmd + else: + return create_cmd + ('-res', ','.join(map(six.text_type, + members))) def DELETE_CONSISTENCYGROUP_CMD(self, cg_name): return ('-np', 'snap', '-group', '-destroy', @@ -812,9 +827,10 @@ State: Ready "Switch Present: NO\n", 0) def LUN_PROPERTY(self, name, is_thin=False, has_snap=False, size=1, - state='Ready', faulted='false', operation='None'): + state='Ready', faulted='false', operation='None', + lunid=1): return (""" - LOGICAL UNIT NUMBER 1 + LOGICAL UNIT NUMBER %(lunid)s Name: %(name)s UID: 60:06:01:60:09:20:32:00:13:DF:B4:EF:C2:63:E3:11 Current Owner: SP A @@ -835,6 +851,7 @@ State: Ready Current Operation Status: N/A Current Operation Percent Completed: 0 Is Thin LUN: %(is_thin)s""" % { + 'lunid': lunid, 'name': name, 'has_snap': 'FakeSnap' if has_snap else 'N/A', 'size': size, @@ -3278,6 +3295,214 @@ Time Remaining: 0 second(s) cg_name, ['1', '3']), poll=False)] fake_cli.assert_has_calls(expect_cmd) + def test_create_consistencygroup_from_cgsnapshot(self): + output_migrate_verify = ('The specified source LUN ' + 'is not currently migrating', 23) + new_cg = self.testData.test_cg.copy() + new_cg.update( + {'id': 'new_cg_id'}) + vol1_in_new_cg = self.testData.test_volume_cg.copy() + vol1_in_new_cg.update( + {'name': 'vol1_in_cg', + 'id': '111111', + 'consistencygroup_id': 'new_cg_id', + 'provider_location': None}) + vol2_in_new_cg = self.testData.test_volume_cg.copy() + vol2_in_new_cg.update( + {'name': 'vol2_in_cg', + 'id': '222222', + 'consistencygroup_id': 'new_cg_id', + 'provider_location': None}) + src_cgsnap = self.testData.test_cgsnapshot + snap1_in_src_cgsnap = self.testData.test_member_cgsnapshot.copy() + snap1_in_src_cgsnap.update( + {'volume': self.testData.test_volume, + 'volume_name': 'src_vol1'}) + snap2_in_src_cgsnap = self.testData.test_member_cgsnapshot.copy() + snap2_in_src_cgsnap.update( + {'volume': self.testData.test_volume2, + 'volume_name': 'src_vol2'}) + copied_snap_name = 'temp_snapshot_for_%s' % new_cg['id'] + td = self.testData + commands = [td.SNAP_COPY_CMD(src_cgsnap['id'], copied_snap_name), + td.ALLOW_READWRITE_ON_SNAP_CMD(copied_snap_name), + td.SNAP_MP_CREATE_CMD(vol1_in_new_cg['name'], + snap1_in_src_cgsnap['volume_name']), + td.SNAP_ATTACH_CMD(vol1_in_new_cg['name'], + copied_snap_name), + td.LUN_CREATION_CMD(vol1_in_new_cg['name'] + '_dest', + vol1_in_new_cg['size'], + 'unit_test_pool', 'thin', None), + td.LUN_PROPERTY_ALL_CMD(vol1_in_new_cg['name'] + '_dest'), + td.LUN_PROPERTY_ALL_CMD(vol1_in_new_cg['name']), + td.MIGRATION_CMD(6231, 1), + + td.SNAP_MP_CREATE_CMD(vol2_in_new_cg['name'], + snap2_in_src_cgsnap['volume_name']), + td.SNAP_ATTACH_CMD(vol2_in_new_cg['name'], + copied_snap_name), + td.LUN_CREATION_CMD(vol2_in_new_cg['name'] + '_dest', + vol2_in_new_cg['size'], + 'unit_test_pool', 'thin', None), + td.LUN_PROPERTY_ALL_CMD(vol2_in_new_cg['name'] + '_dest'), + td.LUN_PROPERTY_ALL_CMD(vol2_in_new_cg['name']), + td.MIGRATION_CMD(6232, 2), + + td.MIGRATION_VERIFY_CMD(6231), + td.MIGRATION_VERIFY_CMD(6232), + td.CREATE_CONSISTENCYGROUP_CMD(new_cg['id'], [6231, 6232]), + td.DELETE_CG_SNAPSHOT(copied_snap_name) + ] + results = [SUCCEED, SUCCEED, SUCCEED, SUCCEED, SUCCEED, + td.LUN_PROPERTY(vol1_in_new_cg['name'] + '_dest', + lunid=1), + td.LUN_PROPERTY(vol1_in_new_cg['name'], lunid=6231), + SUCCEED, SUCCEED, SUCCEED, SUCCEED, + td.LUN_PROPERTY(vol2_in_new_cg['name'] + '_dest', + lunid=2), + td.LUN_PROPERTY(vol2_in_new_cg['name'], lunid=6232), + SUCCEED, output_migrate_verify, output_migrate_verify, + SUCCEED, SUCCEED] + + fake_cli = self.driverSetup(commands, results) + + cg_model_update, volumes_model_update = ( + self.driver.create_consistencygroup_from_src( + None, new_cg, [vol1_in_new_cg, vol2_in_new_cg], + cgsnapshot=src_cgsnap, snapshots=[snap1_in_src_cgsnap, + snap2_in_src_cgsnap])) + self.assertEqual(2, len(volumes_model_update)) + self.assertTrue('id^%s' % 6231 in + volumes_model_update[0]['provider_location']) + self.assertTrue('id^%s' % 6232 in + volumes_model_update[1]['provider_location']) + + expect_cmd = [ + mock.call(*td.SNAP_COPY_CMD(src_cgsnap['id'], copied_snap_name)), + mock.call(*td.ALLOW_READWRITE_ON_SNAP_CMD(copied_snap_name)), + mock.call(*td.SNAP_MP_CREATE_CMD(vol1_in_new_cg['name'], + snap1_in_src_cgsnap['volume_name']), + poll=False), + mock.call(*td.SNAP_ATTACH_CMD(vol1_in_new_cg['name'], + copied_snap_name)), + mock.call(*td.LUN_CREATION_CMD(vol1_in_new_cg['name'] + '_dest', + vol1_in_new_cg['size'], + 'unit_test_pool', 'thick', None)), + mock.call(*td.LUN_PROPERTY_ALL_CMD( + vol1_in_new_cg['name'] + '_dest'), poll=False), + mock.call(*td.LUN_PROPERTY_ALL_CMD( + vol1_in_new_cg['name'] + '_dest'), poll=False), + mock.call(*td.LUN_PROPERTY_ALL_CMD(vol1_in_new_cg['name']), + poll=True), + mock.call(*td.MIGRATION_CMD(6231, 1), + poll=True, retry_disable=True), + mock.call(*td.SNAP_MP_CREATE_CMD(vol2_in_new_cg['name'], + snap2_in_src_cgsnap['volume_name']), + poll=False), + mock.call(*td.SNAP_ATTACH_CMD(vol2_in_new_cg['name'], + copied_snap_name)), + mock.call(*td.LUN_CREATION_CMD(vol2_in_new_cg['name'] + '_dest', + vol2_in_new_cg['size'], + 'unit_test_pool', 'thick', None)), + mock.call(*td.LUN_PROPERTY_ALL_CMD( + vol2_in_new_cg['name'] + '_dest'), poll=False), + mock.call(*td.LUN_PROPERTY_ALL_CMD( + vol2_in_new_cg['name'] + '_dest'), poll=False), + mock.call(*td.LUN_PROPERTY_ALL_CMD(vol2_in_new_cg['name']), + poll=True), + mock.call(*td.MIGRATION_CMD(6232, 2), + poll=True, retry_disable=True), + mock.call(*td.MIGRATION_VERIFY_CMD(6232), poll=True), + mock.call(*td.MIGRATION_VERIFY_CMD(6231), poll=True), + mock.call(*td.CREATE_CONSISTENCYGROUP_CMD( + new_cg['id'], [6232, 6231])), + mock.call(*td.DELETE_CG_SNAPSHOT(copied_snap_name))] + self.assertEqual(expect_cmd, fake_cli.call_args_list) + + def test_create_consistencygroup_from_othersource(self): + new_cg = self.testData.test_cg.copy() + new_cg.update( + {'id': 'new_cg_id'}) + vol1_in_new_cg = self.testData.test_volume_cg.copy() + vol1_in_new_cg.update( + {'name': 'vol1_in_cg', + 'id': '111111', + 'consistencygroup_id': 'new_cg_id', + 'provider_location': None}) + vol2_in_new_cg = self.testData.test_volume_cg.copy() + vol2_in_new_cg.update( + {'name': 'vol2_in_cg', + 'id': '222222', + 'consistencygroup_id': 'new_cg_id', + 'provider_location': None}) + self.driverSetup() + self.assertRaises( + exception.InvalidInput, + self.driver.create_consistencygroup_from_src, + new_cg, [vol1_in_new_cg, vol2_in_new_cg], + None, None) + + def test_create_cg_from_cgsnapshot_migrate_failed(self): + new_cg = self.testData.test_cg.copy() + new_cg.update( + {'id': 'new_cg_id'}) + vol1_in_new_cg = self.testData.test_volume_cg.copy() + vol1_in_new_cg.update( + {'name': 'vol1_in_cg', + 'id': '111111', + 'consistencygroup_id': 'new_cg_id', + 'provider_location': None}) + vol2_in_new_cg = self.testData.test_volume_cg.copy() + vol2_in_new_cg.update( + {'name': 'vol2_in_cg', + 'id': '222222', + 'consistencygroup_id': 'new_cg_id', + 'provider_location': None}) + src_cgsnap = self.testData.test_cgsnapshot + snap1_in_src_cgsnap = self.testData.test_member_cgsnapshot.copy() + snap1_in_src_cgsnap.update( + {'volume': self.testData.test_volume, + 'volume_name': 'src_vol1'}) + snap2_in_src_cgsnap = self.testData.test_member_cgsnapshot.copy() + snap2_in_src_cgsnap.update( + {'volume': self.testData.test_volume2, + 'volume_name': 'src_vol2'}) + copied_snap_name = 'temp_snapshot_for_%s' % new_cg['id'] + td = self.testData + commands = [td.LUN_PROPERTY_ALL_CMD(vol1_in_new_cg['name'] + '_dest'), + td.LUN_PROPERTY_ALL_CMD(vol1_in_new_cg['name']), + td.LUN_PROPERTY_ALL_CMD(vol2_in_new_cg['name'] + '_dest'), + td.LUN_PROPERTY_ALL_CMD(vol2_in_new_cg['name']), + td.MIGRATION_CMD(6232, 2)] + results = [td.LUN_PROPERTY(vol1_in_new_cg['name'] + '_dest', + lunid=1), + td.LUN_PROPERTY(vol1_in_new_cg['name'], lunid=6231), + td.LUN_PROPERTY(vol2_in_new_cg['name'] + '_dest', + lunid=2), + td.LUN_PROPERTY(vol2_in_new_cg['name'], lunid=6232), + FAKE_ERROR_RETURN] + + fake_cli = self.driverSetup(commands, results) + self.assertRaisesRegexp(exception.VolumeBackendAPIException, + 'Migrate volume failed', + self.driver.create_consistencygroup_from_src, + None, new_cg, [vol1_in_new_cg, vol2_in_new_cg], + cgsnapshot=src_cgsnap, + snapshots=[snap1_in_src_cgsnap, + snap2_in_src_cgsnap]) + + expect_cmd = [ + mock.call(*self.testData.LUN_DELETE_CMD( + vol2_in_new_cg['name'] + '_dest')), + mock.call('lun', '-detach', '-name', vol2_in_new_cg['name'], '-o'), + mock.call(*self.testData.LUN_DELETE_CMD(vol2_in_new_cg['name'])), + mock.call(*self.testData.LUN_DELETE_CMD( + vol1_in_new_cg['name'] + '_dest')), + mock.call('lun', '-detach', '-name', vol1_in_new_cg['name'], '-o'), + mock.call(*self.testData.LUN_DELETE_CMD(vol1_in_new_cg['name'])), + mock.call(*td.DELETE_CG_SNAPSHOT(copied_snap_name))] + fake_cli.assert_has_calls(expect_cmd) + def test_deregister_initiator(self): fake_cli = self.driverSetup() self.driver.cli.destroy_empty_sg = True diff --git a/cinder/volume/drivers/emc/emc_cli_fc.py b/cinder/volume/drivers/emc/emc_cli_fc.py index aed9c3c89..69c15bdaf 100644 --- a/cinder/volume/drivers/emc/emc_cli_fc.py +++ b/cinder/volume/drivers/emc/emc_cli_fc.py @@ -55,6 +55,7 @@ class EMCCLIFCDriver(driver.FibreChannelDriver): 5.2.0 - Pool-aware scheduler support 5.3.0 - Consistency group modification support 6.0.0 - Over subscription support + Create consistency group from cgsnapshot support """ def __init__(self, *args, **kwargs): @@ -253,3 +254,12 @@ class EMCCLIFCDriver(driver.FibreChannelDriver): def unmanage(self, volume): """Unmanages a volume.""" return self.cli.unmanage(volume) + + def create_consistencygroup_from_src(self, context, group, volumes, + cgsnapshot=None, snapshots=None): + """Creates a consistency group from source.""" + return self.cli.create_consistencygroup_from_src(context, + group, + volumes, + cgsnapshot, + snapshots) diff --git a/cinder/volume/drivers/emc/emc_cli_iscsi.py b/cinder/volume/drivers/emc/emc_cli_iscsi.py index 8625580cc..72b0e946a 100644 --- a/cinder/volume/drivers/emc/emc_cli_iscsi.py +++ b/cinder/volume/drivers/emc/emc_cli_iscsi.py @@ -53,6 +53,7 @@ class EMCCLIISCSIDriver(driver.ISCSIDriver): 5.2.0 - Pool-aware scheduler support 5.3.0 - Consistency group modification support 6.0.0 - Over subscription support + Create consistency group from cgsnapshot support """ def __init__(self, *args, **kwargs): @@ -232,3 +233,12 @@ class EMCCLIISCSIDriver(driver.ISCSIDriver): def unmanage(self, volume): """Unmanages a volume.""" self.cli.unmanage(volume) + + def create_consistencygroup_from_src(self, context, group, volumes, + cgsnapshot=None, snapshots=None): + """Creates a consistency group from source.""" + return self.cli.create_consistencygroup_from_src(context, + group, + volumes, + cgsnapshot, + snapshots) diff --git a/cinder/volume/drivers/emc/emc_vnx_cli.py b/cinder/volume/drivers/emc/emc_vnx_cli.py index 820c38319..75bae5798 100644 --- a/cinder/volume/drivers/emc/emc_vnx_cli.py +++ b/cinder/volume/drivers/emc/emc_vnx_cli.py @@ -272,6 +272,7 @@ class CommandLineHelper(object): CLI_RESP_PATTERN_LUN_NOT_MIGRATING = ('The specified source LUN ' 'is not currently migrating') CLI_RESP_PATTERN_LUN_IS_PREPARING = '0x712d8e0e' + CLI_RESP_PATTERM_IS_NOT_SMP = 'it is not a snapshot mount point' def __init__(self, configuration): configuration.append_config_values(san.san_opts) @@ -589,13 +590,15 @@ class CommandLineHelper(object): if rc != 0: self._raise_cli_error(command_modify_lun, rc, out) - def create_consistencygroup(self, context, group): + def create_consistencygroup(self, cg_name, members=None): """create the consistency group.""" - cg_name = group['id'] command_create_cg = ('-np', 'snap', '-group', '-create', '-name', cg_name, '-allowSnapAutoDelete', 'no') + if members: + command_create_cg += ('-res', ','.join(map(six.text_type, + members))) out, rc = self.command_execute(*command_create_cg) if rc != 0: @@ -729,9 +732,8 @@ class CommandLineHelper(object): else: self._raise_cli_error(create_cg_snap_cmd, rc, out) - def delete_cgsnapshot(self, cgsnapshot): + def delete_cgsnapshot(self, snap_name): """Delete a cgsnapshot (snap group).""" - snap_name = cgsnapshot['id'] delete_cg_snap_cmd = ('-np', 'snap', '-destroy', '-id', snap_name, '-o') @@ -822,6 +824,27 @@ class CommandLineHelper(object): return rc + def copy_snapshot(self, src_snap_name, new_name): + + copy_snap_cmd = ('snap', '-copy', + '-id', src_snap_name, + '-name', new_name, + '-ignoreMigrationCheck', + '-ignoreDeduplicationCheck') + + out, rc = self.command_execute(*copy_snap_cmd) + if rc != 0: + self._raise_cli_error(copy_snap_cmd, rc, out) + + def allow_snapshot_readwrite_and_autodelete(self, snap_name): + + modify_cmd = ('snap', '-modify', '-id', snap_name, + '-allowReadWrite', 'yes', '-allowAutoDelete', 'yes') + + out, rc = self.command_execute(*modify_cmd) + if rc != 0: + self._raise_cli_error(modify_cmd, rc, out) + def attach_mount_point(self, name, snapshot_name): command_attach_mount_point = ('lun', '-attach', @@ -876,11 +899,11 @@ class CommandLineHelper(object): return rc - def migrate_lun_with_verification(self, src_id, - dst_id=None, - dst_name=None): + def migrate_lun_without_verification(self, src_id, dst_id, + dst_name=None): try: self.migrate_lun(src_id, dst_id) + return True except exception.EMCVnxCLICmdError as ex: migration_succeed = False orig_out = "\n".join(ex.kwargs["out"]) @@ -904,7 +927,10 @@ class CommandLineHelper(object): "start failed. LUN: %s"), dst_name) self.delete_lun(dst_name) return False + else: + return True + def verify_lun_migration(self, src_id): # Set the proper interval to verify the migration status def migration_is_ready(poll=False): mig_ready = False @@ -954,8 +980,6 @@ class CommandLineHelper(object): self._raise_cli_error(cmd_migrate_list, rc, out) return False - eventlet.sleep(INTERVAL_30_SEC) - try: if migration_is_ready(True): return True @@ -984,6 +1008,17 @@ class CommandLineHelper(object): if rc != 0: self._raise_cli_error(cmd_migrate_cancel, rc, out) + def migrate_lun_with_verification(self, src_id, + dst_id, + dst_name=None): + migration_started = self.migrate_lun_without_verification( + src_id, dst_id, dst_name) + if not migration_started: + return False + + eventlet.sleep(INTERVAL_30_SEC) + return self.verify_lun_migration(src_id) + def get_storage_group(self, name, poll=True): # ALU/HLU as key/value map @@ -2257,7 +2292,7 @@ class EMCVnxCliBase(object): new_lun_id = flow_engine.storage.fetch('new_lun_id') # Delete temp Snapshot if consistencygroup_id: - self._client.delete_cgsnapshot(snapshot) + self._client.delete_cgsnapshot(snapshot['id']) else: self.delete_snapshot(snapshot) @@ -2321,7 +2356,7 @@ class EMCVnxCliBase(object): model_update = {'status': 'available'} try: - self._client.create_consistencygroup(context, group) + self._client.create_consistencygroup(group['id']) except Exception: with excutils.save_and_reraise_exception(): LOG.error(_LE('Create consistency group %s failed.'), @@ -2426,7 +2461,7 @@ class EMCVnxCliBase(object): 'group_name': cgsnapshot['consistencygroup_id']}) try: - self._client.delete_cgsnapshot(cgsnapshot) + self._client.delete_cgsnapshot(cgsnapshot['id']) for snapshot in snapshots: snapshot['status'] = 'deleted' except Exception: @@ -3005,6 +3040,96 @@ class EMCVnxCliBase(object): """Unmanages a volume""" pass + def create_consistencygroup_from_src(self, context, group, volumes, + cgsnapshot=None, snapshots=None): + """Creates a consistency group from cgsnapshot.""" + + if not cgsnapshot or not snapshots: + msg = _("create_consistencygroup_from_src only supports a " + "cgsnapshot source, other sources cannot be used.") + raise exception.InvalidInput(msg) + + flow_name = 'create_consistencygroup_from_cgsnapshot' + work_flow = linear_flow.Flow(flow_name) + copied_snapshot_name = 'temp_snapshot_for_%s' % group['id'] + store_spec = { + 'group': group, + 'src_snap_name': cgsnapshot['id'], + 'snap_name': copied_snapshot_name, + 'client': self._client + } + + work_flow.add(CopySnapshotTask(), + AllowReadWriteOnSnapshotTask()) + + # Add tasks for each volumes in the consistency group + lun_id_key_template = 'new_lun_id_%s' + lun_data_key_template = 'vol_%s' + volume_model_updates = [] + + for i, (volume, snap) in enumerate(zip(volumes, snapshots)): + specs = self.get_volumetype_extraspecs(volume) + provisioning, tiering = self._get_and_validate_extra_specs(specs) + pool_name = self. get_target_storagepool(volume, snap['volume']) + sub_store_spec = { + 'volume': volume, + 'source_vol_name': snap['volume_name'], + 'pool_name': pool_name, + 'dest_vol_name': volume['name'] + '_dest', + 'volume_size': volume['size'], + 'provisioning': provisioning, + 'tiering': tiering, + } + work_flow.add( + CreateSMPTask(name="CreateSMPTask%s" % i, + inject=sub_store_spec), + AttachSnapTask(name="AttachSnapTask%s" % i, + inject=sub_store_spec), + CreateDestLunTask(name="CreateDestLunTask%s" % i, + providers=lun_data_key_template % i, + inject=sub_store_spec), + MigrateLunTask(name="MigrateLunTask%s" % i, + providers=lun_id_key_template % i, + inject=sub_store_spec, + rebind={'lun_data': lun_data_key_template % i}, + wait_for_completion=False)) + + volume_model_updates.append({'id': volume['id']}) + volume_host = volume['host'] + host = vol_utils.extract_host(volume_host, 'backend') + host_and_pool = vol_utils.append_host(host, pool_name) + if volume_host != host_and_pool: + volume_model_updates[i]['host'] = host_and_pool + + work_flow.add(WaitMigrationsCompleteTask(lun_id_key_template, + len(volumes)), + CreateConsistencyGroupTask(lun_id_key_template, + len(volumes))) + + flow_engine = taskflow.engines.load(work_flow, store=store_spec) + flow_engine.run() + + # Delete copied snapshot + try: + self._client.delete_cgsnapshot(copied_snapshot_name) + except exception.EMCVnxCLICmdError as ex: + LOG.warning(_LW('Delete the temporary cgsnapshot %(name)s failed. ' + 'This temporary cgsnapshot can be deleted ' + 'manually. Consistency group %(cg)s is created ' + 'successfully from cgsnapshot %(cgsnapshot)s. ' + 'Message: %(msg)s'), + {'name': copied_snapshot_name, + 'cg': group['id'], + 'cgsnapshot': cgsnapshot['id'], + 'msg': ex.kwargs['out']}) + + for i, update in enumerate(volume_model_updates): + new_lun_id = flow_engine.storage.fetch(lun_id_key_template % i) + update['provider_location'] = ( + self._build_provider_location_for_lun(new_lun_id)) + + return None, volume_model_updates + @decorate_all_methods(log_enter_exit) class EMCVnxCliPool(EMCVnxCliBase): @@ -3148,7 +3273,15 @@ class AttachSnapTask(task.Task): else: LOG.warning(_LW('AttachSnapTask.revert: detach mount point %s'), volume['name']) - client.detach_mount_point(volume['name']) + try: + client.detach_mount_point(volume['name']) + except exception.EMCVnxCLICmdError as ex: + with excutils.save_and_reraise_exception() as ctxt: + is_not_smp_err = ( + ex.kwargs["rc"] == 163 and + client.CLI_RESP_PATTERM_IS_NOT_SMP in + "".join(ex.kwargs["out"])) + ctxt.reraise = not is_not_smp_err class CreateDestLunTask(task.Task): @@ -3156,8 +3289,10 @@ class CreateDestLunTask(task.Task): Reversion strategy: Delete the temp destination lun. """ - def __init__(self): - super(CreateDestLunTask, self).__init__(provides='lun_data') + def __init__(self, name=None, providers='lun_data', inject=None): + super(CreateDestLunTask, self).__init__(name=name, + provides=providers, + inject=inject) def execute(self, client, pool_name, dest_vol_name, volume_size, provisioning, tiering, *args, **kwargs): @@ -3182,8 +3317,13 @@ class MigrateLunTask(task.Task): Reversion strategy: None """ - def __init__(self): - super(MigrateLunTask, self).__init__(provides='new_lun_id') + def __init__(self, name=None, providers='new_lun_id', inject=None, + rebind=None, wait_for_completion=True): + super(MigrateLunTask, self).__init__(name=name, + provides=providers, + inject=inject, + rebind=rebind) + self.wait_for_completion = wait_for_completion def execute(self, client, dest_vol_name, volume, lun_data, *args, **kwargs): @@ -3193,10 +3333,13 @@ class MigrateLunTask(task.Task): dest_vol_lun_id = lun_data['lun_id'] LOG.info(_LI('Migrating Mount Point Volume: %s'), new_vol_name) - - migrated = client.migrate_lun_with_verification(new_vol_lun_id, - dest_vol_lun_id, - None) + if self.wait_for_completion: + migrated = client.migrate_lun_with_verification(new_vol_lun_id, + dest_vol_lun_id, + None) + else: + migrated = client.migrate_lun_without_verification( + new_vol_lun_id, dest_vol_lun_id, None) if not migrated: msg = (_("Migrate volume failed between source vol %(src)s" " and dest vol %(dst)s.") % @@ -3237,9 +3380,72 @@ class CreateSnapshotTask(task.Task): LOG.warning(_LW('CreateSnapshotTask.revert: ' 'delete temp cgsnapshot %s'), snapshot['consistencygroup_id']) - client.delete_cgsnapshot(snapshot) + client.delete_cgsnapshot(snapshot['id']) else: LOG.warning(_LW('CreateSnapshotTask.revert: ' 'delete temp snapshot %s'), snapshot['name']) client.delete_snapshot(snapshot['name']) + + +class CopySnapshotTask(task.Task): + """Task to copy a volume snapshot/consistency group snapshot. + + Reversion Strategy: Delete the copied snapshot/cgsnapshot + """ + def execute(self, client, src_snap_name, snap_name, *args, **kwargs): + LOG.debug('CopySnapshotTask.execute') + client.copy_snapshot(src_snap_name, + snap_name) + + def revert(self, result, client, src_snap_name, snap_name, + *args, **kwargs): + LOG.debug('CopySnapshotTask.revert') + if isinstance(result, failure.Failure): + return + else: + LOG.warning(_LW('CopySnapshotTask.revert: delete the ' + 'copied snapshot %(new_name)s of ' + '%(source_name)s.'), + {'new_name': snap_name, + 'source_name': src_snap_name}) + client.delete_cgsnapshot(snap_name) + + +class AllowReadWriteOnSnapshotTask(task.Task): + """Task to modify a Snapshot to allow ReadWrite on it.""" + def execute(self, client, snap_name, *args, **kwargs): + LOG.debug('AllowReadWriteOnSnapshotTask.execute') + client.allow_snapshot_readwrite_and_autodelete(snap_name) + + +class CreateConsistencyGroupTask(task.Task): + """Task to create a consistency group.""" + def __init__(self, lun_id_key_template, num_of_members): + self.lun_id_keys = set( + [lun_id_key_template % i for i in range(num_of_members)]) + super(CreateConsistencyGroupTask, self).__init__( + requires=self.lun_id_keys) + + def execute(self, client, group, *args, **kwargs): + LOG.debug('CreateConsistencyGroupTask.execute') + lun_ids = [kwargs[key] for key in self.lun_id_keys] + client.create_consistencygroup(group['id'], lun_ids) + + +class WaitMigrationsCompleteTask(task.Task): + """Task to wait migrations to be completed.""" + def __init__(self, lun_id_key_template, num_of_members): + self.lun_id_keys = set( + [lun_id_key_template % i for i in range(num_of_members)]) + super(WaitMigrationsCompleteTask, self).__init__( + requires=self.lun_id_keys) + + def execute(self, client, *args, **kwargs): + LOG.debug('WaitMigrationsCompleteTask.execute') + lun_ids = [kwargs[key] for key in self.lun_id_keys] + for lun_id in lun_ids: + migrated = client.verify_lun_migration(lun_id) + if not migrated: + msg = _("Migrate volume %(src)s failed.") % {'src': lun_id} + raise exception.VolumeBackendAPIException(data=msg) -- 2.45.2