]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Check license before clone in VMAX driver
authorXing Yang <xing.yang@emc.com>
Thu, 26 Feb 2015 17:27:16 +0000 (12:27 -0500)
committerXing Yang <xing.yang@emc.com>
Wed, 18 Mar 2015 05:08:53 +0000 (01:08 -0400)
This patch checks if a license for clone is available before
doing the clone operation.

Change-Id: I69d009b2cac775c301e9a5254079238b2b2a3b10
Closes-Bug: #1385450

cinder/tests/test_emc_vmax.py
cinder/volume/drivers/emc/emc_vmax_common.py
cinder/volume/drivers/emc/emc_vmax_utils.py

index d54119f2d9eae27f6e63b73629284947d8058814..996929a0c841a932c6c06ccf7301e4a7a965c246 100644 (file)
@@ -138,6 +138,11 @@ class Fake_CIMProperty():
         cimproperty.value = 'OS-myhost-MV'
         return cimproperty
 
+    def fake_getSupportedReplicationTypes(self):
+        cimproperty = Fake_CIMProperty()
+        cimproperty.value = [2L, 10L]
+        return cimproperty
+
 
 class Fake_CIM_TierPolicyServiceCapabilities():
 
@@ -540,6 +545,8 @@ class FakeEcomConnection():
             result = self._enum_storagesystems()
         elif name == 'Symm_TierPolicyRule':
             result = self._enum_policyrules()
+        elif name == 'CIM_ReplicationServiceCapabilities':
+            result = self._enum_repservcpbls()
         else:
             result = self._default_enum()
         return result
@@ -590,6 +597,8 @@ class FakeEcomConnection():
             result = self._getinstance_storagehardwareid(objectpath)
         elif name == 'Symm_VirtualProvisioningPool':
             result = self._getinstance_pool(objectpath)
+        elif name == 'Symm_ReplicationServiceCapabilities':
+            result = self._getinstance_replicationServCapabilities(objectpath)
         else:
             result = self._default_getinstance(objectpath)
 
@@ -672,6 +681,8 @@ class FakeEcomConnection():
             result = self._enum_initMaskingGroup()
         elif ResultClass == 'Symm_LunMaskingView':
             result = self._enum_maskingView()
+        elif ResultClass == 'EMC_Meta':
+            result = self._enum_metavolume()
         else:
             result = self._default_assocnames(objectpath)
         return result
@@ -1013,6 +1024,15 @@ class FakeEcomConnection():
         svInstance['PercentSynced'] = 100
         return svInstance
 
+    def _getinstance_replicationServCapabilities(self, objectpath):
+        repServCpblInstance = SYMM_SrpStoragePool()
+        classcimproperty = Fake_CIMProperty()
+        repTypesCimproperty = (
+            classcimproperty.fake_getSupportedReplicationTypes())
+        properties = {u'SupportedReplicationTypes': repTypesCimproperty}
+        repServCpblInstance.properties = properties
+        return repServCpblInstance
+
     def _default_getinstance(self, objectpath):
         return objectpath
 
@@ -1377,6 +1397,9 @@ class FakeEcomConnection():
         portgroups.append(portgroup)
         return portgroups
 
+    def _enum_metavolume(self):
+        return []
+
     def _default_enum(self):
         names = []
         name = {}
@@ -2995,31 +3018,6 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
                           self.data.test_volume,
                           EMCVMAXCommonData.test_source_volume)
 
-    @mock.patch.object(
-        emc_vmax_utils.EMCVMAXUtils,
-        'get_volume_meta_head',
-        return_value=None)
-    @mock.patch.object(
-        emc_vmax_common.EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
-    @mock.patch.object(
-        FakeDB,
-        'volume_get',
-        return_value=EMCVMAXCommonData.test_source_volume)
-    @mock.patch.object(
-        volume_types,
-        'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'ISCSIFAST'})
-    def test_create_clone_simple_volume_fast_success(
-            self, mock_volume_type, mock_volume, mock_sync_sv,
-            mock_simple_volume):
-        self.data.test_volume['volume_name'] = "vmax-1234567"
-        self.driver.common.fast.is_volume_in_default_SG = (
-            mock.Mock(return_value=True))
-        self.driver.create_cloned_volume(self.data.test_volume,
-                                         EMCVMAXCommonData.test_source_volume)
-
     @mock.patch.object(
         emc_vmax_common.EMCVMAXCommon,
         '_get_pool_and_storage_system',
@@ -4098,28 +4096,23 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
                           self.data.test_volume,
                           EMCVMAXCommonData.test_source_volume)
 
-    @mock.patch.object(
-        emc_vmax_utils.EMCVMAXUtils,
-        'get_volume_meta_head',
-        return_value=None)
-    @mock.patch.object(
-        emc_vmax_common.EMCVMAXCommon,
-        '_find_storage_sync_sv_sv',
-        return_value=(None, None))
-    @mock.patch.object(
-        FakeDB,
-        'volume_get',
-        return_value=EMCVMAXCommonData.test_source_volume)
-    @mock.patch.object(
-        volume_types,
-        'get_volume_type_extra_specs',
-        return_value={'volume_backend_name': 'FCFAST'})
-    def test_create_clone_simple_volume_fast_success(
-            self, mock_volume_type, mock_volume, mock_sync_sv,
-            mock_simple_volume):
+    def test_create_clone_simple_volume_fast_success(self):
+        extraSpecs = {'storagetype:fastpolicy': 'FC_GOLD1',
+                      'volume_backend_name': 'FCFAST',
+                      'isV3': False}
+        self.driver.common._initial_setup = (
+            mock.Mock(return_value=extraSpecs))
+        self.driver.common.extraSpecs = extraSpecs
+        self.driver.utils.is_clone_licensed = (
+            mock.Mock(return_value=True))
+        FakeDB.volume_get = (
+            mock.Mock(return_value=EMCVMAXCommonData.test_source_volume))
         self.data.test_volume['volume_name'] = "vmax-1234567"
         self.driver.common.fast.is_volume_in_default_SG = (
             mock.Mock(return_value=True))
+        self.driver.utils.isArrayV3 = mock.Mock(return_value=False)
+        self.driver.common._find_storage_sync_sv_sv = (
+            mock.Mock(return_value=(None, None)))
         self.driver.create_cloned_volume(self.data.test_volume,
                                          EMCVMAXCommonData.test_source_volume)
 
@@ -4271,6 +4264,19 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
         self.driver.delete_cgsnapshot(
             self.data.test_ctxt, self.data.test_CG_snapshot)
 
+    # Bug 1385450
+    def test_create_clone_without_license(self):
+        mockRepServCap = {}
+        mockRepServCap['InstanceID'] = 'SYMMETRIX+1385450'
+        self.driver.utils.find_replication_service_capabilities = (
+            mock.Mock(return_value=mockRepServCap))
+        self.driver.utils.is_clone_licensed = (
+            mock.Mock(return_value=False))
+        self.assertRaises(exception.VolumeBackendAPIException,
+                          self.driver.create_cloned_volume,
+                          self.data.test_volume,
+                          EMCVMAXCommonData.test_source_volume)
+
     def _cleanup(self):
         bExists = os.path.exists(self.config_file_path)
         if bExists:
index 17552329fdbea9339cdfd170c06fe43d2faf7e61..1c3945c255acc1b341033ac906015803e63ea62d 100644 (file)
@@ -1947,6 +1947,18 @@ class EMCVMAXCommon(object):
 
         sourceInstance = self._find_lun(sourceVolume)
         storageSystem = sourceInstance['SystemName']
+        repServCapabilityInstanceName = (
+            self.utils.find_replication_service_capabilities(self.conn,
+                                                             storageSystem))
+        is_clone_license = self.utils.is_clone_licensed(
+            self.conn, repServCapabilityInstanceName)
+
+        if is_clone_license is False:
+            exceptionMessage = (_(
+                "Clone feature is not licensed on %(storageSystem)s.")
+                % {'storageSystem': storageSystem})
+            LOG.error(exceptionMessage)
+            raise exception.VolumeBackendAPIException(data=exceptionMessage)
 
         repServiceInstanceName = self.utils.find_replication_service(
             self.conn, storageSystem)
index 44bd8a0dab71090929fce3ebdae39cfbfc87d70b..4b92ee97691d5a11ddde502cd85303f40fd83fd8 100644 (file)
@@ -38,6 +38,7 @@ except ImportError:
 
 STORAGEGROUPTYPE = 4
 POSTGROUPTYPE = 3
+CLONE_REPLICATION_TYPE = 10
 
 EMC_ROOT = 'root/emc'
 CONCATENATED = 'concatenated'
@@ -1838,3 +1839,52 @@ class EMCVMAXUtils(object):
             raise exception.VolumeBackendAPIException(
                 data=exceptionMessage)
         return instance
+
+    def find_replication_service_capabilities(self, conn, storageSystemName):
+        """Find the replication service capabilities instance name.
+
+        :param conn: the connection to the ecom server
+        :param storageSystemName: the storage system name
+        :returns: foundRepServCapability
+        """
+        foundRepServCapability = None
+        repservices = conn.EnumerateInstanceNames(
+            'CIM_ReplicationServiceCapabilities')
+        for repservCap in repservices:
+            if storageSystemName in repservCap['InstanceID']:
+                foundRepServCapability = repservCap
+                LOG.debug("Found Replication Service Capabilities: "
+                          "%(repservCap)s",
+                          {'repservCap': repservCap})
+                break
+        if foundRepServCapability is None:
+            exceptionMessage = (_("Replication Service Capability not found "
+                                  "on %(storageSystemName)s.")
+                                % {'storageSystemName': storageSystemName})
+            LOG.error(exceptionMessage)
+            raise exception.VolumeBackendAPIException(data=exceptionMessage)
+
+        return foundRepServCapability
+
+    def is_clone_licensed(self, conn, capabilityInstanceName):
+        """Check if the clone feature is licensed and enabled.
+
+        :param conn: the connection to the ecom server
+        :param capabilityInstanceName: the replication service capabilities
+        instance name
+        :returns: True if licensed and enabled; False otherwise.
+        """
+        capabilityInstance = conn.GetInstance(capabilityInstanceName)
+        propertiesList = capabilityInstance.properties.items()
+        for properties in propertiesList:
+            if properties[0] == 'SupportedReplicationTypes':
+                cimProperties = properties[1]
+                repTypes = cimProperties.value
+                LOG.debug("Found supported replication types: "
+                          "%(repTypes)s",
+                          {'repTypes': repTypes})
+                if CLONE_REPLICATION_TYPE in repTypes:
+                    # Clone is a supported replication type.
+                    LOG.debug("Clone is licensed and enabled.")
+                    return True
+        return False