]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Create consistgroup from cgsnapshot support in VNX driver
authorTina <tina.tang@emc.com>
Thu, 12 Mar 2015 00:34:36 +0000 (20:34 -0400)
committerTina <tina.tang@emc.com>
Wed, 27 May 2015 09:55:36 +0000 (05:55 -0400)
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
cinder/volume/drivers/emc/emc_cli_fc.py
cinder/volume/drivers/emc/emc_cli_iscsi.py
cinder/volume/drivers/emc/emc_vnx_cli.py

index 2e16e7cf8936008da7df3639a47d1c22c9a9eff0..8fb7cdf9b17f3c95d4a05e213886f54ae8cbc9d1 100644 (file)
@@ -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
index aed9c3c894e2e5d468c2e104186f676eec395e7e..69c15bdafa24e4ff4d2756e1857b1b3bc55f8e28 100644 (file)
@@ -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)
index 8625580cc740f0f296fb94755048bfcc47b49033..72b0e946a5a3d57be4ca402f99b09d9289889e3d 100644 (file)
@@ -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)
index 820c38319d7f0d8076fc050e09e222eae64a401e..75bae5798e351d1e7440b767cbc4538ea23be769 100644 (file)
@@ -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)