]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
EMC VMAX Modify CG
authorXing Yang <xing.yang@emc.com>
Tue, 23 Jun 2015 20:49:37 +0000 (16:49 -0400)
committerXing Yang <xing.yang@emc.com>
Thu, 23 Jul 2015 15:17:31 +0000 (11:17 -0400)
This patch adds support for modify CG in VMAX driver.

implements blueprint emc-vmax-modify-cg

Change-Id: I23087f6b15dd305fa4e882ea4d8f30414a63c9c8

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_provision.py

index c1a37d271dda819ea987a69d10a111e404ca09fe..06da6349cf6996e539c97e8c1f4d1f904d577e80 100644 (file)
@@ -1134,6 +1134,11 @@ class FakeEcomConnection(object):
         replic_service['CreationClassName'] = \
             self.data.replication_service_creationclass
         replic_services.append(replic_service)
+        replic_service2 = {}
+        replic_service2['SystemName'] = self.data.storage_system_v3
+        replic_service2['CreationClassName'] = (
+            self.data.replication_service_creationclass)
+        replic_services.append(replic_service2)
         return replic_services
 
     def _enum_pools(self):
@@ -2979,6 +2984,57 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         self.driver.delete_cgsnapshot(
             self.data.test_ctxt, self.data.test_CG_snapshot)
 
+    @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_update_CG_add_volume_no_fast_success(
+            self, _mock_volume_type, _mock_storage_system):
+        add_volumes = []
+        add_volumes.append(self.data.test_source_volume)
+        remove_volumes = None
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+        # Multiple volumes
+        add_volumes.append(self.data.test_source_volume)
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+        # Can't find CG
+        self.driver.common._find_consistency_group = mock.Mock(
+            return_value=None)
+        self.assertRaises(exception.ConsistencyGroupNotFound,
+                          self.driver.update_consistencygroup,
+                          self.data.test_ctxt, self.data.test_CG,
+                          add_volumes, remove_volumes)
+
+    @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_update_CG_remove_volume_no_fast_success(
+            self, _mock_volume_type, _mock_storage_system):
+        remove_volumes = []
+        remove_volumes.append(self.data.test_source_volume)
+        add_volumes = None
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+        # Multiple volumes
+        remove_volumes.append(self.data.test_source_volume)
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+
     # Bug https://bugs.launchpad.net/cinder/+bug/1442376
     @mock.patch.object(
         emc_vmax_common.EMCVMAXCommon,
@@ -3769,6 +3825,50 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
         self.driver.delete_cgsnapshot(
             self.data.test_ctxt, self.data.test_CG_snapshot)
 
+    @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': 'ISCSIFAST'})
+    def test_update_CG_add_volume_fast_success(
+            self, _mock_volume_type, _mock_storage_system):
+        add_volumes = []
+        add_volumes.append(self.data.test_source_volume)
+        remove_volumes = None
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+        # Multiple volumes
+        add_volumes.append(self.data.test_source_volume)
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+
+    @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': 'ISCSIFAST'})
+    def test_update_CG_remove_volume_fast_success(
+            self, _mock_volume_type, _mock_storage_system):
+        remove_volumes = []
+        remove_volumes.append(self.data.test_source_volume)
+        add_volumes = None
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+        # Multiple volumes
+        remove_volumes.append(self.data.test_source_volume)
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+
     def _cleanup(self):
         bExists = os.path.exists(self.config_file_path)
         if bExists:
@@ -5456,6 +5556,57 @@ class EMCV3DriverTestCase(test.TestCase):
         self.driver.delete_cgsnapshot(
             self.data.test_ctxt, self.data.test_CG_snapshot)
 
+    @mock.patch.object(
+        emc_vmax_common.EMCVMAXCommon,
+        '_get_pool_and_storage_system',
+        return_value=(None, EMCVMAXCommonData.storage_system_v3))
+    @mock.patch.object(
+        volume_types,
+        'get_volume_type_extra_specs',
+        return_value={'volume_backend_name': 'V3_BE'})
+    def test_update_CG_add_volume_v3_success(
+            self, _mock_volume_type, _mock_storage_system):
+        add_volumes = []
+        add_volumes.append(self.data.test_source_volume)
+        remove_volumes = None
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+        # Multiple volumes
+        add_volumes.append(self.data.test_source_volume)
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+        # Can't find CG
+        self.driver.common._find_consistency_group = mock.Mock(
+            return_value=None)
+        self.assertRaises(exception.ConsistencyGroupNotFound,
+                          self.driver.update_consistencygroup,
+                          self.data.test_ctxt, self.data.test_CG,
+                          add_volumes, remove_volumes)
+
+    @mock.patch.object(
+        emc_vmax_common.EMCVMAXCommon,
+        '_get_pool_and_storage_system',
+        return_value=(None, EMCVMAXCommonData.storage_system_v3))
+    @mock.patch.object(
+        volume_types,
+        'get_volume_type_extra_specs',
+        return_value={'volume_backend_name': 'V3_BE'})
+    def test_update_CG_remove_volume_v3_success(
+            self, _mock_volume_type, _mock_storage_system):
+        remove_volumes = []
+        remove_volumes.append(self.data.test_source_volume)
+        add_volumes = None
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+        # Multiple volumes
+        remove_volumes.append(self.data.test_source_volume)
+        self.driver.update_consistencygroup(
+            self.data.test_ctxt, self.data.test_CG,
+            add_volumes, remove_volumes)
+
     @mock.patch.object(
         emc_vmax_common.EMCVMAXCommon,
         '_is_same_host',
index 36d071f266323a109d5bf87dcc0675bd0338125a..3ca9924c40b545e3e3c9e16fa43762a2486829ea 100644 (file)
@@ -19,7 +19,6 @@ import os.path
 from oslo_config import cfg
 from oslo_log import log as logging
 from oslo_utils import units
-
 import six
 
 from cinder import exception
@@ -4068,3 +4067,79 @@ class EMCVMAXCommon(object):
         volumeInstance = self.utils.rename_volume(self.conn,
                                                   volumeInstance,
                                                   volumeId)
+
+    def update_consistencygroup(self, group, add_volumes,
+                                remove_volumes):
+        """Updates LUNs in consistency group.
+
+        :param group: storage configuration service instance
+        :param add_volumes: the volumes uuids you want to add to the CG
+        :param remove_volumes: the volumes uuids you want to remove from
+                               the CG
+        """
+        LOG.info(_LI("Update Consistency Group: %(group)s. "
+                     "This adds and/or removes volumes from a CG."),
+                 {'group': group['id']})
+
+        modelUpdate = {'status': 'available'}
+        volumeTypeId = group['volume_type_id'].replace(",", "")
+
+        cg_name = self.utils.truncate_string(group['id'], 8)
+
+        extraSpecs = self._initial_setup(None, volumeTypeId)
+
+        _poolInstanceName, storageSystem = (
+            self._get_pool_and_storage_system(extraSpecs))
+        add_vols = [vol for vol in add_volumes] if add_volumes else []
+        add_instance_names = self._get_volume_instance_names(add_vols)
+        remove_vols = [vol for vol in remove_volumes] if remove_volumes else []
+        remove_instance_names = self._get_volume_instance_names(remove_vols)
+        self.conn = self._get_ecom_connection()
+
+        try:
+            replicationService = self.utils.find_replication_service(
+                self.conn, storageSystem)
+            cgInstanceName = (
+                self._find_consistency_group(replicationService, cg_name))
+            if cgInstanceName is None:
+                raise exception.ConsistencyGroupNotFound(
+                    consistencygroup_id=cg_name)
+            # Add volume(s) to a consistency group
+            if add_instance_names:
+                self.provision.add_volume_to_cg(
+                    self.conn, replicationService, cgInstanceName,
+                    add_instance_names, cg_name, None,
+                    extraSpecs)
+            # Remove volume(s) from a consistency group
+            if remove_instance_names:
+                self.provision.remove_volume_from_cg(
+                    self.conn, replicationService, cgInstanceName,
+                    remove_instance_names, cg_name, None,
+                    extraSpecs)
+        except exception.ConsistencyGroupNotFound:
+            raise
+        except Exception as ex:
+            LOG.error(_LE("Exception: %(ex)s"), {'ex': ex})
+            exceptionMessage = (_("Failed to update consistency group:"
+                                  " %(cgName)s.")
+                                % {'cgName': cg_name})
+            LOG.error(exceptionMessage)
+            raise exception.VolumeBackendAPIException(data=exceptionMessage)
+
+        return modelUpdate, None, None
+
+    def _get_volume_instance_names(self, volumes):
+        """Get volume instance names from volume.
+
+        :param volumes: volume objects
+        :returns: volume instance names
+        """
+        volumeInstanceNames = []
+        for volume in volumes:
+            volumeInstance = self._find_lun(volume)
+            if volumeInstance is None:
+                LOG.error(_LE("Volume %(name)s not found on the array."),
+                          {'name': volume['name']})
+            else:
+                volumeInstanceNames.append(volumeInstance.path)
+        return volumeInstanceNames
index 8f031a2d0a95df765218efe18cabaa1843285bcf..93da018b74bd427c894cdb5b94788374cc582f0d 100644 (file)
@@ -39,9 +39,10 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
         2.1.3 - Fixed a problem with FAST support (bug #1435069)
         2.2.0 - Add manage/unmanage
         2.2.1 - Support for SE 8.0.3
+        2.2.2 - Update Consistency Group
     """
 
-    VERSION = "2.2.1"
+    VERSION = "2.2.2"
 
     def __init__(self, *args, **kwargs):
 
@@ -347,3 +348,9 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
         Leave the volume intact on the backend array.
         """
         return self.common.unmanage(volume)
+
+    def update_consistencygroup(self, context, group,
+                                add_volumes, remove_volumes):
+        """Updates LUNs in consistency group."""
+        return self.common.update_consistencygroup(group, add_volumes,
+                                                   remove_volumes)
index 35c6a2d47fddcd355414c5962756c4aea690c202..9d60ee457a3b5fa9dccecfbf92c1cd59ff7f0538 100644 (file)
@@ -47,16 +47,17 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
         2.1.3 - Fixed a problem with FAST support (bug #1435069)
         2.2.0 - Add manage/unmanage
         2.2.1 - Support for SE 8.0.3
+        2.2.2 - Update Consistency Group
     """
 
-    VERSION = "2.2.1"
+    VERSION = "2.2.2"
 
     def __init__(self, *args, **kwargs):
 
         super(EMCVMAXISCSIDriver, self).__init__(*args, **kwargs)
-        self.common =\
+        self.common = (
             emc_vmax_common.EMCVMAXCommon('iSCSI',
-                                          configuration=self.configuration)
+                                          configuration=self.configuration))
 
     def check_for_setup_error(self):
         pass
@@ -352,3 +353,9 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
         backend array.
         """
         return self.common.unmanage(volume)
+
+    def update_consistencygroup(self, context, group,
+                                add_volumes, remove_volumes):
+        """Updates LUNs in consistency group."""
+        return self.common.update_consistencygroup(group, add_volumes,
+                                                   remove_volumes)
index 9a234490a0129289752e97914ac13fbfe120acfd..4948a8cb86b96490edab3f605062eba2629e7ba6 100644 (file)
@@ -967,10 +967,16 @@ class EMCVMAXProvision(object):
         """
         startTime = time.time()
 
+        if isinstance(volumeInstanceName, list):
+            theElements = volumeInstanceName
+            volumeName = 'Bulk Add'
+        else:
+            theElements = [volumeInstanceName]
+
         rc, job = conn.InvokeMethod(
             'AddMembers',
             replicationService,
-            Members=[volumeInstanceName],
+            Members=theElements,
             ReplicationGroup=cgInstanceName)
 
         if rc != 0:
@@ -978,9 +984,9 @@ class EMCVMAXProvision(object):
                                                              extraSpecs)
             if rc != 0:
                 exceptionMessage = (_(
-                    "Failed to add volume %(volumeName)s: "
-                    "to consistency group %(cgName)s "
-                    "Return code: %(rc)lu.  Error: %(error)s.")
+                    "Failed to add volume %(volumeName)s "
+                    "to consistency group %(cgName)s. "
+                    "Return code: %(rc)lu. Error: %(error)s.")
                     % {'volumeName': volumeName,
                        'cgName': cgName,
                        'rc': rc,
@@ -1013,21 +1019,26 @@ class EMCVMAXProvision(object):
         """
         startTime = time.time()
 
+        if isinstance(volumeInstanceName, list):
+            theElements = volumeInstanceName
+            volumeName = 'Bulk Remove'
+        else:
+            theElements = [volumeInstanceName]
+
         rc, job = conn.InvokeMethod(
             'RemoveMembers',
             replicationService,
-            Members=[volumeInstanceName],
-            ReplicationGroup=cgInstanceName,
-            RemoveElements=True)
+            Members=theElements,
+            ReplicationGroup=cgInstanceName)
 
         if rc != 0:
             rc, errordesc = self.utils.wait_for_job_complete(conn, job,
                                                              extraSpecs)
             if rc != 0:
                 exceptionMessage = (_(
-                    "Failed to remove volume %(volumeName)s: "
-                    "to consistency group %(cgName)s "
-                    "Return code: %(rc)lu.  Error: %(error)s.")
+                    "Failed to remove volume %(volumeName)s "
+                    "from consistency group %(cgName)s. "
+                    "Return code: %(rc)lu. Error: %(error)s.")
                     % {'volumeName': volumeName,
                        'cgName': cgName,
                        'rc': rc,