diff --git a/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py b/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py index 760adb1dcc1..1141a6a88ba 100644 --- a/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py +++ b/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py @@ -158,6 +158,20 @@ class Fake_CIMProperty(object): cimproperty.value = '10.10.10.10' return cimproperty + def fake_getSupportedReplicationTypesCIMProperty(self, reptypes): + cimproperty = Fake_CIMProperty() + if reptypes == 'V3': + cimproperty.value = [6, 7] + elif reptypes == 'V3_SYNC': + cimproperty.value = [6] + elif reptypes == 'V3_ASYNC': + cimproperty.value = [7] + elif reptypes == 'V2': + cimproperty.value = [10] + else: + cimproperty.value = [2, 3, 4, 5] + return cimproperty + class Fake_CIM_TierPolicyServiceCapabilities(object): @@ -6031,6 +6045,8 @@ class EMCV3DriverTestCase(test.TestCase): common = self.driver.common common.provisionv3.utils.get_v3_default_sg_instance_name = mock.Mock( return_value=(None, None, self.data.default_sg_instance_name)) + common.utils.is_clone_licensed = ( + mock.Mock(return_value=True)) common._initial_setup = mock.Mock( return_value=self.default_extraspec()) self.driver.create_snapshot(self.data.test_volume_v3) @@ -6078,6 +6094,8 @@ class EMCV3DriverTestCase(test.TestCase): cloneVol['BlockSize'] = self.data.block_size cloneVol['host'] = self.data.fake_host_v3 common = self.driver.common + common.utils.is_clone_licensed = ( + mock.Mock(return_value=True)) common._initial_setup = mock.Mock( return_value=self.default_extraspec()) common._get_or_create_storage_group_v3 = mock.Mock( @@ -6390,6 +6408,8 @@ class EMCV3DriverTestCase(test.TestCase): self.data.test_volume['volume_name'] = "vmax-1234567" e = exception.VolumeBackendAPIException('CreateElementReplica Ex') common = self.driver.common + common.utils.is_clone_licensed = ( + mock.Mock(return_value=True)) volumeDict = {'classname': u'Symm_StorageVolume', 'keybindings': EMCVMAXCommonData.keybindings} common._create_v3_volume = ( @@ -7826,6 +7846,7 @@ class EMCVMAXFCTest(test.TestCase): self.assertEqual(0, len(mvInstances)) +@ddt.ddt class EMCVMAXUtilsTest(test.TestCase): def setUp(self): self.data = EMCVMAXCommonData() @@ -7889,6 +7910,40 @@ class EMCVMAXUtilsTest(test.TestCase): emc_vmax_provision.COPY_ON_WRITE, extraSpecs) self.assertIsNotNone(rsdInstance) + def getinstance_capability(self, reptypes): + repservicecap = CIM_ReplicationServiceCapabilities() + repservicecap['CreationClassName'] = ( + 'CIM_ReplicationServiceCapabilities') + + classcimproperty = Fake_CIMProperty() + supportedReplicationTypes = ( + classcimproperty.fake_getSupportedReplicationTypesCIMProperty( + reptypes)) + properties = {u'SupportedReplicationTypes': supportedReplicationTypes} + repservicecap.properties = properties + return repservicecap + + @ddt.data(('V3', True), ('V3_ASYNC', True), ('V3_SYNC', True), + ('V2', False)) + @ddt.unpack + def test_is_clone_licensed(self, reptypes, isV3): + conn = FakeEcomConnection() + capabilityInstanceName = self.getinstance_capability(reptypes) + conn.GetInstance = mock.Mock( + return_value=capabilityInstanceName) + self.assertTrue(self.driver.utils.is_clone_licensed( + conn, capabilityInstanceName, isV3)) + + def test_is_clone_licensed_false(self): + conn = FakeEcomConnection() + isV3 = True + reptypes = None + capabilityInstanceName = self.getinstance_capability(reptypes) + conn.GetInstance = mock.Mock( + return_value=capabilityInstanceName) + self.assertFalse(self.driver.utils.is_clone_licensed( + conn, capabilityInstanceName, isV3)) + class EMCVMAXCommonTest(test.TestCase): def setUp(self): diff --git a/cinder/volume/drivers/emc/emc_vmax_common.py b/cinder/volume/drivers/emc/emc_vmax_common.py index e24e14fad65..a1feb800106 100644 --- a/cinder/volume/drivers/emc/emc_vmax_common.py +++ b/cinder/volume/drivers/emc/emc_vmax_common.py @@ -2068,7 +2068,7 @@ class EMCVMAXCommon(object): self.utils.find_replication_service_capabilities(self.conn, storageSystem)) is_clone_license = self.utils.is_clone_licensed( - self.conn, repServCapabilityInstanceName) + self.conn, repServCapabilityInstanceName, extraSpecs[ISV3]) if is_clone_license is False: exceptionMessage = (_( diff --git a/cinder/volume/drivers/emc/emc_vmax_fc.py b/cinder/volume/drivers/emc/emc_vmax_fc.py index dd2559a550e..29a3fce8d07 100644 --- a/cinder/volume/drivers/emc/emc_vmax_fc.py +++ b/cinder/volume/drivers/emc/emc_vmax_fc.py @@ -67,10 +67,12 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver): - Replacement of EMCGetTargetEndpoints api (bug #1512791) - VMAX3 snapvx improvements (bug #1522821) - Operations and timeout issues (bug #1538214) + 2.4.0 - EMC VMAX - locking SG for concurrent threads (bug #1554634) + - SnapVX licensing checks for VMAX3 (bug #1587017) """ - VERSION = "2.3.0" + VERSION = "2.4.0" def __init__(self, *args, **kwargs): diff --git a/cinder/volume/drivers/emc/emc_vmax_iscsi.py b/cinder/volume/drivers/emc/emc_vmax_iscsi.py index a6de60d7c4f..29538003a0a 100644 --- a/cinder/volume/drivers/emc/emc_vmax_iscsi.py +++ b/cinder/volume/drivers/emc/emc_vmax_iscsi.py @@ -73,10 +73,12 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver): - Replacement of EMCGetTargetEndpoints api (bug #1512791) - VMAX3 snapvx improvements (bug #1522821) - Operations and timeout issues (bug #1538214) + 2.4.0 - EMC VMAX - locking SG for concurrent threads (bug #1554634) + - SnapVX licensing checks for VMAX3 (bug #1587017) """ - VERSION = "2.3.0" + VERSION = "2.4.0" def __init__(self, *args, **kwargs): diff --git a/cinder/volume/drivers/emc/emc_vmax_utils.py b/cinder/volume/drivers/emc/emc_vmax_utils.py index 698351a1459..02ea6c9e440 100644 --- a/cinder/volume/drivers/emc/emc_vmax_utils.py +++ b/cinder/volume/drivers/emc/emc_vmax_utils.py @@ -41,6 +41,8 @@ except ImportError: STORAGEGROUPTYPE = 4 POSTGROUPTYPE = 3 CLONE_REPLICATION_TYPE = 10 +SYNC_SNAPSHOT_LOCAL = 6 +ASYNC_SNAPSHOT_LOCAL = 7 MAX_POOL_LENGTH = 16 MAX_FASTPOLICY_LENGTH = 14 @@ -1692,7 +1694,7 @@ class EMCVMAXUtils(object): return foundRepServCapability - def is_clone_licensed(self, conn, capabilityInstanceName): + def is_clone_licensed(self, conn, capabilityInstanceName, isV3): """Check if the clone feature is licensed and enabled. :param conn: the connection to the ecom server @@ -1709,10 +1711,19 @@ class EMCVMAXUtils(object): 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 + if isV3: + if (SYNC_SNAPSHOT_LOCAL in repTypes or + ASYNC_SNAPSHOT_LOCAL in repTypes): + # Snapshot is a supported replication type. + LOG.debug("Snapshot for VMAX3 is licensed and " + "enabled.") + return True + else: + if CLONE_REPLICATION_TYPE in repTypes: + # Clone is a supported replication type. + LOG.debug("Clone for VMAX2 is licensed and " + "enabled.") + return True return False def create_storage_hardwareId_instance_name(