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 26ce08ef905..2544b498a7d 100644
--- a/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py
+++ b/cinder/tests/unit/volume/drivers/emc/test_emc_vmax.py
@@ -13,6 +13,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import ast
import os
import shutil
import sys
@@ -31,6 +32,7 @@ from cinder.objects import consistencygroup
from cinder.objects import fields
from cinder import test
from cinder.tests.unit import utils
+from cinder import utils as cinder_utils
from cinder.volume import configuration as conf
from cinder.volume.drivers.emc import emc_vmax_common
@@ -323,6 +325,8 @@ class EMCVMAXCommonData(object):
fake_host = 'HostX@Backend#gold+1234567891011'
fake_host_v3 = 'HostX@Backend#Bronze+SRP_1+1234567891011'
fake_host_2_v3 = 'HostY@Backend#SRP_1+1234567891011'
+ fake_host_3_v3 = 'HostX@Backend#Bronze+DSS+SRP_1+1234567891011'
+ fake_host_4_v3 = 'HostX@Backend#Silver+None+SRP_1+1234567891011'
unit_creationclass = 'CIM_ProtocolControllerForUnit'
storage_type = 'gold'
@@ -414,6 +418,24 @@ class EMCVMAXCommonData(object):
'BlockSize': block_size
}
+ test_volume_v4 = {'name': 'vol1',
+ 'size': 1,
+ 'volume_name': 'vol1',
+ 'id': '1',
+ 'device_id': '1',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'vol1',
+ 'display_description': 'test volume',
+ 'volume_type_id': 'abc',
+ 'provider_location': six.text_type(provider_location),
+ 'status': 'available',
+ 'host': fake_host_3_v3,
+ 'NumberOfBlocks': 100,
+ 'BlockSize': block_size,
+ 'pool_name': 'Bronze+DSS+SRP_1+1234567891011'
+ }
+
test_volume_CG = {'name': 'volInCG',
'consistencygroup_id': 'abc',
'size': 1,
@@ -497,6 +519,22 @@ class EMCVMAXCommonData(object):
six.text_type(provider_location3),
'display_description': 'snapshot source volume'}
+ test_source_volume_1_v3 = {'size': 1,
+ 'volume_type_id': 'sourceid',
+ 'display_name': 'sourceVolume',
+ 'name': 'sourceVolume',
+ 'id': 'sourceVolume',
+ 'device_id': '10',
+ 'volume_name': 'vmax-154326',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'host': fake_host_4_v3,
+ 'NumberOfBlocks': 100,
+ 'BlockSize': block_size,
+ 'provider_location':
+ six.text_type(provider_location),
+ 'display_description': 'snapshot source volume'}
+
test_CG = consistencygroup.ConsistencyGroup(
context=None, name='myCG1', id='12345abcde',
volume_type_id='abc', status=fields.ConsistencyGroupStatus.AVAILABLE)
@@ -518,6 +556,13 @@ class EMCVMAXCommonData(object):
'volume': test_source_volume_v3,
'provider_location': six.text_type(provider_location)
}
+ test_snapshot_1_v3 = {'name': 'mySnap',
+ 'id': '1',
+ 'status': 'available',
+ 'host': fake_host_4_v3,
+ 'volume': test_source_volume_1_v3,
+ 'provider_location': six.text_type(provider_location)
+ }
test_CG_snapshot = {'name': 'testSnap',
'id': '12345abcde',
'consistencygroup_id': '123456789',
@@ -533,6 +578,8 @@ class EMCVMAXCommonData(object):
'host': 'fake_host'}
test_host_v3 = {'capabilities': location_info_v3,
'host': fake_host_2_v3}
+ test_host_1_v3 = {'capabilities': location_info_v3,
+ 'host': fake_host_4_v3}
initiatorNames = ["123456789012345", "123456789054321"]
storagegroups = [{'CreationClassName': storagegroup_creationclass,
'ElementName': storagegroupname},
@@ -558,6 +605,7 @@ class EMCVMAXCommonData(object):
'storagetype:workload': u'DSS',
'storagetype:slo': u'Bronze',
'storagetype:array': u'1234567891011',
+ 'MultiPoolSupport': False,
'isV3': True,
'portgroupname': u'OS-portgroup-PG'}
extra_specs_no_slo = {'storagetype:pool': 'SRP_1',
@@ -567,9 +615,20 @@ class EMCVMAXCommonData(object):
'storagetype:array': '1234567891011',
'isV3': True,
'portgroupname': 'OS-portgroup-PG'}
+
+ multi_pool_extra_specs = {'storagetype:pool': u'SRP_1',
+ 'volume_backend_name': 'MULTI_POOL_BE',
+ 'storagetype:workload': u'DSS',
+ 'storagetype:slo': u'Bronze',
+ 'storagetype:array': u'1234567891011',
+ 'isV3': True,
+ 'portgroupname': u'OS-portgroup-PG',
+ 'pool_name': u'Bronze+DSS+SRP_1+1234567891011'}
+
remainingSLOCapacity = '123456789'
SYNCHRONIZED = 4
UNSYNCHRONIZED = 3
+ multiPoolSupportEnabled = True
class FakeLookupService(object):
@@ -899,6 +958,8 @@ class FakeEcomConnection(object):
result = None
if ResultClass == 'CIM_ProtocolControllerForUnit':
result = self._ref_unitnames2()
+ elif ResultClass == 'SE_StorageSynchronized_SV_SV':
+ result = self._enum_storageSyncSvSv()
else:
result = self._default_ref(objectpath)
return result
@@ -1831,6 +1892,10 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
instancename.fake_getinstancename)
self.mock_object(emc_vmax_utils.EMCVMAXUtils, 'isArrayV3',
self.fake_is_v3)
+ self.mock_object(emc_vmax_utils.EMCVMAXUtils, '_is_sync_complete',
+ return_value=True)
+ self.mock_object(cinder_utils, 'get_bool_param',
+ return_value=False)
driver = emc_vmax_iscsi.EMCVMAXISCSIDriver(configuration=configuration)
driver.db = FakeDB()
self.driver = driver
@@ -3071,6 +3136,10 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
if bExists:
os.remove(file_name)
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'override_ratio',
+ return_value=2.0)
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'isArrayV3',
@@ -3091,8 +3160,18 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
mock_storage_system,
mock_is_fast_enabled,
mock_capacity,
- mock_is_v3):
+ mock_is_v3,
+ mock_or):
+ self.driver.common.pool_info['arrays_info'] = (
+ [{'EcomServerIp': '1.1.1.1',
+ 'EcomServerPort': '5989',
+ 'EcomUserName': 'name',
+ 'EcomPassword': 'password',
+ 'SerialNumber': '1234567890',
+ 'PoolName': 'v2_pool',
+ 'FastPolicy': 'gold'}])
self.driver.get_volume_stats(True)
+ self.driver.common.pool_info['arrays_info'] = []
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
@@ -3400,7 +3479,11 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
mock.Mock(return_value=volumeDict))
self.driver.create_snapshot(self.data.test_snapshot)
- def test_create_snapshot_no_fast_failed(self):
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_validate_pool',
+ return_value=('Bogus_Pool'))
+ def test_create_snapshot_no_fast_failed(self, mock_pool):
self.data.test_volume['volume_name'] = "vmax-1234567"
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_snapshot,
@@ -3531,20 +3614,6 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
self.driver.migrate_volume(self.data.test_ctxt, self.data.test_volume,
self.data.test_host)
- @mock.patch.object(
- emc_vmax_utils.EMCVMAXUtils,
- 'parse_pool_instance_id',
- return_value=('silver', 'SYMMETRIX+000195900551'))
- @mock.patch.object(
- volume_types,
- 'get_volume_type_extra_specs',
- return_value={'volume_backend_name': 'ISCSINoFAST'})
- def test_retype_volume_no_fast_success(
- self, _mock_volume_type, mock_values):
- self.driver.retype(
- self.data.test_ctxt, self.data.test_volume, self.data.new_type,
- self.data.diff, self.data.test_host)
-
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_get_pool_and_storage_system',
@@ -3942,7 +4011,18 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
emc_vmax_common.EMCVMAXCommon,
'_update_pool_stats',
return_value={1, 2, 3, 4, 5})
- def test_ssl_support(self, pool_stats):
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'override_ratio',
+ return_value=1.0)
+ def test_ssl_support(self, mock_ratio, pool_stats):
+ self.driver.common.pool_info['arrays_info'] = (
+ [{'EcomServerIp': '1.1.1.1',
+ 'EcomServerPort': '5989',
+ 'EcomUserName': 'name',
+ 'EcomPassword': 'password',
+ 'SerialNumber': '1234567890',
+ 'PoolName': 'v2_pool'}])
self.driver.common.update_volume_stats()
self.assertTrue(self.driver.common.ecomUseSSL)
@@ -3977,6 +4057,8 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
instancename.fake_getinstancename)
self.mock_object(emc_vmax_utils.EMCVMAXUtils, 'isArrayV3',
self.fake_is_v3)
+ self.mock_object(cinder_utils, 'get_bool_param',
+ return_value=False)
driver = emc_vmax_iscsi.EMCVMAXISCSIDriver(configuration=configuration)
driver.db = FakeDB()
self.driver = driver
@@ -4054,6 +4136,10 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
def fake_is_v3(self, conn, serialNumber):
return False
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'override_ratio',
+ return_value=2.0)
@mock.patch.object(
emc_vmax_fast.EMCVMAXFast,
'get_capacities_associated_to_policy',
@@ -4079,8 +4165,18 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
mock_is_fast_enabled,
mock_get_policy,
mock_pool_capacities,
- mock_capacities_associated_to_policy):
+ mock_capacities_associated_to_policy,
+ mock_or):
+ self.driver.common.pool_info['arrays_info'] = (
+ [{'EcomServerIp': '1.1.1.1',
+ 'EcomServerPort': '5989',
+ 'EcomUserName': 'name',
+ 'EcomPassword': 'password',
+ 'SerialNumber': '1234567890',
+ 'PoolName': 'v2_pool',
+ 'FastPolicy': 'gold'}])
self.driver.get_volume_stats(True)
+ self.driver.common.pool_info['arrays_info'] = []
@mock.patch.object(
emc_vmax_fast.EMCVMAXFast,
@@ -4333,7 +4429,11 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
mock.Mock(return_value=True))
self.driver.create_snapshot(self.data.test_snapshot)
- def test_create_snapshot_fast_failed(self):
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_validate_pool',
+ return_value=('Bogus_Pool'))
+ def test_create_snapshot_fast_failed(self, mock_pool):
self.data.test_volume['volume_name'] = "vmax-1234567"
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_snapshot,
@@ -4618,7 +4718,10 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
instancename.fake_getinstancename)
self.mock_object(emc_vmax_utils.EMCVMAXUtils, 'isArrayV3',
self.fake_is_v3)
-
+ self.mock_object(emc_vmax_utils.EMCVMAXUtils, '_is_sync_complete',
+ return_value=True)
+ self.mock_object(cinder_utils, 'get_bool_param',
+ return_value=False)
driver = emc_vmax_fc.EMCVMAXFCDriver(configuration=configuration)
driver.db = FakeDB()
driver.common.conn = FakeEcomConnection()
@@ -4690,6 +4793,10 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
def fake_is_v3(self, conn, serialNumber):
return False
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'override_ratio',
+ return_value=2.0)
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_pool_capacities',
@@ -4705,7 +4812,8 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
def test_get_volume_stats_no_fast(self,
mock_storage_system,
mock_is_fast_enabled,
- mock_capacity):
+ mock_capacity,
+ mock_or):
self.driver.get_volume_stats(True)
@mock.patch.object(
@@ -4918,20 +5026,6 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
self.driver.migrate_volume(self.data.test_ctxt, self.data.test_volume,
self.data.test_host)
- @mock.patch.object(
- emc_vmax_utils.EMCVMAXUtils,
- 'parse_pool_instance_id',
- return_value=('silver', 'SYMMETRIX+000195900551'))
- @mock.patch.object(
- volume_types,
- 'get_volume_type_extra_specs',
- return_value={'volume_backend_name': 'FCNoFAST'})
- def test_retype_volume_no_fast_success(
- self, _mock_volume_type, mock_values):
- self.driver.retype(
- self.data.test_ctxt, self.data.test_volume, self.data.new_type,
- self.data.diff, self.data.test_host)
-
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_get_pool_and_storage_system',
@@ -5178,6 +5272,10 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
instancename.fake_getinstancename)
self.mock_object(emc_vmax_utils.EMCVMAXUtils, 'isArrayV3',
self.fake_is_v3)
+ self.mock_object(emc_vmax_utils.EMCVMAXUtils, '_is_sync_complete',
+ return_value=True)
+ self.mock_object(cinder_utils, 'get_bool_param',
+ return_value=False)
driver = emc_vmax_fc.EMCVMAXFCDriver(configuration=configuration)
driver.db = FakeDB()
driver.common.conn = FakeEcomConnection()
@@ -5255,6 +5353,10 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
def fake_is_v3(self, conn, serialNumber):
return False
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'override_ratio',
+ return_value=2.0)
@mock.patch.object(
emc_vmax_fast.EMCVMAXFast,
'get_capacities_associated_to_policy',
@@ -5280,7 +5382,8 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
mock_is_fast_enabled,
mock_get_policy,
mock_pool_capacities,
- mock_capacities_associated_to_policy):
+ mock_capacities_associated_to_policy,
+ mock_or):
self.driver.get_volume_stats(True)
@mock.patch.object(
@@ -5683,24 +5786,6 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
self.driver.migrate_volume(self.data.test_ctxt, self.data.test_volume,
self.data.test_host)
- @mock.patch.object(
- emc_vmax_masking.EMCVMAXMasking,
- '_wrap_get_storage_group_from_volume',
- return_value=None)
- @mock.patch.object(
- emc_vmax_utils.EMCVMAXUtils,
- 'parse_pool_instance_id',
- return_value=('silver', 'SYMMETRIX+000195900551'))
- @mock.patch.object(
- volume_types,
- 'get_volume_type_extra_specs',
- return_value={'volume_backend_name': 'FCFAST'})
- def test_retype_volume_fast_success(
- self, _mock_volume_type, mock_values, mock_wrap):
- self.driver.retype(
- self.data.test_ctxt, self.data.test_volume, self.data.new_type,
- self.data.diff, self.data.test_host)
-
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_get_pool_and_storage_system',
@@ -5785,13 +5870,15 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
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))
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'is_clone_licensed',
+ return_value=False)
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'find_replication_service_capabilities',
+ return_value={'InstanceID': 'SYMMETRIX+1385450'})
+ def test_create_clone_without_license(self, mock_service, mock_license):
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_cloned_volume,
self.data.test_volume,
@@ -5831,23 +5918,24 @@ class EMCV3DriverTestCase(test.TestCase):
self.data = EMCVMAXCommonData()
self.data.storage_system = 'SYMMETRIX-+-000197200056'
- self.flags(rpc_backend='oslo_messaging._drivers.impl_fake')
self.tempdir = tempfile.mkdtemp()
super(EMCV3DriverTestCase, self).setUp()
self.config_file_path = None
self.create_fake_config_file_v3()
self.addCleanup(self._cleanup)
+ self.flags(rpc_backend='oslo_messaging._drivers.impl_fake')
self.set_configuration()
def set_configuration(self):
configuration = mock.Mock()
configuration.cinder_emc_config_file = self.config_file_path
- configuration.safe_get.return_value = 3
configuration.config_group = 'V3'
self.mock_object(emc_vmax_common.EMCVMAXCommon, '_get_ecom_connection',
self.fake_ecom_connection)
+ self.mock_object(cinder_utils, 'get_bool_param',
+ return_value=False)
instancename = FakeCIMInstanceName()
self.mock_object(emc_vmax_utils.EMCVMAXUtils, 'get_instance_name',
instancename.fake_getinstancename)
@@ -5936,6 +6024,9 @@ class EMCV3DriverTestCase(test.TestCase):
def fake_is_v3(self, conn, serialNumber):
return True
+ def fake_gather_info(self):
+ return
+
def default_extraspec(self):
return {'storagetype:pool': 'SRP_1',
'volume_backend_name': 'V3_BE',
@@ -6024,13 +6115,19 @@ class EMCV3DriverTestCase(test.TestCase):
storagegroup, storagegroup['ElementName'], vol,
vol['name'], extraSpecs)
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'override_ratio',
+ return_value=2.0)
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'find_storageSystem',
return_value={'Name': EMCVMAXCommonData.storage_system_v3})
def test_get_volume_stats_v3(
- self, mock_storage_system):
+ self, mock_storage_system, mock_or):
+ self.driver.common.pool_info['reserved_percentage'] = 5
self.driver.get_volume_stats(True)
+ self.driver.common.pool_info['reserved_percentage'] = 0
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
@@ -6669,497 +6766,398 @@ class EMCV3DriverTestCase(test.TestCase):
shutil.rmtree(self.tempdir)
-class EMCV2MultiPoolDriverTestCase(test.TestCase):
-
+class EMCV3MultiPoolDriverTestCase(test.TestCase):
def setUp(self):
self.data = EMCVMAXCommonData()
- self.vol_v2 = self.data.test_volume_v2
- self.vol_v2['provider_location'] = (
- six.text_type(self.data.provider_location_multi_pool))
- self.tempdir = tempfile.mkdtemp()
- super(EMCV2MultiPoolDriverTestCase, self).setUp()
- self.config_file_path = None
- self.create_fake_config_file_multi_pool()
- self.addCleanup(self._cleanup)
-
- configuration = mock.Mock()
- configuration.safe_get.return_value = 'MULTI_POOL'
- configuration.cinder_emc_config_file = self.config_file_path
- configuration.config_group = 'MULTI_POOL'
-
- self.mock_object(emc_vmax_common.EMCVMAXCommon, '_get_ecom_connection',
- self.fake_ecom_connection)
- instancename = FakeCIMInstanceName()
- self.mock_object(emc_vmax_utils.EMCVMAXUtils, 'get_instance_name',
- instancename.fake_getinstancename)
- self.mock_object(emc_vmax_utils.EMCVMAXUtils, 'isArrayV3',
- self.fake_is_v3)
- emc_vmax_utils.EMCVMAXUtils._is_sync_complete = mock.Mock(
- return_value=True)
- driver = emc_vmax_iscsi.EMCVMAXISCSIDriver(configuration=configuration)
- driver.db = FakeDB()
- self.driver = driver
- self.driver.utils = emc_vmax_utils.EMCVMAXUtils(object)
-
- def create_fake_config_file_multi_pool(self):
- doc = minidom.Document()
- emc = doc.createElement("EMC")
- doc.appendChild(emc)
-
- eComServers = doc.createElement("EcomServers")
- emc.appendChild(eComServers)
-
- eComServer = doc.createElement("EcomServer")
- eComServers.appendChild(eComServer)
-
- ecomserverip = doc.createElement("EcomServerIp")
- eComServer.appendChild(ecomserverip)
- ecomserveriptext = doc.createTextNode("1.1.1.1")
- ecomserverip.appendChild(ecomserveriptext)
-
- ecomserverport = doc.createElement("EcomServerPort")
- eComServer.appendChild(ecomserverport)
- ecomserverporttext = doc.createTextNode("10")
- ecomserverport.appendChild(ecomserverporttext)
-
- ecomusername = doc.createElement("EcomUserName")
- eComServer.appendChild(ecomusername)
- ecomusernametext = doc.createTextNode("user")
- ecomusername.appendChild(ecomusernametext)
-
- ecompassword = doc.createElement("EcomPassword")
- eComServer.appendChild(ecompassword)
- ecompasswordtext = doc.createTextNode("pass")
- ecompassword.appendChild(ecompasswordtext)
-
- arrays = doc.createElement("Arrays")
- eComServer.appendChild(arrays)
-
- array = doc.createElement("Array")
- arrays.appendChild(array)
-
- serialNo = doc.createElement("SerialNumber")
- array.appendChild(serialNo)
- serialNoText = doc.createTextNode("1234567891011")
- serialNo.appendChild(serialNoText)
-
- portgroups = doc.createElement("PortGroups")
- array.appendChild(portgroups)
-
- portgroup = doc.createElement("PortGroup")
- portgroups.appendChild(portgroup)
- portgrouptext = doc.createTextNode(self.data.port_group)
- portgroup.appendChild(portgrouptext)
-
- pools = doc.createElement("Pools")
- array.appendChild(pools)
-
- pool = doc.createElement("Pool")
- pools.appendChild(pool)
- poolName = doc.createElement("PoolName")
- pool.appendChild(poolName)
- poolNameText = doc.createTextNode("gold")
- poolName.appendChild(poolNameText)
-
- pool2 = doc.createElement("Pool")
- pools.appendChild(pool2)
- pool2Name = doc.createElement("PoolName")
- pool2.appendChild(pool2Name)
- pool2NameText = doc.createTextNode("SATA_BRONZE1")
- pool2Name.appendChild(pool2NameText)
- pool2FastPolicy = doc.createElement("FastPolicy")
- pool2.appendChild(pool2FastPolicy)
- pool2FastPolicyText = doc.createTextNode("BRONZE1")
- pool2FastPolicy.appendChild(pool2FastPolicyText)
-
- filename = 'cinder_emc_config_V2_MULTI_POOL.xml'
- self.config_file_path = self.tempdir + '/' + filename
-
- f = open(self.config_file_path, 'w')
- doc.writexml(f)
- f.close()
-
- def fake_ecom_connection(self):
- self.conn = FakeEcomConnection()
- return self.conn
-
- def fake_is_v3(self, conn, serialNumber):
- return False
-
- def default_extraspec(self):
- return {'storagetype:pool': u'gold',
- 'volume_backend_name': 'MULTI_POOL_BE',
- 'storagetype:fastpolicy': None,
- 'storagetype:compositetype': u'concatenated',
- 'storagetype:membercount': 1,
- 'storagetype:array': u'1234567891011',
- 'isV3': False,
- 'portgroupname': u'OS-portgroup-PG'}
-
- def test_validate_pool(self):
- v2_valid_pool = self.data.test_volume_v2.copy()
- # Pool aware scheduler enabled
- v2_valid_pool['host'] = self.data.fake_host
- pool = self.driver.common._validate_pool(v2_valid_pool)
- self.assertEqual('gold+1234567891011', pool)
-
- # Cannot get the pool from the host
- v2_valid_pool['host'] = 'HostX@Backend'
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.common._validate_pool,
- v2_valid_pool)
-
- # Legacy test. Provider Location does not have the version
- v2_valid_pool['host'] = self.data.fake_host
- v2_valid_pool['provider_location'] = self.data.provider_location
- pool = self.driver.common._validate_pool(v2_valid_pool)
- self.assertIsNone(pool)
-
- def test_array_info_multi_pool(self):
-
- arrayInfo = self.driver.utils.parse_file_to_get_array_map(
- self.config_file_path)
- self.assertEqual(2, len(arrayInfo))
- for arrayInfoRec in arrayInfo:
- self.assertEqual(
- '1234567891011', arrayInfoRec['SerialNumber'])
- self.assertIn(self.data.port_group, arrayInfoRec['PortGroup'])
- self.assertTrue(
- self.data.poolname in arrayInfoRec['PoolName'] or
- 'SATA_BRONZE1' in arrayInfoRec['PoolName'])
-
- @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': 'MULTI_POOL_BE'})
- def test_create_volume_multi_pool_success(
- self, _mock_volume_type, mock_storage_system):
- self.vol_v2['provider_location'] = None
- self.driver.common._initial_setup = mock.Mock(
- return_value=self.default_extraspec())
- self.driver.create_volume(self.vol_v2)
-
- @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': 'MULTI_POOL_BE'})
- def test_delete_volume_multi_pool_success(
- self, _mock_volume_type, mock_storage_system):
- self.driver.common._initial_setup = mock.Mock(
- return_value=self.default_extraspec())
- self.driver.delete_volume(self.vol_v2)
-
- @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': 'MULTI_POOL_BE'})
- def test_create_volume_in_CG_multi_pool_success(
- self, _mock_volume_type, mock_storage_system):
- self.data.test_volume_CG['provider_location'] = None
- self.driver.common._initial_setup = mock.Mock(
- return_value=self.default_extraspec())
- self.driver.create_volume(self.data.test_volume_CG)
-
- @mock.patch.object(
- volume_types,
- 'get_volume_type_extra_specs',
- return_value={'volume_backend_name': 'MULTI_POOL_BE'})
- def test_retype_volume_multi_pool_success(
- self, _mock_volume_type):
- self.driver.common._initial_setup = mock.Mock(
- return_value=self.default_extraspec())
- self.driver.retype(
- self.data.test_ctxt, self.vol_v2, self.data.new_type,
- self.data.diff, self.data.test_host)
-
- @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': 'MULTI_POOL_BE'})
- # There is only one unique array in the conf file
- def test_create_CG_multi_pool_success(
- self, _mock_volume_type, _mock_storage_system):
- self.driver.create_consistencygroup(
- self.data.test_ctxt, self.data.test_CG)
-
- @mock.patch.object(
- FakeDB,
- 'volume_get_all_by_group',
- return_value=None)
- @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': 'MULTI_POOL_BE'})
- def test_delete_CG_no_volumes_multi_pool_success(
- self, _mock_volume_type, _mock_storage_system,
- _mock_db_volumes):
- self.driver.delete_consistencygroup(
- self.data.test_ctxt, self.data.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': 'MULTI_POOL_BE'})
- def test_delete_CG_with_volumes_multi_pool_success(
- self, _mock_volume_type, _mock_storage_system):
- self.driver.delete_consistencygroup(
- self.data.test_ctxt, self.data.test_CG, [])
-
- def _cleanup(self):
- bExists = os.path.exists(self.config_file_path)
- if bExists:
- os.remove(self.config_file_path)
- shutil.rmtree(self.tempdir)
-
-
-class EMCV3MultiSloDriverTestCase(test.TestCase):
-
- def setUp(self):
- self.data = EMCVMAXCommonData()
- self.vol_v3 = self.data.test_volume_v3
+ self.vol_v3 = self.data.test_volume_v4
self.vol_v3['provider_location'] = (
six.text_type(self.data.provider_location_multi_pool))
- self.tempdir = tempfile.mkdtemp()
- super(EMCV3MultiSloDriverTestCase, self).setUp()
- self.config_file_path = None
- self.create_fake_config_file_multi_slo_v3()
- self.addCleanup(self._cleanup)
+ super(EMCV3MultiPoolDriverTestCase, self).setUp()
self.set_configuration()
def set_configuration(self):
configuration = mock.Mock()
- configuration.safe_get.return_value = 'MULTI_SLO_V3'
- configuration.cinder_emc_config_file = self.config_file_path
- configuration.config_group = 'MULTI_SLO_V3'
-
+ configuration.safe_get.return_value = 'MULTI_POOL_V3'
+ configuration.config_group = 'MULTI_POOL_V3'
self.mock_object(emc_vmax_common.EMCVMAXCommon, '_get_ecom_connection',
self.fake_ecom_connection)
+ self.mock_object(emc_vmax_common.EMCVMAXCommon, '_gather_info',
+ self.fake_gather_info)
instancename = FakeCIMInstanceName()
self.mock_object(emc_vmax_utils.EMCVMAXUtils, 'get_instance_name',
instancename.fake_getinstancename)
self.mock_object(emc_vmax_utils.EMCVMAXUtils, 'isArrayV3',
- self.fake_is_v3)
-
+ return_value=True)
+ self.mock_object(emc_vmax_utils.EMCVMAXUtils, '_is_sync_complete',
+ return_value=True)
+ self.mock_object(emc_vmax_common.EMCVMAXCommon,
+ '_get_multi_pool_support_enabled_flag',
+ return_value=True)
+ volume_types.get_volume_type_extra_specs = mock.Mock(
+ return_value={'volume_backend_name': 'MULTI_POOL_BE',
+ 'pool_name': 'Bronze+DSS+SRP_1+1234567891011'})
driver = emc_vmax_fc.EMCVMAXFCDriver(configuration=configuration)
driver.db = FakeDB()
self.driver = driver
self.driver.utils = emc_vmax_utils.EMCVMAXUtils(object)
- def create_fake_config_file_multi_slo_v3(self):
+ def create_fake_config_file_multi_pool_v3(self, tempdir):
doc = minidom.Document()
emc = doc.createElement("EMC")
doc.appendChild(emc)
- eComServers = doc.createElement("EcomServers")
- emc.appendChild(eComServers)
-
- eComServer = doc.createElement("EcomServer")
- eComServers.appendChild(eComServer)
-
ecomserverip = doc.createElement("EcomServerIp")
- eComServer.appendChild(ecomserverip)
ecomserveriptext = doc.createTextNode("1.1.1.1")
+ emc.appendChild(ecomserverip)
ecomserverip.appendChild(ecomserveriptext)
ecomserverport = doc.createElement("EcomServerPort")
- eComServer.appendChild(ecomserverport)
ecomserverporttext = doc.createTextNode("10")
+ emc.appendChild(ecomserverport)
ecomserverport.appendChild(ecomserverporttext)
ecomusername = doc.createElement("EcomUserName")
- eComServer.appendChild(ecomusername)
ecomusernametext = doc.createTextNode("user")
+ emc.appendChild(ecomusername)
ecomusername.appendChild(ecomusernametext)
ecompassword = doc.createElement("EcomPassword")
- eComServer.appendChild(ecompassword)
ecompasswordtext = doc.createTextNode("pass")
+ emc.appendChild(ecompassword)
ecompassword.appendChild(ecompasswordtext)
- arrays = doc.createElement("Arrays")
- eComServer.appendChild(arrays)
-
- array = doc.createElement("Array")
- arrays.appendChild(array)
-
- serialNo = doc.createElement("SerialNumber")
- array.appendChild(serialNo)
- serialNoText = doc.createTextNode("1234567891011")
- serialNo.appendChild(serialNoText)
-
- portgroups = doc.createElement("PortGroups")
- array.appendChild(portgroups)
-
portgroup = doc.createElement("PortGroup")
- portgroups.appendChild(portgroup)
portgrouptext = doc.createTextNode(self.data.port_group)
portgroup.appendChild(portgrouptext)
- vpools = doc.createElement("Pools")
- array.appendChild(vpools)
- vpool = doc.createElement("Pool")
- vpools.appendChild(vpool)
- poolName = doc.createElement("PoolName")
- vpool.appendChild(poolName)
- poolNameText = doc.createTextNode("SRP_1")
- poolName.appendChild(poolNameText)
- poolslo = doc.createElement("ServiceLevel")
- vpool.appendChild(poolslo)
- poolsloText = doc.createTextNode("Bronze")
- poolslo.appendChild(poolsloText)
- poolworkload = doc.createElement("Workload")
- vpool.appendChild(poolworkload)
- poolworkloadText = doc.createTextNode("DSS")
- poolworkload.appendChild(poolworkloadText)
+ pool = doc.createElement("Pool")
+ pooltext = doc.createTextNode("SRP_1")
+ emc.appendChild(pool)
+ pool.appendChild(pooltext)
- vpool2 = doc.createElement("Pool")
- vpools.appendChild(vpool2)
- pool2Name = doc.createElement("PoolName")
- vpool2.appendChild(pool2Name)
- pool2NameText = doc.createTextNode("SRP_1")
- pool2Name.appendChild(pool2NameText)
- pool2slo = doc.createElement("ServiceLevel")
- vpool2.appendChild(pool2slo)
- pool2sloText = doc.createTextNode("Silver")
- pool2slo.appendChild(pool2sloText)
- pool2workload = doc.createElement("Workload")
- vpool.appendChild(pool2workload)
- pool2workloadText = doc.createTextNode("OLTP")
- pool2workload.appendChild(pool2workloadText)
+ array = doc.createElement("Array")
+ arraytext = doc.createTextNode("1234567891011")
+ emc.appendChild(array)
+ array.appendChild(arraytext)
- filename = 'cinder_emc_config_MULTI_SLO_V3.xml'
- self.config_file_path = self.tempdir + '/' + filename
+ portgroups = doc.createElement("PortGroups")
+ portgroups.appendChild(portgroup)
+ emc.appendChild(portgroups)
- f = open(self.config_file_path, 'w')
+ timeout = doc.createElement("Timeout")
+ timeouttext = doc.createTextNode("0")
+ emc.appendChild(timeout)
+ timeout.appendChild(timeouttext)
+
+ filename = 'cinder_emc_config_V3.xml'
+
+ config_file_path = tempdir + '/' + filename
+
+ f = open(config_file_path, 'w')
doc.writexml(f)
f.close()
+ return config_file_path
+
+ def create_fake_config_file_legacy_v3(self, tempdir):
+
+ doc = minidom.Document()
+ emc = doc.createElement("EMC")
+ doc.appendChild(emc)
+
+ ecomserverip = doc.createElement("EcomServerIp")
+ ecomserveriptext = doc.createTextNode("1.1.1.1")
+ emc.appendChild(ecomserverip)
+ ecomserverip.appendChild(ecomserveriptext)
+
+ ecomserverport = doc.createElement("EcomServerPort")
+ ecomserverporttext = doc.createTextNode("10")
+ emc.appendChild(ecomserverport)
+ ecomserverport.appendChild(ecomserverporttext)
+
+ ecomusername = doc.createElement("EcomUserName")
+ ecomusernametext = doc.createTextNode("user")
+ emc.appendChild(ecomusername)
+ ecomusername.appendChild(ecomusernametext)
+
+ ecompassword = doc.createElement("EcomPassword")
+ ecompasswordtext = doc.createTextNode("pass")
+ emc.appendChild(ecompassword)
+ ecompassword.appendChild(ecompasswordtext)
+
+ portgroup = doc.createElement("PortGroup")
+ portgrouptext = doc.createTextNode(self.data.port_group)
+ portgroup.appendChild(portgrouptext)
+
+ pool = doc.createElement("Pool")
+ pooltext = doc.createTextNode("SRP_1")
+ emc.appendChild(pool)
+ pool.appendChild(pooltext)
+
+ array = doc.createElement("Array")
+ arraytext = doc.createTextNode("1234567891011")
+ emc.appendChild(array)
+ array.appendChild(arraytext)
+
+ slo = doc.createElement("ServiceLevel")
+ slotext = doc.createTextNode("Silver")
+ emc.appendChild(slo)
+ slo.appendChild(slotext)
+
+ workload = doc.createElement("Workload")
+ workloadtext = doc.createTextNode("OLTP")
+ emc.appendChild(workload)
+ workload.appendChild(workloadtext)
+
+ portgroups = doc.createElement("PortGroups")
+ portgroups.appendChild(portgroup)
+ emc.appendChild(portgroups)
+
+ timeout = doc.createElement("Timeout")
+ timeouttext = doc.createTextNode("0")
+ emc.appendChild(timeout)
+ timeout.appendChild(timeouttext)
+
+ filename = 'cinder_emc_config_V3.xml'
+
+ config_file_path = tempdir + '/' + filename
+
+ f = open(config_file_path, 'w')
+ doc.writexml(f)
+ f.close()
+ return config_file_path
def fake_ecom_connection(self):
self.conn = FakeEcomConnection()
return self.conn
- def fake_is_v3(self, conn, serialNumber):
- return True
+ def fake_gather_info(self):
+ return
- def default_extraspec(self):
- return {'storagetype:pool': u'SRP_1',
- 'volume_backend_name': 'MULTI_SLO_BE',
- 'storagetype:workload': u'DSS',
- 'storagetype:slo': u'Bronze',
- 'storagetype:array': u'1234567891011',
- 'isV3': True,
- 'portgroupname': u'OS-portgroup-PG'}
+ def default_array_info_list(self):
+ return [{'EcomServerIp': u'1.1.1.1',
+ 'EcomServerPort': 10,
+ 'EcomUserName': u'user',
+ 'EcomPassword': u'pass',
+ 'PoolName': u'SRP_1',
+ 'PortGroup': u'OS-portgroup-PG',
+ 'SerialNumber': 1234567891011,
+ 'SLO': u'Bronze',
+ 'Workload': u'DSS'}]
+
+ def multiple_array_info_list(self):
+ return [{'EcomServerIp': u'1.1.1.1',
+ 'EcomServerPort': 10,
+ 'EcomUserName': u'user',
+ 'EcomPassword': u'pass',
+ 'PoolName': u'SRP_1',
+ 'PortGroup': u'OS-portgroup-PG',
+ 'SerialNumber': 1234567891011,
+ 'SLO': u'Bronze',
+ 'Workload': u'DSS'},
+ {'EcomServerIp': u'1.1.1.1',
+ 'EcomServerPort': 10,
+ 'EcomUserName': u'user',
+ 'EcomPassword': u'pass',
+ 'PoolName': u'SRP_1',
+ 'PortGroup': u'OS-portgroup-PG',
+ 'SerialNumber': 1234567891011,
+ 'SLO': u'Silver',
+ 'Workload': u'OLTP'}]
+
+ def test_initial_setup(self):
+ tempdir = tempfile.mkdtemp()
+ config_file_path = self.create_fake_config_file_multi_pool_v3(tempdir)
+ with mock.patch.object(
+ self.driver.common, '_register_config_file_from_config_group',
+ return_value=config_file_path):
+ extraSpecs = self.driver.common._initial_setup(self.vol_v3)
+ self.assertEqual('SRP_1', extraSpecs['storagetype:pool'])
+ self.assertEqual('DSS', extraSpecs['storagetype:workload'])
+ self.assertEqual('Bronze', extraSpecs['storagetype:slo'])
+ self.assertEqual('1234567891011', extraSpecs['storagetype:array'])
+ self.assertEqual('OS-portgroup-PG', extraSpecs['portgroupname'])
+ self.assertTrue(extraSpecs['isV3'])
+ self.assertTrue(extraSpecs['MultiPoolSupport'])
+ self.assertEqual('Bronze+DSS+SRP_1+1234567891011',
+ extraSpecs['pool_name'])
+ self._cleanup(tempdir, config_file_path)
+
+ def test_initial_setup_with_legacy_file(self):
+ # Test with legacy config file and verify
+ # if the values for SLO and workload are used from
+ # the pool_name and not the config file
+ tempdir = tempfile.mkdtemp()
+ config_file_path = self.create_fake_config_file_legacy_v3(tempdir)
+ with mock.patch.object(
+ self.driver.common, '_register_config_file_from_config_group',
+ return_value=config_file_path):
+ extraSpecs = self.driver.common._initial_setup(self.vol_v3)
+ self.assertEqual('DSS', extraSpecs['storagetype:workload'])
+ self.assertEqual('Bronze', extraSpecs['storagetype:slo'])
+ self._cleanup(tempdir, config_file_path)
+
+ def test_initial_setup_invalid_volume(self):
+ # Test with volume which don't have pool_name
+ tempdir = tempfile.mkdtemp()
+ config_file_path = self.create_fake_config_file_multi_pool_v3(tempdir)
+ with mock.patch.object(
+ self.driver.common, '_register_config_file_from_config_group',
+ return_value=config_file_path):
+ invalid_vol_v3 = self.data.test_volume_v4.copy()
+ invalid_vol_v3.pop('host', None)
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.common._initial_setup,
+ invalid_vol_v3)
+ self._cleanup(tempdir, config_file_path)
def test_validate_pool(self):
- v3_valid_pool = self.data.test_volume_v3.copy()
+ v3_valid_pool = self.data.test_volume_v4.copy()
# Pool aware scheduler enabled
- v3_valid_pool['host'] = self.data.fake_host_v3
- pool = self.driver.common._validate_pool(v3_valid_pool)
- self.assertEqual('Bronze+SRP_1+1234567891011', pool)
+ v3_valid_pool['host'] = self.data.fake_host_3_v3
+ # Validate pool uses extraSpecs as a new argument
+ # Use default extraSpecs as the argument
+ pool = self.driver.common._validate_pool(
+ v3_valid_pool, self.data.multi_pool_extra_specs)
+ self.assertEqual('Bronze+DSS+SRP_1+1234567891011', pool)
+ def test_validate_pool_invalid_pool_name(self):
+ # Validate using older volume dictionary
+ # and check if a exception is raised if multi_pool_support
+ # is enabled and pool_name is not specified
+ extraSpecs = self.data.multi_pool_extra_specs
+ invalid_pool_name = extraSpecs.copy()
+ invalid_pool_name['pool_name'] = 'not_valid'
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.common._validate_pool,
+ self.data.test_volume_v4, invalid_pool_name)
+
+ def test_validate_pool_invalid_host(self):
# Cannot get the pool from the host
+ v3_valid_pool = self.data.test_volume_v4.copy()
v3_valid_pool['host'] = 'HostX@Backend'
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.common._validate_pool,
v3_valid_pool)
+
+ def test_validate_pool_legacy(self):
# Legacy test. Provider Location does not have the version
- v3_valid_pool['host'] = self.data.fake_host_v3
+ v3_valid_pool = self.data.test_volume_v4.copy()
+ v3_valid_pool['host'] = self.data.fake_host_3_v3
v3_valid_pool['provider_location'] = self.data.provider_location
pool = self.driver.common._validate_pool(v3_valid_pool)
self.assertIsNone(pool)
- def test_array_info_multi_slo(self):
-
- arrayInfo = self.driver.utils.parse_file_to_get_array_map(
- self.config_file_path)
- self.assertEqual(2, len(arrayInfo))
- for arrayInfoRec in arrayInfo:
- self.assertEqual(
- '1234567891011', arrayInfoRec['SerialNumber'])
- self.assertIn(self.data.port_group, arrayInfoRec['PortGroup'])
- self.assertIn('SRP_1', arrayInfoRec['PoolName'])
- self.assertTrue(
- 'Bronze' in arrayInfoRec['SLO'] or
- 'Silver' in arrayInfoRec['SLO'])
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'override_ratio',
+ return_value=2.0)
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'find_storageSystem',
+ return_value={'Name': EMCVMAXCommonData.storage_system_v3})
+ def test_get_volume_stats_v3(
+ self, mock_storage_system, mock_or):
+ self.driver.common.pool_info['reserved_percentage'] = 5
+ self.driver.get_volume_stats(True)
+ self.driver.common.pool_info['reserved_percentage'] = 0
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_initial_setup',
+ return_value=EMCVMAXCommonData.multi_pool_extra_specs)
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_get_or_create_storage_group_v3',
+ return_value=EMCVMAXCommonData.default_sg_instance_name)
@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': 'MULTI_SLO_BE'})
def test_create_volume_multi_slo_success(
- self, _mock_volume_type, mock_storage_system):
- self.vol_v3['host'] = self.data.fake_host_v3
+ self, mock_storage_system, mock_sg, mock_is):
+ self.vol_v3['host'] = self.data.fake_host_3_v3
self.vol_v3['provider_location'] = None
- self.driver.common._initial_setup = mock.Mock(
- return_value=self.default_extraspec())
- self.driver.common._get_or_create_storage_group_v3 = mock.Mock(
- return_value = self.data.default_sg_instance_name)
- self.driver.create_volume(self.vol_v3)
+ model_update = self.driver.create_volume(self.vol_v3)
+ # Verify if the device id is provided in the output
+ provider_location = model_update['provider_location']
+ provider_location = ast.literal_eval(provider_location)
+ keybindings = provider_location['keybindings']
+ device_id = keybindings['DeviceID']
+ self.assertEqual('1', device_id)
+ @mock.patch.object(
+ emc_vmax_masking.EMCVMAXMasking,
+ 'get_associated_masking_groups_from_device',
+ return_value=EMCVMAXCommonData.storagegroups)
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_initial_setup',
+ return_value=EMCVMAXCommonData.multi_pool_extra_specs)
@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': 'MULTI_SLO_BE'})
def test_delete_volume_multi_slo_success(
- self, _mock_volume_type, mock_storage_system):
- self.driver.common._initial_setup = mock.Mock(
- return_value=self.default_extraspec())
+ self, mock_storage_system, mock_is, mock_mv):
+ provider_location = (
+ {'classname': 'Symm_StorageVolume',
+ 'keybindings':
+ {'CreationClassName': 'Symm_StorageVolume',
+ 'SystemName': 'SYMMETRIX+000195900551',
+ 'DeviceID': '1',
+ 'SystemCreationClassName': 'Symm_StorageSystem'
+ }
+ })
+ volumeInstanceName = (
+ {'NumberOfBlocks': 100,
+ 'ElementName': '1',
+ 'Name': 'vol1',
+ 'BlockSize': 512,
+ 'provider_location': six.text_type(provider_location),
+ 'SystemName': 'SYMMETRIX+000195900551',
+ 'DeviceID': '1',
+ 'CreationClassName': 'Symm_StorageVolume',
+ 'Id': '1',
+ 'SystemCreationClassName': 'Symm_StorageSystem'})
self.driver.delete_volume(self.vol_v3)
+ masking = self.driver.common.masking
+ get_groups_from_device = (
+ masking.get_associated_masking_groups_from_device)
+ get_groups_from_device.assert_called_once_with(
+ self.conn, volumeInstanceName)
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_get_or_create_storage_group_v3',
+ return_value=EMCVMAXCommonData.default_sg_instance_name)
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_initial_setup',
+ return_value=EMCVMAXCommonData.multi_pool_extra_specs)
@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': 'MULTI_SLO_BE'})
def test_create_volume_in_CG_multi_slo_success(
- self, _mock_volume_type, mock_storage_system):
+ self, mock_storage_system, mock_is, mock_sg):
self.data.test_volume_CG_v3['provider_location'] = None
- self.driver.common._initial_setup = mock.Mock(
- return_value=self.default_extraspec())
- self.driver.common._get_or_create_storage_group_v3 = mock.Mock(
- return_value = self.data.default_sg_instance_name)
- self.driver.create_volume(self.data.test_volume_CG_v3)
+ model_update = self.driver.create_volume(self.data.test_volume_CG_v3)
+ # Verify if the device id is provided in the output
+ provider_location = model_update['provider_location']
+ provider_location = ast.literal_eval(provider_location)
+ keybindings = provider_location['keybindings']
+ device_id = keybindings['DeviceID']
+ self.assertEqual('1', device_id)
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_volume_element_name',
return_value='1')
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_initial_setup',
+ return_value=EMCVMAXCommonData.multi_pool_extra_specs)
@mock.patch.object(
emc_vmax_provision_v3.EMCVMAXProvisionV3,
'_find_new_storage_group',
@@ -7172,315 +7170,31 @@ class EMCV3MultiSloDriverTestCase(test.TestCase):
emc_vmax_utils.EMCVMAXUtils,
'_get_fast_settings_from_storage_group',
return_value='Gold+DSS_REP')
- @mock.patch.object(
- volume_types,
- 'get_volume_type_extra_specs',
- return_value={'volume_backend_name': 'MULTI_SLO_BE'})
def test_retype_volume_multi_slo_success(
- self, _mock_volume_type, mock_fast_settings,
- mock_storage_group, mock_found_SG, mock_element_name):
- self.driver.common._initial_setup = mock.Mock(
- return_value=self.default_extraspec())
+ self, mock_fast_settings,
+ mock_storage_group, mock_found_SG, mock_is, mock_element_name):
self.assertTrue(self.driver.retype(
- self.data.test_ctxt, self.data.test_volume_v3, self.data.new_type,
- self.data.diff, self.data.test_host_v3))
+ self.data.test_ctxt, self.data.test_volume_v4, self.data.new_type,
+ self.data.diff, self.data.test_host_1_v3))
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_initial_setup',
+ return_value=EMCVMAXCommonData.multi_pool_extra_specs)
@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': 'MULTI_SLO_BE'})
# There is only one unique array in the conf file
def test_create_CG_multi_slo_success(
- self, _mock_volume_type, _mock_storage_system):
- self.driver.common._initial_setup = mock.Mock(
- return_value=self.default_extraspec())
+ self, _mock_storage_system, mock_is):
self.driver.create_consistencygroup(
self.data.test_ctxt, self.data.test_CG)
- @mock.patch.object(
- FakeDB,
- 'volume_get_all_by_group',
- return_value=None)
@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': 'MULTI_SLO_BE'})
- def test_delete_CG_no_volumes_multi_slo_success(
- self, _mock_volume_type, _mock_storage_system,
- _mock_db_volumes):
- self.driver.delete_consistencygroup(
- self.data.test_ctxt, self.data.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': 'MULTI_SLO_BE'})
- def test_delete_CG_with_volumes_multi_slo_success(
- self, _mock_volume_type, _mock_storage_system):
- self.driver.delete_consistencygroup(
- self.data.test_ctxt, self.data.test_CG, [])
-
- def _cleanup(self):
- bExists = os.path.exists(self.config_file_path)
- if bExists:
- os.remove(self.config_file_path)
- shutil.rmtree(self.tempdir)
-
-
-class EMCV2MultiPoolDriverMultipleEcomsTestCase(test.TestCase):
-
- def setUp(self):
-
- self.data = EMCVMAXCommonData()
- self.vol_v2 = self.data.test_volume_v2
- self.vol_v2['provider_location'] = (
- six.text_type(self.data.provider_location_multi_pool))
-
- self.tempdir = tempfile.mkdtemp()
- super(EMCV2MultiPoolDriverMultipleEcomsTestCase, self).setUp()
- self.config_file_path = None
- self.create_fake_config_file_multi_ecom()
- self.addCleanup(self._cleanup)
-
- configuration = mock.Mock()
- configuration.cinder_emc_config_file = self.config_file_path
- configuration.safe_get.return_value = 'MULTI_ECOM'
- configuration.config_group = 'MULTI_ECOM'
-
- self.mock_object(emc_vmax_common.EMCVMAXCommon, '_get_ecom_connection',
- self.fake_ecom_connection)
- instancename = FakeCIMInstanceName()
- self.mock_object(emc_vmax_utils.EMCVMAXUtils, 'get_instance_name',
- instancename.fake_getinstancename)
- self.mock_object(emc_vmax_utils.EMCVMAXUtils, 'isArrayV3',
- self.fake_is_v3)
- driver = emc_vmax_fc.EMCVMAXFCDriver(configuration=configuration)
- driver.db = FakeDB()
- driver.common.conn = FakeEcomConnection()
- driver.zonemanager_lookup_service = FakeLookupService()
- self.driver = driver
- self.driver.utils = emc_vmax_utils.EMCVMAXUtils(object)
-
- def create_fake_config_file_multi_ecom(self):
- doc = minidom.Document()
- emc = doc.createElement("EMC")
- doc.appendChild(emc)
-
- eComServers = doc.createElement("EcomServers")
- emc.appendChild(eComServers)
-
- eComServer = doc.createElement("EcomServer")
- eComServers.appendChild(eComServer)
-
- ecomserverip = doc.createElement("EcomServerIp")
- eComServer.appendChild(ecomserverip)
- ecomserveriptext = doc.createTextNode("1.1.1.1")
- ecomserverip.appendChild(ecomserveriptext)
-
- ecomserverport = doc.createElement("EcomServerPort")
- eComServer.appendChild(ecomserverport)
- ecomserverporttext = doc.createTextNode("10")
- ecomserverport.appendChild(ecomserverporttext)
-
- ecomusername = doc.createElement("EcomUserName")
- eComServer.appendChild(ecomusername)
- ecomusernametext = doc.createTextNode("user")
- ecomusername.appendChild(ecomusernametext)
-
- ecompassword = doc.createElement("EcomPassword")
- eComServer.appendChild(ecompassword)
- ecompasswordtext = doc.createTextNode("pass")
- ecompassword.appendChild(ecompasswordtext)
-
- arrays = doc.createElement("Arrays")
- eComServer.appendChild(arrays)
-
- array = doc.createElement("Array")
- arrays.appendChild(array)
-
- serialNo = doc.createElement("SerialNumber")
- array.appendChild(serialNo)
- serialNoText = doc.createTextNode("1110987654321")
- serialNo.appendChild(serialNoText)
-
- portgroups = doc.createElement("PortGroups")
- array.appendChild(portgroups)
-
- portgroup = doc.createElement("PortGroup")
- portgroups.appendChild(portgroup)
- portgrouptext = doc.createTextNode(self.data.port_group)
- portgroup.appendChild(portgrouptext)
-
- pools = doc.createElement("Pools")
- array.appendChild(pools)
-
- pool = doc.createElement("Pool")
- pools.appendChild(pool)
- poolName = doc.createElement("PoolName")
- pool.appendChild(poolName)
- poolNameText = doc.createTextNode("gold")
- poolName.appendChild(poolNameText)
-
- pool2 = doc.createElement("Pool")
- pools.appendChild(pool2)
- pool2Name = doc.createElement("PoolName")
- pool2.appendChild(pool2Name)
- pool2NameText = doc.createTextNode("SATA_BRONZE1")
- pool2Name.appendChild(pool2NameText)
- pool2FastPolicy = doc.createElement("FastPolicy")
- pool2.appendChild(pool2FastPolicy)
- pool2FastPolicyText = doc.createTextNode("BRONZE1")
- pool2FastPolicy.appendChild(pool2FastPolicyText)
-
- eComServer = doc.createElement("EcomServer")
- eComServers.appendChild(eComServer)
-
- ecomserverip = doc.createElement("EcomServerIp")
- eComServer.appendChild(ecomserverip)
- ecomserveriptext = doc.createTextNode("1.1.1.1")
- ecomserverip.appendChild(ecomserveriptext)
-
- ecomserverport = doc.createElement("EcomServerPort")
- eComServer.appendChild(ecomserverport)
- ecomserverporttext = doc.createTextNode("10")
- ecomserverport.appendChild(ecomserverporttext)
-
- ecomusername = doc.createElement("EcomUserName")
- eComServer.appendChild(ecomusername)
- ecomusernametext = doc.createTextNode("user")
- ecomusername.appendChild(ecomusernametext)
-
- ecompassword = doc.createElement("EcomPassword")
- eComServer.appendChild(ecompassword)
- ecompasswordtext = doc.createTextNode("pass")
- ecompassword.appendChild(ecompasswordtext)
-
- arrays = doc.createElement("Arrays")
- eComServer.appendChild(arrays)
-
- array = doc.createElement("Array")
- arrays.appendChild(array)
-
- serialNo = doc.createElement("SerialNumber")
- array.appendChild(serialNo)
- serialNoText = doc.createTextNode("1234567891011")
- serialNo.appendChild(serialNoText)
-
- portgroups = doc.createElement("PortGroups")
- array.appendChild(portgroups)
-
- portgroup = doc.createElement("PortGroup")
- portgroups.appendChild(portgroup)
- portgrouptext = doc.createTextNode(self.data.port_group)
- portgroup.appendChild(portgrouptext)
-
- pools = doc.createElement("Pools")
- array.appendChild(pools)
-
- pool = doc.createElement("Pool")
- pools.appendChild(pool)
- poolName = doc.createElement("PoolName")
- pool.appendChild(poolName)
- poolNameText = doc.createTextNode("gold")
- poolName.appendChild(poolNameText)
-
- pool2 = doc.createElement("Pool")
- pools.appendChild(pool2)
- pool2Name = doc.createElement("PoolName")
- pool2.appendChild(pool2Name)
- pool2NameText = doc.createTextNode("SATA_BRONZE1")
- pool2Name.appendChild(pool2NameText)
- pool2FastPolicy = doc.createElement("FastPolicy")
- pool2.appendChild(pool2FastPolicy)
- pool2FastPolicyText = doc.createTextNode("BRONZE1")
- pool2FastPolicy.appendChild(pool2FastPolicyText)
-
- filename = 'cinder_emc_config_V2_MULTI_ECOM.xml'
- self.config_file_path = self.tempdir + '/' + filename
-
- f = open(self.config_file_path, 'w')
- doc.writexml(f)
- f.close()
-
- def fake_ecom_connection(self):
- self.conn = FakeEcomConnection()
- return self.conn
-
- def fake_is_v3(self, conn, serialNumber):
- return False
-
- def test_array_info_multi_ecom_no_fast(self):
- pool = 'gold+1234567891011'
- arrayInfo = self.driver.utils.parse_file_to_get_array_map(
- self.config_file_path)
- self.assertEqual(4, len(arrayInfo))
- poolRec = self.driver.utils.extract_record(arrayInfo, pool)
-
- self.assertEqual('1234567891011', poolRec['SerialNumber'])
- self.assertEqual(self.data.port_group, poolRec['PortGroup'])
- self.assertEqual(self.data.poolname, poolRec['PoolName'])
- self.assertEqual('user', poolRec['EcomUserName'])
- self.assertEqual('pass', poolRec['EcomPassword'])
- self.assertIsNone(poolRec['FastPolicy'])
- self.assertFalse(poolRec['EcomUseSSL'])
-
- def test_array_info_multi_ecom_fast(self):
- pool = 'SATA_BRONZE1+1234567891011'
-
- arrayInfo = self.driver.utils.parse_file_to_get_array_map(
- self.config_file_path)
- self.assertEqual(4, len(arrayInfo))
- poolRec = self.driver.utils.extract_record(arrayInfo, pool)
-
- self.assertEqual('1234567891011', poolRec['SerialNumber'])
- self.assertEqual(self.data.port_group, poolRec['PortGroup'])
- self.assertEqual('SATA_BRONZE1', poolRec['PoolName'])
- self.assertEqual('user', poolRec['EcomUserName'])
- self.assertEqual('pass', poolRec['EcomPassword'])
- self.assertEqual('BRONZE1', poolRec['FastPolicy'])
- self.assertFalse(poolRec['EcomUseSSL'])
-
- @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': 'MULTI_ECOM_BE'})
- def test_create_volume_multi_ecom_success(
- self, _mock_volume_type, mock_storage_system):
- self.vol_v2['provider_location'] = None
- self.driver.create_volume(self.vol_v2)
-
- @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': 'MULTI_ECOM_BE'})
- # If there are more than one unique arrays in conf file
- def test_create_CG_multi_array_failure(
- self, _mock_volume_type, _mock_storage_system):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.create_consistencygroup,
- self.data.test_ctxt,
- self.data.test_CG)
-
+ '_initial_setup',
+ return_value=EMCVMAXCommonData.multi_pool_extra_specs)
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_get_members_of_replication_group',
@@ -7493,38 +7207,175 @@ class EMCV2MultiPoolDriverMultipleEcomsTestCase(test.TestCase):
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': 'MULTI_ECOM_BE'})
- # There is more than one unique arrays in the conf file
- def test_delete_CG_no_volumes_multi_array_failure(
- self, _mock_volume_type, _mock_storage_system,
- _mock_db_volumes, _mock_members):
- self.assertRaises(exception.VolumeBackendAPIException,
- self.driver.delete_consistencygroup,
- self.data.test_ctxt,
- self.data.test_CG,
- [])
+ def test_delete_CG_no_volumes_multi_slo_success(
+ self, _mock_storage_system,
+ _mock_db_volumes, _mock_members, mock_is):
+ # This is a CG delete with no volumes
+ # there won't be a deleted status
+ model_update = {}
+ ret_model_update, ret_volumes_model_update = (
+ self.driver.delete_consistencygroup(self.data.test_ctxt,
+ self.data.test_CG, []))
+ self.assertEqual(model_update, ret_model_update)
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_initial_setup',
+ return_value=EMCVMAXCommonData.multi_pool_extra_specs)
@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': 'MULTI_ECOM_BE'})
- def test_create_volume_in_CG_multi_ecom_success(
- self, _mock_volume_type, mock_storage_system):
- self.data.test_volume_CG['provider_location'] = None
- self.driver.create_volume(self.data.test_volume_CG)
+ def test_delete_CG_with_volumes_multi_slo_success(
+ self, _mock_storage_system, mock_is):
+ # Check for the status deleted after a successful delete CG
+ model_update = {'status': 'deleted'}
+ ret_model_update, ret_volumes_model_update = (
+ self.driver.delete_consistencygroup(self.data.test_ctxt,
+ self.data.test_CG, []))
+ self.assertEqual(model_update, ret_model_update)
- def _cleanup(self):
- bExists = os.path.exists(self.config_file_path)
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_initial_setup',
+ return_value=EMCVMAXCommonData.multi_pool_extra_specs)
+ def test_migrate_volume_v3_success(self, mock_is):
+ retVal, retList = self.driver.migrate_volume(
+ self.data.test_ctxt, self.data.test_volume_v4,
+ self.data.test_host_1_v3)
+ self.assertTrue(retVal)
+
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'get_volume_element_name',
+ return_value='1')
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'get_v3_default_sg_instance_name',
+ return_value=(None, None, EMCVMAXCommonData.default_sg_instance_name))
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'is_clone_licensed',
+ return_value=True)
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_initial_setup',
+ return_value=EMCVMAXCommonData.multi_pool_extra_specs)
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_get_pool_and_storage_system',
+ return_value=(None, EMCVMAXCommonData.storage_system))
+ def test_create_snapshot_v3_success(
+ self, mock_pool, mock_is, mock_license, mock_sg, mock_element):
+ self.data.test_volume_v4['volume_name'] = "vmax-1234567"
+ self.driver.create_snapshot(self.data.test_snapshot_1_v3)
+ utils = self.driver.common.provisionv3.utils
+ utils.get_v3_default_sg_instance_name.assert_called_once_with(
+ self.conn, u'SRP_1', u'Bronze', u'DSS', u'SYMMETRIX+000195900551')
+
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_initial_setup',
+ return_value=EMCVMAXCommonData.multi_pool_extra_specs)
+ def test_delete_snapshot_v3_success(self, mock_is):
+ masking = self.driver.common.masking
+ with mock.patch.object(
+ masking, 'get_associated_masking_groups_from_device',
+ return_value=self.data.storagegroups):
+ self.driver.delete_snapshot(self.data.test_snapshot_1_v3)
+
+ @mock.patch.object(
+ emc_vmax_provision_v3.EMCVMAXProvisionV3,
+ 'get_srp_pool_stats',
+ return_value=(100, 10, 1, 20, False))
+ def test_update_volume_stats_single_array_info(self, mock_stats):
+ self.driver.common.pool_info['reserved_percentage'] = 5
+ self.driver.common.pool_info['arrays_info'] = (
+ self.default_array_info_list())
+ self.driver.common.multiPoolSupportEnabled = True
+ data = self.driver.common.update_volume_stats()
+ pools = data['pools']
+ self.assertEqual("Bronze+DSS+SRP_1+1234567891011",
+ pools[0]['pool_name'])
+ self.assertEqual("1234567891011#SRP_1#Bronze#DSS",
+ pools[0]['location_info'])
+ self._cleanup_pool_info()
+
+ @mock.patch.object(
+ emc_vmax_provision_v3.EMCVMAXProvisionV3,
+ 'get_srp_pool_stats',
+ return_value=(100, 10, 1, 20, False))
+ def test_update_volume_stats_multiple_array_info_wlp_disabled(
+ self, mock_stats):
+ self.driver.common.pool_info['reserved_percentage'] = 5
+ self.driver.common.pool_info['arrays_info'] = (
+ self.multiple_array_info_list())
+ self.driver.common.multiPoolSupportEnabled = True
+ data = self.driver.common.update_volume_stats()
+ pools = data['pools']
+ self.assertEqual("Bronze+DSS+SRP_1+1234567891011",
+ pools[0]['pool_name'])
+ self.assertEqual("1234567891011#SRP_1#Bronze#DSS",
+ pools[0]['location_info'])
+ self.assertEqual("Silver+OLTP+SRP_1+1234567891011",
+ pools[1]['pool_name'])
+ self.assertEqual("1234567891011#SRP_1#Silver#OLTP",
+ pools[1]['location_info'])
+ self._cleanup_pool_info()
+
+ @mock.patch.object(
+ emc_vmax_provision_v3.EMCVMAXProvisionV3,
+ 'get_srp_pool_stats',
+ return_value=(100, 10, 1, 20, False))
+ def test_update_volume_stats_multiple_array_info_wlp_enabled(
+ self, mock_stats):
+ self.driver.common.pool_info['reserved_percentage'] = 5
+ self.driver.common.pool_info['arrays_info'] = (
+ self.multiple_array_info_list())
+ self.driver.common.multiPoolSupportEnabled = True
+ data = self.driver.common.update_volume_stats()
+ pools = data['pools']
+ self.assertEqual("Bronze+DSS+SRP_1+1234567891011",
+ pools[0]['pool_name'])
+ self.assertEqual("1234567891011#SRP_1#Bronze#DSS",
+ pools[0]['location_info'])
+ self.assertEqual("Silver+OLTP+SRP_1+1234567891011",
+ pools[1]['pool_name'])
+ self.assertEqual("1234567891011#SRP_1#Silver#OLTP",
+ pools[1]['location_info'])
+ self._cleanup_pool_info()
+
+ @mock.patch.object(
+ emc_vmax_provision_v3.EMCVMAXProvisionV3,
+ 'get_srp_pool_stats',
+ return_value=(100, 10, 1, 20, False))
+ def test_update_volume_stats_without_multi_pool(self, mock_stats):
+ self.driver.common.pool_info['reserved_percentage'] = 5
+ self.driver.common.pool_info['arrays_info'] = (
+ self.multiple_array_info_list())
+ data = self.driver.common.update_volume_stats()
+ pools = data['pools']
+ # Match with the older pool_name format
+ self.assertEqual("Bronze+SRP_1+1234567891011",
+ pools[0]['pool_name'])
+ self.assertEqual("1234567891011#SRP_1#Bronze#DSS",
+ pools[0]['location_info'])
+ self.assertEqual("Silver+SRP_1+1234567891011",
+ pools[1]['pool_name'])
+ self.assertEqual("1234567891011#SRP_1#Silver#OLTP",
+ pools[1]['location_info'])
+ self._cleanup_pool_info()
+
+ def _cleanup(self, tempdir, config_file_path):
+ bExists = os.path.exists(config_file_path)
if bExists:
- os.remove(self.config_file_path)
- shutil.rmtree(self.tempdir)
+ os.remove(config_file_path)
+ shutil.rmtree(tempdir)
+
+ def _cleanup_pool_info(self):
+ self.driver.common.pool_info['reserved_percentage'] = 0
+ self.driver.common.pool_info['arrays_info'] = []
+ self.driver.common.multiPoolSupportEnabled = False
class EMCVMAXProvisionV3Test(test.TestCase):
diff --git a/cinder/volume/drivers/emc/emc_vmax_common.py b/cinder/volume/drivers/emc/emc_vmax_common.py
index e8f91002bc8..1134e06dca4 100644
--- a/cinder/volume/drivers/emc/emc_vmax_common.py
+++ b/cinder/volume/drivers/emc/emc_vmax_common.py
@@ -20,9 +20,11 @@ import os.path
from oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import units
+import re
import six
from cinder import exception
+from cinder import utils as cinder_utils
from cinder.i18n import _, _LE, _LI, _LW
from cinder.objects import fields
from cinder.volume.drivers.emc import emc_vmax_fast
@@ -56,6 +58,7 @@ ARRAY = 'storagetype:array'
FASTPOLICY = 'storagetype:fastpolicy'
BACKENDNAME = 'volume_backend_name'
COMPOSITETYPE = 'storagetype:compositetype'
+MULTI_POOL_SUPPORT = 'MultiPoolSupport'
STRIPECOUNT = 'storagetype:stripecount'
MEMBERCOUNT = 'storagetype:membercount'
STRIPED = 'striped'
@@ -79,7 +82,11 @@ emc_opts = [
cfg.StrOpt('cinder_emc_config_file',
default=CINDER_EMC_CONFIG_FILE,
help='use this file for cinder emc plugin '
- 'config data'), ]
+ 'config data'),
+ cfg.StrOpt('multi_pool_support',
+ default=False,
+ help='use this value to specify'
+ 'multi-pool support for VMAX3')]
CONF.register_opts(emc_opts)
@@ -128,6 +135,7 @@ class EMCVMAXCommon(object):
self.provision = emc_vmax_provision.EMCVMAXProvision(prtcl)
self.provisionv3 = emc_vmax_provision_v3.EMCVMAXProvisionV3(prtcl)
self.version = version
+ self.multiPoolSupportEnabled = False
self._gather_info()
def _gather_info(self):
@@ -138,7 +146,11 @@ class EMCVMAXCommon(object):
else:
self.pool_info['config_file'] = (
self.configuration.safe_get('cinder_emc_config_file'))
-
+ if hasattr(self.configuration, 'multi_pool_support'):
+ tempMultiPoolSupported = cinder_utils.get_bool_param(
+ 'multi_pool_support', self.configuration)
+ if tempMultiPoolSupported:
+ self.multiPoolSupportEnabled = True
self.pool_info['backend_name'] = (
self.configuration.safe_get('volume_backend_name'))
self.pool_info['max_over_subscription_ratio'] = (
@@ -151,9 +163,75 @@ class EMCVMAXCommon(object):
{'emcConfigFileName': self.pool_info['config_file'],
'backendName': self.pool_info['backend_name']})
- self.pool_info['arrays_info'] = (
- self.utils.parse_file_to_get_array_map(
- self.pool_info['config_file']))
+ arrayInfoList = self.utils.parse_file_to_get_array_map(
+ self.pool_info['config_file'])
+ # Assuming that there is a single array info object always
+ # Check if Multi pool support is enabled
+ if self.multiPoolSupportEnabled is False:
+ self.pool_info['arrays_info'] = arrayInfoList
+ else:
+ finalArrayInfoList = self._get_slo_workload_combinations(
+ arrayInfoList)
+ self.pool_info['arrays_info'] = finalArrayInfoList
+
+ def _get_slo_workload_combinations(self, arrayInfoList):
+ """Method to query the array for SLO and Workloads.
+
+ Takes the arrayInfoList object and generates a set which has
+ all available SLO & Workload combinations
+
+ :param arrayInfoList:
+ :return: finalArrayInfoList
+ :raises: Exception
+ """
+ try:
+ sloWorkloadSet = set()
+ # Pattern for extracting the SLO & Workload String
+ pattern = re.compile("^-S[A-Z]+")
+ for arrayInfo in arrayInfoList:
+ self._set_ecom_credentials(arrayInfo)
+ isV3 = self.utils.isArrayV3(self.conn,
+ arrayInfo['SerialNumber'])
+ # Only if the array is VMAX3
+ if isV3:
+ poolInstanceName, storageSystemStr = (
+ self._find_pool_in_array(arrayInfo['SerialNumber'],
+ arrayInfo['PoolName'], isV3))
+ # Get the pool capability
+ storagePoolCapability = (
+ self.provisionv3.get_storage_pool_capability(
+ self.conn, poolInstanceName))
+ # Get the pool settings
+ storagePoolSettings = self.conn.AssociatorNames(
+ storagePoolCapability,
+ ResultClass='CIM_storageSetting')
+ for storagePoolSetting in storagePoolSettings:
+ settingInstanceID = storagePoolSetting['InstanceID']
+ settingInstanceDetails = settingInstanceID.split('+')
+ sloWorkloadString = settingInstanceDetails[2]
+ if pattern.match(sloWorkloadString):
+ length = len(sloWorkloadString)
+ tempSloWorkloadString = (
+ sloWorkloadString[2:length - 1])
+ sloWorkloadSet.add(tempSloWorkloadString)
+ # Assuming that there is always a single arrayInfo object
+ finalArrayInfoList = []
+ for sloWorkload in sloWorkloadSet:
+ # Doing a shallow copy will work as we are modifying
+ # only strings
+ temparrayInfo = arrayInfoList[0].copy()
+ slo, workload = sloWorkload.split(':')
+ if temparrayInfo['SLO'] is None:
+ temparrayInfo['SLO'] = slo
+ temparrayInfo['Workload'] = workload
+ finalArrayInfoList.append(temparrayInfo)
+ except Exception:
+ exceptionMessage = (_(
+ "Unable to get the SLO/Workload combinations from the array"))
+ LOG.exception(exceptionMessage)
+ raise exception.VolumeBackendAPIException(
+ data=exceptionMessage)
+ return finalArrayInfoList
def create_volume(self, volume):
"""Creates a EMC(VMAX) volume from a pre-existing storage pool.
@@ -230,14 +308,12 @@ class EMCVMAXCommon(object):
:raises: VolumeBackendAPIException
"""
LOG.debug("Entering create_volume_from_snapshot.")
- snapshot['host'] = volume['host']
- extraSpecs = self._initial_setup(snapshot)
+ extraSpecs = self._initial_setup(snapshot, host=volume['host'])
self.conn = self._get_ecom_connection()
snapshotInstance = self._find_lun(snapshot)
self._sync_check(snapshotInstance, snapshot['name'], extraSpecs)
- snapshot['host'] = volume['host']
return self._create_cloned_volume(volume, snapshot, extraSpecs, False)
def create_cloned_volume(self, cloneVolume, sourceVolume):
@@ -285,8 +361,7 @@ class EMCVMAXCommon(object):
"""
LOG.info(_LI("Delete Snapshot: %(snapshotName)s."),
{'snapshotName': snapshot['name']})
- snapshot['host'] = volume['host']
- self._delete_snapshot(snapshot)
+ self._delete_snapshot(snapshot, volume['host'])
def _remove_members(self, controllerConfigService,
volumeInstance, connector, extraSpecs):
@@ -662,6 +737,10 @@ class EMCVMAXCommon(object):
def update_volume_stats(self):
"""Retrieve stats info."""
pools = []
+ # Dictionary to hold the VMAX3 arrays for which the SRP details
+ # have already been queried
+ # This only applies to the arrays for which WLP is not enabled
+ arrays = {}
backendName = self.pool_info['backend_name']
max_oversubscription_ratio = (
self.pool_info['max_over_subscription_ratio'])
@@ -669,17 +748,43 @@ class EMCVMAXCommon(object):
array_max_over_subscription = None
array_reserve_percent = None
for arrayInfo in self.pool_info['arrays_info']:
+ alreadyQueried = False
self._set_ecom_credentials(arrayInfo)
# Check what type of array it is
- isV3 = self.utils.isArrayV3(self.conn, arrayInfo['SerialNumber'])
+ isV3 = self.utils.isArrayV3(self.conn,
+ arrayInfo['SerialNumber'])
if isV3:
- (location_info, total_capacity_gb, free_capacity_gb,
- provisioned_capacity_gb,
- array_reserve_percent) = self._update_srp_stats(arrayInfo)
- poolName = ("%(slo)s+%(poolName)s+%(array)s"
- % {'slo': arrayInfo['SLO'],
- 'poolName': arrayInfo['PoolName'],
- 'array': arrayInfo['SerialNumber']})
+ # Report only the SLO name in the pool name for
+ # backward compatibility
+ if self.multiPoolSupportEnabled is False:
+ (location_info, total_capacity_gb, free_capacity_gb,
+ provisioned_capacity_gb,
+ array_reserve_percent,
+ wlpEnabled) = self._update_srp_stats(arrayInfo)
+ poolName = ("%(slo)s+%(poolName)s+%(array)s"
+ % {'slo': arrayInfo['SLO'],
+ 'poolName': arrayInfo['PoolName'],
+ 'array': arrayInfo['SerialNumber']})
+ else:
+ # Add both SLO & Workload name in the pool name
+ # Query the SRP only once if WLP is not enabled
+ # Only insert the array details in the dict once
+ if arrayInfo['SerialNumber'] not in arrays:
+ (location_info, total_capacity_gb, free_capacity_gb,
+ provisioned_capacity_gb,
+ array_reserve_percent,
+ wlpEnabled) = self._update_srp_stats(arrayInfo)
+ else:
+ alreadyQueried = True
+ poolName = ("%(slo)s+%(workload)s+%(poolName)s+%(array)s"
+ % {'slo': arrayInfo['SLO'],
+ 'workload': arrayInfo['Workload'],
+ 'poolName': arrayInfo['PoolName'],
+ 'array': arrayInfo['SerialNumber']})
+ if wlpEnabled is False:
+ arrays[arrayInfo['SerialNumber']] = (
+ [total_capacity_gb, free_capacity_gb,
+ provisioned_capacity_gb, array_reserve_percent])
else:
# This is V2
(location_info, total_capacity_gb, free_capacity_gb,
@@ -689,28 +794,64 @@ class EMCVMAXCommon(object):
% {'poolName': arrayInfo['PoolName'],
'array': arrayInfo['SerialNumber']})
- pool = {'pool_name': poolName,
- 'total_capacity_gb': total_capacity_gb,
- 'free_capacity_gb': free_capacity_gb,
- 'provisioned_capacity_gb': provisioned_capacity_gb,
- 'QoS_support': False,
- 'location_info': location_info,
- 'consistencygroup_support': True,
- 'thin_provisioning_support': True,
- 'thick_provisioning_support': False,
- 'max_over_subscription_ratio': max_oversubscription_ratio
- }
+ if (alreadyQueried
+ is True and self.multiPoolSupportEnabled is True):
+ # The dictionary will only have one key per VMAX3
+ # Construct the location info
+ temp_location_info = (
+ ("%(arrayName)s#%(poolName)s#%(slo)s#%(workload)s"
+ % {'arrayName': arrayInfo['SerialNumber'],
+ 'poolName': arrayInfo['PoolName'],
+ 'slo': arrayInfo['SLO'],
+ 'workload': arrayInfo['Workload']}))
+ pool = {'pool_name': poolName,
+ 'total_capacity_gb':
+ arrays[arrayInfo['SerialNumber']][0],
+ 'free_capacity_gb':
+ arrays[arrayInfo['SerialNumber']][1],
+ 'provisioned_capacity_gb':
+ arrays[arrayInfo['SerialNumber']][2],
+ 'QoS_support': True,
+ 'location_info': temp_location_info,
+ 'consistencygroup_support': True,
+ 'thin_provisioning_support': True,
+ 'thick_provisioning_support': False,
+ 'max_over_subscription_ratio':
+ max_oversubscription_ratio
+ }
+ if (
+ arrays[arrayInfo['SerialNumber']][3] and
+ (arrays[arrayInfo['SerialNumber']][3] >
+ reservedPercentage)):
+ pool['reserved_percentage'] = (
+ arrays[arrayInfo['SerialNumber']][3])
+ else:
+ pool['reserved_percentage'] = reservedPercentage
+ else:
+ pool = {'pool_name': poolName,
+ 'total_capacity_gb': total_capacity_gb,
+ 'free_capacity_gb': free_capacity_gb,
+ 'provisioned_capacity_gb': provisioned_capacity_gb,
+ 'QoS_support': False,
+ 'location_info': location_info,
+ 'consistencygroup_support': True,
+ 'thin_provisioning_support': True,
+ 'thick_provisioning_support': False,
+ 'max_over_subscription_ratio':
+ max_oversubscription_ratio
+ }
+ if (
+ array_reserve_percent and
+ (array_reserve_percent > reservedPercentage)):
+ pool['reserved_percentage'] = array_reserve_percent
+ else:
+ pool['reserved_percentage'] = reservedPercentage
+
if array_max_over_subscription:
pool['max_over_subscription_ratio'] = (
self.utils.override_ratio(
max_oversubscription_ratio,
array_max_over_subscription))
-
- if array_reserve_percent and (
- array_reserve_percent > reservedPercentage):
- pool['reserved_percentage'] = array_reserve_percent
- else:
- pool['reserved_percentage'] = reservedPercentage
pools.append(pool)
data = {'vendor_name': "EMC",
@@ -736,10 +877,11 @@ class EMCVMAXCommon(object):
:returns: remainingManagedSpaceGbs
:returns: provisionedManagedSpaceGbs
:returns: array_reserve_percent
+ :returns: wlpEnabled
"""
(totalManagedSpaceGbs, remainingManagedSpaceGbs,
- provisionedManagedSpaceGbs, array_reserve_percent) = (
+ provisionedManagedSpaceGbs, array_reserve_percent, wlpEnabled) = (
self.provisionv3.get_srp_pool_stats(self.conn, arrayInfo))
LOG.info(_LI(
@@ -761,7 +903,7 @@ class EMCVMAXCommon(object):
return (location_info, totalManagedSpaceGbs,
remainingManagedSpaceGbs, provisionedManagedSpaceGbs,
- array_reserve_percent)
+ array_reserve_percent, wlpEnabled)
def retype(self, ctxt, volume, new_type, diff, host):
"""Migrate volume to another host using retype.
@@ -1338,14 +1480,31 @@ class EMCVMAXCommon(object):
extraSpecs = self.utils.get_volumetype_extraspecs(volume, volumeTypeId)
qosSpecs = self.utils.get_volumetype_qosspecs(volume, volumeTypeId)
configGroup = None
-
# If there are no extra specs then the default case is assumed.
if extraSpecs:
configGroup = self.configuration.config_group
configurationFile = self._register_config_file_from_config_group(
configGroup)
+ self.multiPoolSupportEnabled = (
+ self._get_multi_pool_support_enabled_flag())
+ extraSpecs[MULTI_POOL_SUPPORT] = self.multiPoolSupportEnabled
return extraSpecs, configurationFile, qosSpecs
+ def _get_multi_pool_support_enabled_flag(self):
+ """Reads the configuration fpr multi pool support flag.
+
+ :returns: MultiPoolSupportEnabled flag
+ """
+
+ confString = (
+ self.configuration.safe_get('multi_pool_support'))
+ retVal = False
+ stringTrue = "True"
+ if confString:
+ if confString.lower() == stringTrue.lower():
+ retVal = True
+ return retVal
+
def _get_ecom_connection(self):
"""Get the ecom connection.
@@ -1774,7 +1933,7 @@ class EMCVMAXCommon(object):
% {'ip_port': ip_port})
self.conn = self._get_ecom_connection()
- def _initial_setup(self, volume, volumeTypeId=None):
+ def _initial_setup(self, volume, volumeTypeId=None, host=None):
"""Necessary setup to accumulate the relevant information.
The volume object has a host in which we can parse the
@@ -1794,13 +1953,17 @@ class EMCVMAXCommon(object):
extraSpecs, configurationFile, qosSpecs = (
self._set_config_file_and_get_extra_specs(
volume, volumeTypeId))
-
- pool = self._validate_pool(volume)
+ pool = self._validate_pool(volume, extraSpecs=extraSpecs,
+ host=host)
LOG.debug("Pool returned is %(pool)s.",
{'pool': pool})
arrayInfo = self.utils.parse_file_to_get_array_map(
configurationFile)
- poolRecord = self.utils.extract_record(arrayInfo, pool)
+ if arrayInfo is not None:
+ if extraSpecs['MultiPoolSupport'] is True:
+ poolRecord = arrayInfo[0]
+ else:
+ poolRecord = self.utils.extract_record(arrayInfo, pool)
if not poolRecord:
exceptionMessage = (_(
@@ -2148,7 +2311,6 @@ class EMCVMAXCommon(object):
'sourceName': sourceName})
self.conn = self._get_ecom_connection()
-
sourceInstance = self._find_lun(sourceVolume)
storageSystem = sourceInstance['SystemName']
repServCapabilityInstanceName = (
@@ -2267,7 +2429,7 @@ class EMCVMAXCommon(object):
cloneDict, cloneName, storageConfigService, storageSystemName,
fastPolicyName, extraSpecs)
- def _delete_volume(self, volume):
+ def _delete_volume(self, volume, host=None):
"""Helper function to delete the specified volume.
:param volume: volume object to be deleted
@@ -2278,7 +2440,7 @@ class EMCVMAXCommon(object):
rc = -1
errorRet = (rc, volumeName)
- extraSpecs = self._initial_setup(volume)
+ extraSpecs = self._initial_setup(volume, host=host)
self.conn = self._get_ecom_connection()
volumeInstance = self._find_lun(volume)
@@ -2454,7 +2616,7 @@ class EMCVMAXCommon(object):
return numVolumesMapped
- def _delete_snapshot(self, snapshot):
+ def _delete_snapshot(self, snapshot, host=None):
"""Helper function to delete the specified snapshot.
:param snapshot: snapshot object to be deleted
@@ -2465,7 +2627,7 @@ class EMCVMAXCommon(object):
self.conn = self._get_ecom_connection()
# Delete the target device.
- rc, snapshotname = self._delete_volume(snapshot)
+ rc, snapshotname = self._delete_volume(snapshot, host)
LOG.info(_LI("Leaving delete_snapshot: %(ssname)s Return code: "
"%(rc)lu."),
{'ssname': snapshotname,
@@ -2486,7 +2648,7 @@ class EMCVMAXCommon(object):
cgName = self._update_consistency_group_name(group)
volumeTypeId = group['volume_type_id'].replace(",", "")
- extraSpecs = self._initial_setup(None, volumeTypeId)
+ extraSpecs = self._initial_setup(None, volumeTypeId=volumeTypeId)
_poolInstanceName, storageSystem = (
self._get_pool_and_storage_system(extraSpecs))
@@ -2522,8 +2684,8 @@ class EMCVMAXCommon(object):
{'group': group['id']})
cgName = self._update_consistency_group_name(group)
-
modelUpdate = {}
+ volumes_model_update = {}
if not self.conn:
self.conn = self._get_ecom_connection()
@@ -2547,7 +2709,6 @@ class EMCVMAXCommon(object):
memberInstanceNames = self._get_members_of_replication_group(
cgInstanceName)
-
self.provision.delete_consistency_group(self.conn,
replicationService,
cgInstanceName, cgName,
@@ -3177,23 +3338,9 @@ class EMCVMAXCommon(object):
"belonging to any storage group."),
{'volumeName': volumeName})
else:
- self.provision.remove_device_from_storage_group(
- self.conn,
- controllerConfigService,
- foundStorageGroupInstanceName,
- volumeInstance.path,
- volumeName, extraSpecs)
- # Check that it has been removed.
- sgFromVolRemovedInstanceName = (
- self.utils.wrap_get_storage_group_from_volume(
- self.conn, volumeInstance.path, defaultSgName))
- if sgFromVolRemovedInstanceName is not None:
- LOG.error(_LE(
- "Volume : %(volumeName)s has not been "
- "removed from source storage group %(storageGroup)s."),
- {'volumeName': volumeName,
- 'storageGroup': sgFromVolRemovedInstanceName})
- return False
+ self.masking.remove_and_reset_members(
+ self.conn, controllerConfigService, volumeInstance,
+ volumeName, extraSpecs, None, False)
storageGroupName = self.utils.get_v3_storage_group_name(
poolName, targetSlo, targetWorkload)
@@ -3385,8 +3532,33 @@ class EMCVMAXCommon(object):
:param poolRecord: pool record
:returns: dict -- the extra specifications dictionary
"""
- extraSpecs[SLO] = poolRecord['SLO']
- extraSpecs[WORKLOAD] = poolRecord['Workload']
+ if extraSpecs['MultiPoolSupport'] is True:
+ sloFromExtraSpec = None
+ workloadFromExtraSpec = None
+ if 'pool_name' in extraSpecs:
+ try:
+ poolDetails = extraSpecs['pool_name'].split('+')
+ sloFromExtraSpec = poolDetails[0]
+ workloadFromExtraSpec = poolDetails[1]
+ except KeyError:
+ LOG.error(_LE("Error parsing SLO, workload from "
+ "the provided extra_specs."))
+ else:
+ # Throw an exception as it is compulsory to have
+ # pool_name in the extra specs
+ exceptionMessage = (_(
+ "Pool_name is not present in the extraSpecs "
+ "and MultiPoolSupport is enabled"))
+ raise exception.VolumeBackendAPIException(
+ data=exceptionMessage)
+ # If MultiPoolSupport is enabled, we completely
+ # ignore any entry for SLO & Workload in the poolRecord
+ extraSpecs[SLO] = sloFromExtraSpec
+ extraSpecs[WORKLOAD] = workloadFromExtraSpec
+ else:
+ extraSpecs[SLO] = poolRecord['SLO']
+ extraSpecs[WORKLOAD] = poolRecord['Workload']
+
extraSpecs[ISV3] = True
extraSpecs = self._set_common_extraSpecs(extraSpecs, poolRecord)
LOG.debug("Pool is: %(pool)s "
@@ -4021,7 +4193,7 @@ class EMCVMAXCommon(object):
extraSpecs)
return rc
- def _validate_pool(self, volume):
+ def _validate_pool(self, volume, extraSpecs=None, host=None):
"""Get the pool from volume['host'].
There may be backward compatibiliy concerns, so putting in a
@@ -4030,6 +4202,7 @@ class EMCVMAXCommon(object):
assume it was created pre 'Pool Aware Scheduler' feature.
:param volume: the volume Object
+ :param extraSpecs: extraSpecs provided in the volume type
:returns: string -- pool
:raises: VolumeBackendAPIException
"""
@@ -4038,6 +4211,9 @@ class EMCVMAXCommon(object):
if volume is None:
return pool
+ if host is None:
+ host = volume['host']
+
# This check is for all operations except a create.
# On a create provider_location is None
try:
@@ -4049,10 +4225,21 @@ class EMCVMAXCommon(object):
except KeyError:
return pool
try:
- pool = volume_utils.extract_host(volume['host'], 'pool')
+ pool = volume_utils.extract_host(host, 'pool')
if pool:
LOG.debug("Pool from volume['host'] is %(pool)s.",
{'pool': pool})
+ # Check if it matches with the poolname if it is provided
+ # in the extra specs
+ if extraSpecs is not None:
+ if 'pool_name' in extraSpecs:
+ if extraSpecs['pool_name'] != pool:
+ exceptionMessage = (_(
+ "Pool from volume['host'] %(host)s doesn't"
+ " match with pool_name in extraSpecs.")
+ % {'host': volume['host']})
+ raise exception.VolumeBackendAPIException(
+ data=exceptionMessage)
else:
exceptionMessage = (_(
"Pool from volume['host'] %(host)s not found.")
diff --git a/cinder/volume/drivers/emc/emc_vmax_fc.py b/cinder/volume/drivers/emc/emc_vmax_fc.py
index b6633657720..698bdadd68d 100644
--- a/cinder/volume/drivers/emc/emc_vmax_fc.py
+++ b/cinder/volume/drivers/emc/emc_vmax_fc.py
@@ -72,6 +72,8 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
- QoS support (blueprint vmax-qos)
2.5.0 - Attach and detach snapshot (blueprint vmax-attach-snapshot)
- MVs and SGs not reflecting correct protocol (bug #1640222)
+ - Storage assisted volume migration via retype
+ (bp vmax-volume-migration)
"""
diff --git a/cinder/volume/drivers/emc/emc_vmax_iscsi.py b/cinder/volume/drivers/emc/emc_vmax_iscsi.py
index e6854f83395..952a56818fb 100644
--- a/cinder/volume/drivers/emc/emc_vmax_iscsi.py
+++ b/cinder/volume/drivers/emc/emc_vmax_iscsi.py
@@ -78,6 +78,8 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
https://blueprints.launchpad.net/cinder/+spec/vmax-iscsi-multipath
2.5.0 - Attach and detach snapshot (blueprint vmax-attach-snapshot)
- MVs and SGs not reflecting correct protocol (bug #1640222)
+ - Storage assisted volume migration via retype
+ (bp vmax-volume-migration)
"""
diff --git a/cinder/volume/drivers/emc/emc_vmax_provision_v3.py b/cinder/volume/drivers/emc/emc_vmax_provision_v3.py
index 9d0ec4c717f..c422e05e8de 100644
--- a/cinder/volume/drivers/emc/emc_vmax_provision_v3.py
+++ b/cinder/volume/drivers/emc/emc_vmax_provision_v3.py
@@ -706,11 +706,13 @@ class EMCVMAXProvisionV3(object):
:returns: remainingCapacityGb
:returns: subscribedCapacityGb
:returns: array_reserve_percent
+ :returns: wlpEnabled
"""
totalCapacityGb = -1
remainingCapacityGb = -1
subscribedCapacityGb = -1
array_reserve_percent = -1
+ wlpEnabled = False
storageSystemInstanceName = self.utils.find_storageSystem(
conn, arrayInfo['SerialNumber'])
@@ -756,6 +758,7 @@ class EMCVMAXProvisionV3(object):
storageSystemInstanceName['Name']))
if remainingSLOCapacityGb != -1:
remainingCapacityGb = remainingSLOCapacityGb
+ wlpEnabled = True
else:
LOG.warning(_LW(
"Remaining capacity %(remainingCapacityGb)s "
@@ -765,7 +768,7 @@ class EMCVMAXProvisionV3(object):
{'remainingCapacityGb': remainingCapacityGb})
return (totalCapacityGb, remainingCapacityGb, subscribedCapacityGb,
- array_reserve_percent)
+ array_reserve_percent, wlpEnabled)
def _get_remaining_slo_capacity_wlp(self, conn, srpPoolInstanceName,
arrayInfo, systemName):
diff --git a/cinder/volume/drivers/emc/emc_vmax_utils.py b/cinder/volume/drivers/emc/emc_vmax_utils.py
index fc2cdf5649e..ba574de0450 100644
--- a/cinder/volume/drivers/emc/emc_vmax_utils.py
+++ b/cinder/volume/drivers/emc/emc_vmax_utils.py
@@ -1260,13 +1260,12 @@ class EMCVMAXUtils(object):
:returns: foundSyncInstanceName
"""
foundSyncInstanceName = None
- syncInstanceNames = conn.EnumerateInstanceNames(
- 'SE_StorageSynchronized_SV_SV')
+ syncInstanceNames = conn.ReferenceNames(
+ volumeInstance.path,
+ ResultClass='SE_StorageSynchronized_SV_SV')
for syncInstanceName in syncInstanceNames:
syncSvTarget = syncInstanceName['SyncedElement']
syncSvSource = syncInstanceName['SystemElement']
- if storageSystem != syncSvTarget['SystemName']:
- continue
if syncSvTarget['DeviceID'] == volumeInstance['DeviceID'] or (
syncSvSource['DeviceID'] == volumeInstance['DeviceID']):
# Check that it hasn't recently been deleted.
@@ -1902,65 +1901,10 @@ class EMCVMAXUtils(object):
return kwargs
- def _multi_pool_support(self, fileName):
- """Multi pool support.
-
-
-
-
- 10.108.246.202
- ...
-
-
- 000198700439
- ...
-
-
- FC_SLVR1
- ...
-
-
-
-
-
-
-
-
- :param fileName: the configuration file
- :returns: list
- """
- myList = []
- connargs = {}
- myFile = open(fileName, 'r')
- data = myFile.read()
- myFile.close()
- dom = minidom.parseString(data)
- interval = self._process_tag(dom, 'Interval')
- retries = self._process_tag(dom, 'Retries')
- try:
- ecomElements = dom.getElementsByTagName('EcomServer')
- if ecomElements and len(ecomElements) > 0:
- for ecomElement in ecomElements:
- connargs = self._get_connection_info(ecomElement)
- arrayElements = ecomElement.getElementsByTagName('Array')
- if arrayElements and len(arrayElements) > 0:
- for arrayElement in arrayElements:
- myList = self._get_pool_info(arrayElement,
- fileName, connargs,
- interval, retries,
- myList)
- else:
- LOG.error(_LE(
- "Please check your xml for format or syntax "
- "errors. Please see documentation for more "
- "details."))
- except IndexError:
- pass
- return myList
-
def _single_pool_support(self, fileName):
"""Single pool support.
+ VMAX2
10.108.246.202
5988
@@ -1972,6 +1916,7 @@ class EMCVMAXUtils(object):
000198700439
FC_SLVR1
+ VMAX3
:param fileName: the configuration file
:returns: list
@@ -2021,12 +1966,70 @@ class EMCVMAXUtils(object):
:param fileName: the path and name of the file
:returns: list
- """
- # Multi-pool support.
- myList = self._multi_pool_support(fileName)
- if len(myList) == 0:
- myList = self._single_pool_support(fileName)
+ Sample VMAX2 XML file
+
+ 10.108.246.202
+ 5988
+ admin
+ #1Password
+
+ OS-PORTGROUP1-PG
+
+ 000198700439
+ FC_SLVR1
+
+ Sample VMAX3 XML file
+
+ 10.108.246.202
+ 5988
+ admin
+ #1Password
+
+ OS-PORTGROUP1-PG
+
+ 000198700439
+ FC_SLVR1
+ Diamond <--This is optional
+ OLTP <--This is optional
+
+ :param fileName: the configuration file
+ :returns: list
+ """
+ myList = []
+ kwargs = {}
+ connargs = {}
+ with open(fileName, 'r') as my_file:
+ data = my_file.read()
+ my_file.close()
+ dom = minidom.parseString(data)
+ try:
+ connargs = self._get_connection_info(dom)
+ interval = self._process_tag(dom, 'Interval')
+ retries = self._process_tag(dom, 'Retries')
+ portGroup = self._get_random_portgroup(dom)
+ serialNumber = self._process_tag(dom, 'Array')
+ if serialNumber is None:
+ LOG.error(_LE(
+ "Array Serial Number must be in the file "
+ "%(fileName)s."),
+ {'fileName': fileName})
+ poolName = self._process_tag(dom, 'Pool')
+ if poolName is None:
+ LOG.error(_LE(
+ "PoolName must be in the file "
+ "%(fileName)s."),
+ {'fileName': fileName})
+ kwargs = self._fill_record(
+ connargs, serialNumber, poolName, portGroup, dom)
+ if interval:
+ kwargs['Interval'] = interval
+ if retries:
+ kwargs['Retries'] = retries
+
+ myList.append(kwargs)
+ except IndexError:
+ pass
return myList
def extract_record(self, arrayInfo, pool):
diff --git a/releasenotes/notes/vmax-volume-migration-992c8c68e2207bbc.yaml b/releasenotes/notes/vmax-volume-migration-992c8c68e2207bbc.yaml
new file mode 100644
index 00000000000..4c20b59e727
--- /dev/null
+++ b/releasenotes/notes/vmax-volume-migration-992c8c68e2207bbc.yaml
@@ -0,0 +1,5 @@
+---
+features:
+ - Storage assisted volume migration from one Pool/SLO/Workload combination
+ to another, on the same array, via retype, for the VMAX driver. Both
+ All Flash and Hybrid VMAX3 arrays are supported. VMAX2 is not supported.