]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
EMC VMAX Create CG from CG Snapshot
authorXing Yang <xing.yang@emc.com>
Thu, 13 Aug 2015 13:15:33 +0000 (09:15 -0400)
committerXing Yang <xing.yang@emc.com>
Mon, 10 Aug 2015 23:06:52 +0000 (19:06 -0400)
This patch adds support for create CG from CG snapshot
in the VMAX driver.

implements blueprint emc-vmax-create-cg-from-cgsnapshot
Change-Id: Iab13167382ceebbc6eaa165c431c5a09796ce3e9

cinder/tests/unit/test_emc_vmax.py
cinder/volume/drivers/emc/emc_vmax_common.py
cinder/volume/drivers/emc/emc_vmax_fc.py
cinder/volume/drivers/emc/emc_vmax_iscsi.py
cinder/volume/drivers/emc/emc_vmax_utils.py

index 7d7109bf8be53f5dadecbf92cd19c26801fe8ff0..7c34d30148984584c73dc2f9904bf089392536b3 100644 (file)
@@ -3151,6 +3151,45 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
             conn, volumeInstance, originalName)
         self.assertEqual(originalName, volumeInstance['ElementName'])
 
+    def test_get_volume_model_updates(self):
+        utils = self.driver.common.utils
+        status = 'status-string'
+        volumes = utils.get_volume_model_updates(
+            None, self.driver.db, self.data.test_CG['id'],
+            status)
+        self.assertEqual(status, volumes[0]['status'])
+
+    @mock.patch.object(
+        emc_vmax_utils.EMCVMAXUtils,
+        'find_group_sync_rg_by_target',
+        return_value="")
+    @mock.patch.object(
+        emc_vmax_common.EMCVMAXCommon,
+        '_find_consistency_group',
+        return_value=(None, EMCVMAXCommonData.test_CG))
+    @mock.patch.object(
+        emc_vmax_common.EMCVMAXCommon,
+        '_get_pool_and_storage_system',
+        return_value=(None, EMCVMAXCommonData.storage_system))
+    @mock.patch.object(
+        volume_types,
+        'get_volume_type_extra_specs',
+        return_value={'volume_backend_name': 'ISCSINoFAST'})
+    def test_create_consistencygroup_from_src(
+            self, _mock_volume_type, _mock_storage, _mock_cg, _mock_rg):
+        volumes = []
+        volumes.append(self.data.test_source_volume)
+        snapshots = []
+        self.data.test_snapshot['volume_size'] = "10"
+        snapshots.append(self.data.test_snapshot)
+        model_update, volumes_model_update = (
+            self.driver.create_consistencygroup_from_src(
+                self.data.test_ctxt, self.data.test_CG, volumes,
+                self.data.test_CG_snapshot, snapshots))
+        self.assertEqual({'status': 'available'}, model_update)
+        self.assertEqual([{'status': 'available', 'id': '2'}],
+                         volumes_model_update)
+
     def _cleanup(self):
         if self.config_file_path:
             bExists = os.path.exists(self.config_file_path)
index 402d623fa396d73d0404e234510159aa37a4324c..ae9701d067e4f3094f56a2d2e14a177bc00fdf84 100644 (file)
@@ -64,6 +64,8 @@ WORKLOAD = 'storagetype:workload'
 INTERVAL = 'storagetype:interval'
 RETRIES = 'storagetype:retries'
 ISV3 = 'isV3'
+TRUNCATE_5 = 5
+TRUNCATE_8 = 8
 
 emc_opts = [
     cfg.StrOpt('cinder_emc_config_file',
@@ -2523,8 +2525,8 @@ class EMCVMAXCommon(object):
                 targetVolumeInstance = self.utils.find_volume_instance(
                     self.conn, volumeDict, targetVolumeName)
                 LOG.debug("Create target volume for member volume "
-                          "source volume: %(memberVol)s "
-                          "target volume %(targetVol)s.",
+                          "Source volume: %(memberVol)s "
+                          "Target volume %(targetVol)s.",
                           {'memberVol': memberInstanceName,
                            'targetVol': targetVolumeInstance.path})
                 self.provision.add_volume_to_cg(self.conn,
@@ -3742,7 +3744,7 @@ class EMCVMAXCommon(object):
         # 8 - Detach operation.
         # 9 - Dissolve operation.
         if isSnapshot:
-            # Operation 7: dissolve for snapVx.
+            # Operation 9: dissolve for snapVx.
             operation = self.utils.get_num(9, '16')
         else:
             # Operation 8: detach for clone.
@@ -4132,3 +4134,141 @@ class EMCVMAXCommon(object):
             else:
                 volumeInstanceNames.append(volumeInstance.path)
         return volumeInstanceNames
+
+    def create_consistencygroup_from_src(self, context, group, volumes,
+                                         cgsnapshot, snapshots, db):
+        """Creates the consistency group from source.
+
+        Currently the source can only be a cgsnapshot.
+
+        :param context: the context
+        :param group: the consistency group object to be created
+        :param volumes: volumes in the consistency group
+        :param cgsnapshot: the source consistency group snapshot
+        :param snapshots: snapshots of the source volumes
+        :param db: database
+        :returns: model_update, volumes_model_update
+                  model_update is a dictionary of cg status
+                  volumes_model_update is a list of dictionaries of volume
+                  update
+        """
+        LOG.debug("Enter EMCVMAXCommon::create_consistencygroup_from_src. "
+                  "Group to be created: %(cgId)s, "
+                  "Source snapshot: %(cgSnapshot)s.",
+                  {'cgId': group['id'],
+                   'cgSnapshot': cgsnapshot['consistencygroup_id']})
+
+        volumeTypeId = group['volume_type_id'].replace(",", "")
+        extraSpecs = self._initial_setup(None, volumeTypeId)
+
+        self.create_consistencygroup(context, group)
+        targetCgName = self.utils.truncate_string(group['id'], TRUNCATE_8)
+
+        if not snapshots:
+            exceptionMessage = (_("No source snapshots provided to create "
+                                  "consistency group %s.") % targetCgName)
+            raise exception.VolumeBackendAPIException(
+                data=exceptionMessage)
+
+        modelUpdate = {'status': 'available'}
+
+        _poolInstanceName, storageSystem = (
+            self._get_pool_and_storage_system(extraSpecs))
+        try:
+            replicationService = self.utils.find_replication_service(
+                self.conn, storageSystem)
+            if replicationService is None:
+                exceptionMessage = (_(
+                    "Cannot find replication service on system %s.") %
+                    storageSystem)
+                raise exception.VolumeBackendAPIException(
+                    data=exceptionMessage)
+            targetCgInstanceName = self._find_consistency_group(
+                replicationService, targetCgName)
+            LOG.debug("Create CG %(targetCg)s from snapshot.",
+                      {'targetCg': targetCgInstanceName})
+
+            for volume, snapshot in zip(volumes, snapshots):
+                volumeSizeInbits = int(self.utils.convert_gb_to_bits(
+                    snapshot['volume_size']))
+                targetVolumeName = 'targetVol'
+                volume = {'size': int(self.utils.convert_bits_to_gbs(
+                    volumeSizeInbits))}
+                if extraSpecs[ISV3]:
+                    _rc, volumeDict, _storageSystemName = (
+                        self._create_v3_volume(
+                            volume, targetVolumeName, volumeSizeInbits,
+                            extraSpecs))
+                else:
+                    _rc, volumeDict, _storageSystemName = (
+                        self._create_composite_volume(
+                            volume, targetVolumeName, volumeSizeInbits,
+                            extraSpecs))
+                targetVolumeInstance = self.utils.find_volume_instance(
+                    self.conn, volumeDict, targetVolumeName)
+                LOG.debug("Create target volume for member snapshot. "
+                          "Source snapshot: %(snapshot)s, "
+                          "Target volume: %(targetVol)s.",
+                          {'snapshot': snapshot['id'],
+                           'targetVol': targetVolumeInstance.path})
+
+                self.provision.add_volume_to_cg(self.conn,
+                                                replicationService,
+                                                targetCgInstanceName,
+                                                targetVolumeInstance.path,
+                                                targetCgName,
+                                                targetVolumeName,
+                                                extraSpecs)
+
+            sourceCgName = self.utils.truncate_string(cgsnapshot['id'],
+                                                      TRUNCATE_8)
+            sourceCgInstanceName = self._find_consistency_group(
+                replicationService, sourceCgName)
+            if sourceCgInstanceName is None:
+                exceptionMessage = (_("Cannot find source CG instance. "
+                                      "consistencygroup_id: %s.") %
+                                    cgsnapshot['consistencygroup_id'])
+                raise exception.VolumeBackendAPIException(
+                    data=exceptionMessage)
+            relationName = self.utils.truncate_string(group['id'], TRUNCATE_5)
+            if extraSpecs[ISV3]:
+                self.provisionv3.create_group_replica(
+                    self.conn, replicationService, sourceCgInstanceName,
+                    targetCgInstanceName, relationName, extraSpecs)
+            else:
+                self.provision.create_group_replica(
+                    self.conn, replicationService, sourceCgInstanceName,
+                    targetCgInstanceName, relationName, extraSpecs)
+            # Break the replica group relationship.
+            rgSyncInstanceName = self.utils.find_group_sync_rg_by_target(
+                self.conn, storageSystem, targetCgInstanceName, extraSpecs,
+                True)
+
+            if rgSyncInstanceName is not None:
+                if extraSpecs[ISV3]:
+                    # Operation 9: dissolve for snapVx
+                    operation = self.utils.get_num(9, '16')
+                    self.provisionv3.break_replication_relationship(
+                        self.conn, replicationService, rgSyncInstanceName,
+                        operation, extraSpecs)
+                else:
+                    self.provision.delete_clone_relationship(
+                        self.conn, replicationService,
+                        rgSyncInstanceName, extraSpecs)
+        except Exception as ex:
+            modelUpdate['status'] = 'error'
+            cgSnapshotId = cgsnapshot['consistencygroup_id']
+            volumes_model_update = self.utils.get_volume_model_updates(
+                context, db, group['id'], modelUpdate['status'])
+            LOG.error(_LE("Exception: %(ex)s."), {'ex': ex})
+            exceptionMessage = (_("Failed to create CG %(cgName)s "
+                                  "from snapshot %(cgSnapshot)s.")
+                                % {'cgName': targetCgName,
+                                   'cgSnapshot': cgSnapshotId})
+            LOG.error(exceptionMessage)
+            return modelUpdate, volumes_model_update
+
+        volumes_model_update = self.utils.get_volume_model_updates(
+            context, db, group['id'], modelUpdate['status'])
+
+        return modelUpdate, volumes_model_update
index 08376cd3f71da366cffd70110d2fe77947fbd57e..13404bc4229e04ebe123a47f29d32ffd28af9347 100644 (file)
@@ -41,9 +41,10 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
         2.2.1 - Support for SE 8.0.3
         2.2.2 - Update Consistency Group
         2.2.3 - Pool aware scheduler(multi-pool) support
+        2.2.4 - Create CG from CG snapshot
     """
 
-    VERSION = "2.2.3"
+    VERSION = "2.2.4"
 
     def __init__(self, *args, **kwargs):
 
@@ -356,3 +357,21 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
         """Updates LUNs in consistency group."""
         return self.common.update_consistencygroup(group, add_volumes,
                                                    remove_volumes)
+
+    def create_consistencygroup_from_src(self, context, group, volumes,
+                                         cgsnapshot=None, snapshots=None,
+                                         source_cg=None, source_vols=None):
+        """Creates the consistency group from source.
+
+        Currently the source can only be a cgsnapshot.
+
+        :param context: the context
+        :param group: the consistency group object to be created
+        :param volumes: volumes in the consistency group
+        :param cgsnapshot: the source consistency group snapshot
+        :param snapshots: snapshots of the source volumes
+        :param source_cg: the dictionary of a consistency group as source.
+        :param source_vols: a list of volume dictionaries in the source_cg.
+        """
+        return self.common.create_consistencygroup_from_src(
+            context, group, volumes, cgsnapshot, snapshots, self.db)
index cfae7121fc2ee1b8452a5fc9db9dc52c7d030890..179cada101e5e18aaaf49d2d435a1873a35d03d9 100644 (file)
@@ -49,9 +49,10 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
         2.2.1 - Support for SE 8.0.3
         2.2.2 - Update Consistency Group
         2.2.3 - Pool aware scheduler(multi-pool) support
+        2.2.4 - Create CG from CG snapshot
     """
 
-    VERSION = "2.2.3"
+    VERSION = "2.2.4"
 
     def __init__(self, *args, **kwargs):
 
@@ -359,3 +360,21 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
         """Updates LUNs in consistency group."""
         return self.common.update_consistencygroup(group, add_volumes,
                                                    remove_volumes)
+
+    def create_consistencygroup_from_src(self, context, group, volumes,
+                                         cgsnapshot=None, snapshots=None,
+                                         source_cg=None, source_vols=None):
+        """Creates the consistency group from source.
+
+        Currently the source can only be a cgsnapshot.
+
+        :param context: the context
+        :param group: the consistency group object to be created
+        :param volumes: volumes in the consistency group
+        :param cgsnapshot: the source consistency group snapshot
+        :param snapshots: snapshots of the source volumes
+        :param source_cg: the dictionary of a consistency group as source.
+        :param source_vols: a list of volume dictionaries in the source_cg.
+        """
+        return self.common.create_consistencygroup_from_src(
+            context, group, volumes, cgsnapshot, snapshots, self.db)
index 41457ca35958e3088616ccec7e28ba1e81bdda74..722c33ef81c2ba7daac2e4621f0eaab1b095f138 100644 (file)
@@ -2259,3 +2259,27 @@ class EMCVMAXUtils(object):
                 {'source': sourceDeviceId, 'storageSystem': storageSystem})
 
         return foundSyncInstanceName
+
+    def get_volume_model_updates(
+            self, context, db, cgId, status='available'):
+        """Update the volume model's status and return it.
+
+        :param context: the context
+        :param db: cinder database
+        :param cgId: cg id
+        :param status: string value reflects the status of the member volume
+        :returns: volume_model_updates - updated volumes
+        """
+        volume_model_updates = []
+        volumes = db.volume_get_all_by_group(context, cgId)
+        LOG.info(_LI(
+            "Updating status for CG: %(id)s."),
+            {'id': cgId})
+        if volumes:
+            for volume in volumes:
+                volume_model_updates.append({'id': volume['id'],
+                                             'status': status})
+        else:
+            LOG.info(_LI("No volume found for CG: %(cg)s."),
+                     {'cg': cgId})
+        return volume_model_updates