storage_system = 'SYMMETRIX+000195900551'
storage_system_v3 = 'SYMMETRIX-+-000197200056'
port_group = 'OS-portgroup-PG'
- lunmaskctrl_id = \
- 'SYMMETRIX+000195900551+OS-fakehost-gold-MV'
- lunmaskctrl_name = \
- 'OS-fakehost-gold-MV'
-
- initiatorgroup_id = \
- 'SYMMETRIX+000195900551+OS-fakehost-IG'
- initiatorgroup_name = \
- 'OS-fakehost-IG'
+ lunmaskctrl_id = (
+ 'SYMMETRIX+000195900551+OS-fakehost-gold-MV')
+ lunmaskctrl_name = 'OS-fakehost-gold-MV'
+
+ initiatorgroup_id = (
+ 'SYMMETRIX+000195900551+OS-fakehost-IG')
+ initiatorgroup_name = 'OS-fakehost-IG'
initiatorgroup_creationclass = 'SE_InitiatorMaskingGroup'
iscsi_initiator = 'iqn.1993-08.org.debian'
storageextent_creationclass = 'CIM_StorageExtent'
totalmanagedspace_gbs = 931
subscribedcapacity_gbs = 466
fake_host = 'HostX@Backend#gold+1234567891011'
+ fake_host_v3 = 'HostX@Backend#Bronze+SRP_1+1234567891011'
+ fake_host_2_v3 = 'HostY@Backend#SRP_1+1234567891011'
+
unit_creationclass = 'CIM_ProtocolControllerForUnit'
storage_type = 'gold'
keybindings = {'CreationClassName': u'Symm_StorageVolume',
'keybindings': keybindings}
provider_location2 = {'classname': 'Symm_StorageVolume',
'keybindings': keybindings2}
+ provider_location_multi_pool = {'classname': 'Symm_StorageVolume',
+ 'keybindings': keybindings,
+ 'version': '2.2.0'}
properties = {'ConsumableBlocks': '12345',
'BlockSize': '512'}
- block_size = 512
test_volume = {'name': 'vol1',
'size': 1,
'volume_name': 'vol1',
'volume_type_id': 'abc',
'provider_location': six.text_type(provider_location),
'status': 'available',
- 'host': 'fake-host',
+ 'host': fake_host,
'NumberOfBlocks': 100,
- 'BlockSize': block_size
+ 'BlockSize': 512
}
test_volume_v2 = {'name': 'vol1',
'volume_type_id': 'abc',
'provider_location': six.text_type(provider_location),
'status': 'available',
- 'host': 'fake-host',
+ 'host': fake_host,
'NumberOfBlocks': 100,
- 'BlockSize': block_size
+ 'BlockSize': 512
}
test_volume_v3 = {'name': 'vol1',
'volume_type_id': 'abc',
'provider_location': six.text_type(provider_location),
'status': 'available',
- 'host': 'fake-host',
+ 'host': fake_host_v3,
'NumberOfBlocks': 100,
- 'BlockSize': block_size
+ 'BlockSize': 512
}
- metaHead_volume = {'DeviceID': 10,
- 'ConsumableBlocks': 1000
- }
- meta_volume1 = {'DeviceID': 11,
- 'ConsumableBlocks': 200
- }
- meta_volume2 = {'DeviceID': 12,
- 'ConsumableBlocks': 300
- }
+
test_volume_CG = {'name': 'volInCG',
'consistencygroup_id': 'abc',
'size': 1,
'volume_type_id': 'abc',
'provider_location': six.text_type(provider_location),
'status': 'available',
- 'host': 'fake-host'
+ 'host': fake_host
}
+ test_volume_CG_v3 = {'name': 'volInCG',
+ 'consistencygroup_id': 'abc',
+ 'size': 1,
+ 'volume_name': 'volInCG',
+ 'id': 'volInCG',
+ 'device_id': '1',
+ 'provider_auth': None,
+ 'project_id': 'project',
+ 'display_name': 'volInCG',
+ 'display_description':
+ 'test volume in Consistency group',
+ 'volume_type_id': 'abc',
+ 'provider_location':
+ six.text_type(provider_location),
+ 'status': 'available',
+ 'host': fake_host_v3}
+
test_failed_volume = {'name': 'failed_vol',
'size': 1,
'volume_name': 'failed_vol',
'project_id': 'project',
'display_name': 'failed_vol',
'display_description': 'test failed volume',
- 'volume_type_id': 'abc'}
+ 'volume_type_id': 'abc',
+ 'host': fake_host}
failed_delete_vol = {'name': 'failed_delete_vol',
'size': '-1',
'display_name': 'failed delete vol',
'display_description': 'failed delete volume',
'volume_type_id': 'abc',
- 'provider_location': six.text_type(provider_location2)
- }
+ 'provider_location':
+ six.text_type(provider_location2),
+ 'host': fake_host}
test_source_volume = {'size': 1,
'volume_type_id': 'sourceid',
'provider_auth': None,
'project_id':
'project', 'id': '2',
+ 'host': fake_host,
'provider_location':
six.text_type(provider_location),
'display_description': 'snapshot source volume'}
+ test_source_volume_v3 = {'size': 1,
+ 'volume_type_id': 'sourceid',
+ 'display_name': 'sourceVolume',
+ 'name': 'sourceVolume',
+ 'id': 'sourceVolume',
+ 'device_id': '1',
+ 'volume_name': 'vmax-154326',
+ 'provider_auth': None,
+ 'project_id':
+ 'project', 'id': '2',
+ 'host': fake_host_v3,
+ 'provider_location':
+ six.text_type(provider_location),
+ 'display_description': 'snapshot source volume'}
+
test_CG = {'name': 'myCG1',
'id': '12345abcde',
'volume_type_id': 'abc',
- 'status': 'available',
- 'host': 'fake-host'
+ 'status': 'available'
}
test_snapshot = {'name': 'myCG1',
'id': '12345abcde',
- 'status': 'available'
+ 'status': 'available',
+ 'host': fake_host
}
test_CG_snapshot = {'name': 'testSnap',
'id': '12345abcde',
}
location_info = {'location_info': '000195900551#silver#None',
'storage_protocol': 'ISCSI'}
+ location_info_v3 = {'location_info': '1234567891011#SRP_1#Bronze#DSS',
+ 'storage_protocol': 'FC'}
test_host = {'capabilities': location_info,
'host': 'fake_host'}
-
- location_info_v3 = {'location_info': '0123456789#SRP_1#Bronze#DSS',
- 'storage_protocol': 'FC'}
test_host_v3 = {'capabilities': location_info_v3,
- 'host': 'fake_v3_host'}
+ 'host': fake_host_2_v3}
initiatorNames = ["123456789012345", "123456789054321"]
test_ctxt = {}
new_type = {}
'volume_backend_name': 'V3_BE',
'storagetype:workload': u'DSS',
'storagetype:slo': u'Bronze',
- 'storagetype:array': u'0123456789',
- 'isV3': True}
- majorVersion = 1
- minorVersion = 2
- revNumber = 3
+ 'storagetype:array': u'1234567891011',
+ 'isV3': True,
+ 'portgroupname': u'OS-portgroup-PG'}
+ remainingSLOCapacity = '123456789'
class FakeLookupService(object):
Type=None, EMCSRP=None, EMCSLO=None, EMCWorkload=None,
EMCCollections=None, InitiatorMaskingGroup=None,
DeviceMaskingGroup=None, TargetMaskingGroup=None,
- ProtocolController=None, StorageID=None, IDType=None,
- WaitForCopyState=None):
+ ProtocolController=None, StorageID=None, IDType=None):
rc = 0
myjob = SE_ConcreteJob()
myjob['status'] = 'success'
myjob['type'] = ElementName
- if Size == -1073741824 and \
- MethodName == 'CreateOrModifyCompositeElement':
+ if Size == -1073741824 and (
+ MethodName == 'CreateOrModifyCompositeElement'):
rc = 0
myjob = SE_ConcreteJob()
myjob.classname = 'SE_ConcreteJob'
myjob['status'] = 'success'
myjob['type'] = 'failed_delete_vol'
- if ElementName == 'failed_vol' and \
- MethodName == 'CreateOrModifyElementFromStoragePool':
+ if ElementName == 'failed_vol' and (
+ MethodName == 'CreateOrModifyElementFromStoragePool'):
rc = 10
myjob['status'] = 'failure'
- elif TheElements and \
- TheElements[0]['DeviceID'] == '99999' and \
- MethodName == 'ReturnElementsToStoragePool':
+ elif TheElements and TheElements[0]['DeviceID'] == '99999' and (
+ MethodName == 'EMCReturnToStoragePool'):
rc = 10
myjob['status'] = 'failure'
elif HardwareId:
endpoints.append(endpoint2)
targetendpoints['TargetEndpoints'] = endpoints
return rc, targetendpoints
- elif ReplicationType and \
- MethodName == 'GetDefaultReplicationSettingData':
+ elif ReplicationType and (
+ MethodName == 'GetDefaultReplicationSettingData'):
rc = 0
rsd = SE_ReplicationSettingData()
rsd['DefaultInstance'] = SE_ReplicationSettingData()
rc = 0
ret['HardwareID'] = self.data.iscsi_initiator
return rc, ret
- elif MethodName == 'GetCompositeElements':
+ if MethodName == 'GetSupportedSizeRange':
ret = {}
rc = 0
- ret['OutElements'] = [self.data.metaHead_volume,
- self.data.meta_volume1,
- self.data.meta_volume2]
+ ret['EMCInformationSource'] = 3
+ ret['EMCRemainingSLOCapacity'] = self.data.remainingSLOCapacity
return rc, ret
job = {'Job': myjob}
result = self._enum_repservcpbls()
elif name == 'SE_StorageSynchronized_SV_SV':
result = self._enum_storageSyncSvSv()
- elif name == 'Symm_SRPStoragePool':
- result = self._enum_srpstoragepool()
else:
result = self._default_enum()
return result
result = self._enum_pool_details()
elif name == 'SE_StorageHardwareID':
result = self._enum_storhdwids()
- elif name == 'SE_ManagementServerSoftwareIdentity':
- result = self._enum_sw_identity()
else:
result = self._default_enum()
return result
def _getinstance_pool(self, objectpath):
pool = {}
pool['CreationClassName'] = 'Symm_VirtualProvisioningPool'
- pool['ElementName'] = self.data.poolname
+ pool['ElementName'] = 'gold'
pool['SystemName'] = self.data.storage_system
pool['TotalManagedSpace'] = self.data.totalmanagedspace_bits
pool['EMCSubscribedCapacity'] = self.data.subscribedcapacity_bits
srpstoragepool = SYMM_SrpStoragePool()
srpstoragepool['CreationClassName'] = (
self.data.srpstoragepool_creationclass)
- srpstoragepool['ElementName'] = 'SRP_1'
classcimproperty = Fake_CIMProperty()
totalManagedSpace = (
def _enum_stconfsvcs(self):
conf_services = []
- conf_service = {}
- conf_service['SystemName'] = self.data.storage_system
- conf_service['CreationClassName'] = \
- self.data.stconf_service_creationclass
- conf_services.append(conf_service)
+ conf_service1 = {}
+ conf_service1['SystemName'] = self.data.storage_system
+ conf_service1['CreationClassName'] = (
+ self.data.stconf_service_creationclass)
+ conf_services.append(conf_service1)
+ conf_service2 = {}
+ conf_service2['SystemName'] = self.data.storage_system_v3
+ conf_service2['CreationClassName'] = (
+ self.data.stconf_service_creationclass)
+ conf_services.append(conf_service2)
return conf_services
def _enum_ctrlconfsvcs(self):
conf_services = []
conf_service = {}
conf_service['SystemName'] = self.data.storage_system
- conf_service['CreationClassName'] = \
- self.data.ctrlconf_service_creationclass
+ conf_service['CreationClassName'] = (
+ self.data.ctrlconf_service_creationclass)
conf_services.append(conf_service)
+ conf_service1 = {}
+ conf_service1['SystemName'] = self.data.storage_system_v3
+ conf_service1['CreationClassName'] = (
+ self.data.ctrlconf_service_creationclass)
+ conf_services.append(conf_service1)
return conf_services
def _enum_elemcompsvcs(self):
comp_services = []
comp_service = {}
comp_service['SystemName'] = self.data.storage_system
- comp_service['CreationClassName'] = \
- self.data.elementcomp_service_creationclass
+ comp_service['CreationClassName'] = (
+ self.data.elementcomp_service_creationclass)
comp_services.append(comp_service)
return comp_services
reloc_services = []
reloc_service = {}
reloc_service['SystemName'] = self.data.storage_system
- reloc_service['CreationClassName'] = \
- self.data.storreloc_service_creationclass
+ reloc_service['CreationClassName'] = (
+ self.data.storreloc_service_creationclass)
reloc_services.append(reloc_service)
return reloc_services
replic_services = []
replic_service = {}
replic_service['SystemName'] = self.data.storage_system
- replic_service['CreationClassName'] = \
- self.data.replication_service_creationclass
+ replic_service['CreationClassName'] = (
+ self.data.replication_service_creationclass)
replic_services.append(replic_service)
replic_service2 = {}
replic_service2['SystemName'] = self.data.storage_system_v3
def _enum_pools(self):
pools = []
pool = {}
- pool['InstanceID'] = self.data.storage_system + '+U+' + \
- self.data.storage_type
+ pool['InstanceID'] = (
+ self.data.storage_system + '+U+' + self.data.storage_type)
pool['CreationClassName'] = 'Symm_VirtualProvisioningPool'
pool['ElementName'] = 'gold'
pools.append(pool)
def _enum_pool_details(self):
pools = []
pool = {}
- pool['InstanceID'] = self.data.storage_system + '+U+' + \
- self.data.storage_type
+ pool['InstanceID'] = (
+ self.data.storage_system + '+U+' + self.data.storage_type)
pool['CreationClassName'] = 'Symm_VirtualProvisioningPool'
pool['TotalManagedSpace'] = 12345678
pool['RemainingManagedSpace'] = 123456
# Added vol to vol.path
failed_delete_vol['SystemCreationClassName'] = 'Symm_StorageSystem'
failed_delete_vol.path = failed_delete_vol
- failed_delete_vol.path.classname = \
- failed_delete_vol['CreationClassName']
+ failed_delete_vol.path.classname = (
+ failed_delete_vol['CreationClassName'])
vols.append(failed_delete_vol)
failed_vol = EMC_StorageVolume()
# Added vol to vol.path
failed_vol['SystemCreationClassName'] = 'Symm_StorageSystem'
failed_vol.path = failed_vol
- failed_vol.path.classname = \
- failed_vol['CreationClassName']
+ failed_vol.path.classname = failed_vol['CreationClassName']
name_failed = {}
name_failed['classname'] = 'Symm_StorageVolume'
vols.append(failed_vol)
- volumeHead = EMC_StorageVolume()
- volumeHead.classname = 'Symm_StorageVolume'
- blockSize = self.data.block_size
- volumeHead['ConsumableBlocks'] = (
- self.data.metaHead_volume['ConsumableBlocks'])
- volumeHead['BlockSize'] = blockSize
- volumeHead['DeviceID'] = self.data.metaHead_volume['DeviceID']
- vols.append(volumeHead)
-
- metaMember1 = EMC_StorageVolume()
- metaMember1.classname = 'Symm_StorageVolume'
- metaMember1['ConsumableBlocks'] = (
- self.data.meta_volume1['ConsumableBlocks'])
- metaMember1['BlockSize'] = blockSize
- metaMember1['DeviceID'] = self.data.meta_volume1['DeviceID']
- vols.append(metaMember1)
-
- metaMember2 = EMC_StorageVolume()
- metaMember2.classname = 'Symm_StorageVolume'
- metaMember2['ConsumableBlocks'] = (
- self.data.meta_volume2['ConsumableBlocks'])
- metaMember2['BlockSize'] = blockSize
- metaMember2['DeviceID'] = self.data.meta_volume2['DeviceID']
- vols.append(metaMember2)
-
return vols
def _enum_initiatorMaskingGroup(self):
svInstances.append(svInstance)
return svInstances
- def _enum_sw_identity(self):
- swIdentities = []
- swIdentity = {}
- swIdentity['MajorVersion'] = self.data.majorVersion
- swIdentity['MinorVersion'] = self.data.minorVersion
- swIdentity['RevisionNumber'] = self.data.revNumber
- swIdentities.append(swIdentity)
- return swIdentities
-
def _default_enum(self):
names = []
name = {}
self.tempdir = tempfile.mkdtemp()
super(EMCVMAXISCSIDriverNoFastTestCase, self).setUp()
self.config_file_path = None
- self.config_file_1364232 = None
self.create_fake_config_file_no_fast()
self.addCleanup(self._cleanup)
doc.writexml(f)
f.close()
- def create_fake_config_file_no_fast_with_add_ons(self):
+ def create_fake_config_file_no_fast_with_interval_retries(self):
doc = minidom.Document()
emc = doc.createElement("EMC")
doc.appendChild(emc)
doc = self.add_array_info(doc, emc)
doc = self.add_interval_and_retries(doc, emc)
- filename = 'cinder_emc_config_ISCSINoFAST.xml'
- self.config_file_path = self.tempdir + '/' + filename
+ filename = 'cinder_emc_config_ISCSINoFAST_int_ret.xml'
+ config_file_path = self.tempdir + '/' + filename
f = open(self.config_file_path, 'w')
doc.writexml(f)
f.close()
+ return config_file_path
def create_fake_config_file_no_fast_with_interval(self):
doc.appendChild(emc)
doc = self.add_array_info(doc, emc)
doc = self.add_interval_only(doc, emc)
- filename = 'cinder_emc_config_ISCSINoFAST.xml'
- self.config_file_path = self.tempdir + '/' + filename
+ filename = 'cinder_emc_config_ISCSINoFAST_int.xml'
+ config_file_path = self.tempdir + '/' + filename
f = open(self.config_file_path, 'w')
doc.writexml(f)
f.close()
+ return config_file_path
def create_fake_config_file_no_fast_with_retries(self):
doc.appendChild(emc)
doc = self.add_array_info(doc, emc)
doc = self.add_retries_only(doc, emc)
- filename = 'cinder_emc_config_ISCSINoFAST.xml'
- self.config_file_path = self.tempdir + '/' + filename
+ filename = 'cinder_emc_config_ISCSINoFAST_ret.xml'
+ config_file_path = self.tempdir + '/' + filename
f = open(self.config_file_path, 'w')
doc.writexml(f)
f.close()
+ return config_file_path
def add_array_info(self, doc, emc):
array = doc.createElement("Array")
pool.appendChild(pooltext)
array = doc.createElement("Array")
- arraytext = doc.createTextNode("0123456789")
+ arraytext = doc.createTextNode("1234567891011")
emc.appendChild(array)
array.appendChild(arraytext)
# fix for https://bugs.launchpad.net/cinder/+bug/1364232
def create_fake_config_file_1364232(self):
filename = 'cinder_emc_config_1364232.xml'
- self.config_file_1364232 = self.tempdir + '/' + filename
- text_file = open(self.config_file_1364232, "w")
+ config_file_1364232 = self.tempdir + '/' + filename
+ text_file = open(config_file_1364232, "w")
text_file.write("<?xml version='1.0' encoding='UTF-8'?>\n<EMC>\n"
"<EcomServerIp>10.10.10.10</EcomServerIp>\n"
"<EcomServerPort>5988</EcomServerPort>\n"
"</Pool>\n<FastPolicy>SILVER1</FastPolicy>\n"
"</EMC>")
text_file.close()
+ return config_file_1364232
def fake_ecom_connection(self):
conn = FakeEcomConnection()
self.assertEqual(storageHardwareIDInstanceNames[0],
self.data.iscsi_initiator)
- def test_get_pool_instance_and_system_name(self):
- conn = self.fake_ecom_connection()
- # V2 - old '+' separator
- storagesystem = {}
- storagesystem['SystemName'] = self.data.storage_system
- storagesystem['Name'] = self.data.storage_system
- pools = conn.EnumerateInstanceNames("EMC_VirtualProvisioningPool")
- poolname = 'gold'
- poolinstancename, systemname = (
- self.driver.common.utils._get_pool_instance_and_system_name(
- conn, pools, storagesystem, poolname))
- self.assertEqual(self.data.storage_system, systemname)
- self.assertEqual(self.data.storagepoolid,
- poolinstancename['InstanceID'])
- # V3 - note: V2 can also have the '-+-' separator
- storagesystem = {}
- storagesystem['SystemName'] = self.data.storage_system_v3
- storagesystem['Name'] = self.data.storage_system_v3
- pools = conn.EnumerateInstanceNames('Symm_SRPStoragePool')
- poolname = 'SRP_1'
- poolinstancename, systemname = (
- self.driver.common.utils._get_pool_instance_and_system_name(
- conn, pools, storagesystem, poolname))
- self.assertEqual(self.data.storage_system_v3, systemname)
- self.assertEqual('SYMMETRIX-+-000197200056-+-SRP_1',
- poolinstancename['InstanceID'])
- # Invalid poolname
- poolname = 'bogus'
- poolinstancename, systemname = (
- self.driver.common.utils._get_pool_instance_and_system_name(
- conn, pools, storagesystem, poolname))
- self.assertIsNone(poolinstancename)
- self.assertEqual(self.data.storage_system_v3, systemname)
+ def test_format_system_name(self):
+ v2array = ['SYMMETRIX', '000195900551', 'U', 'gold']
+ systemnameV2 = self.driver.utils._format_system_name(v2array[0],
+ v2array[1],
+ '+')
+ self.assertEqual('SYMMETRIX+000195900551', systemnameV2)
+
+ v3array = ['SYMMETRIX', '000197200056', 'SRP_1']
+ systemnameV3 = self.driver.utils._format_system_name(v3array[0],
+ v3array[1],
+ '-+-')
+ self.assertEqual('SYMMETRIX-+-000197200056', systemnameV3)
def test_get_hardware_type(self):
iqn_initiator = 'iqn.1992-04.com.emc: 50000973f006dd80'
def test_wait_for_sync(self):
mysync = 'fakesync'
conn = self.fake_ecom_connection()
+
self.driver.utils._is_sync_complete = mock.Mock(
return_value=True)
rc = self.driver.utils.wait_for_sync(conn, mysync)
def test_wait_for_sync_extra_specs(self):
mysync = 'fakesync'
conn = self.fake_ecom_connection()
- self.create_fake_config_file_no_fast_with_add_ons()
+ file_name = (
+ self.create_fake_config_file_no_fast_with_interval_retries())
extraSpecs = {'volume_backend_name': 'ISCSINoFAST'}
- extraSpecs = (
- self.driver.common._get_job_extra_specs(self.config_file_path,
- extraSpecs))
+ pool = 'gold+1234567891011'
+ arrayInfo = self.driver.utils.parse_file_to_get_array_map(
+ self.config_file_path)
+ poolRec = self.driver.utils.extract_record(arrayInfo, pool)
+ extraSpecs = self.driver.common._set_v2_extra_specs(extraSpecs,
+ poolRec)
self.driver.utils._is_sync_complete = mock.Mock(
return_value=True)
mock.ANY)
loopingcall.FixedIntervalLoopingCall.reset_mock()
loopingcall.FixedIntervalLoopingCall = loopingcall_orig
+ bExists = os.path.exists(file_name)
+ if bExists:
+ os.remove(file_name)
# Bug 1395830: _find_lun throws exception when lun is not found.
def test_find_lun(self):
volume2 = EMC_StorageVolume()
volume2['name'] = 'myVol'
volume2['provider_location'] = six.text_type(provider_location2)
- verify_orig = self.driver.common.conn.GetInstance
- self.driver.common.conn.GetInstance = mock.Mock(
+ verify_orig = self.driver.common.utils.get_existing_instance
+ self.driver.common.utils.get_existing_instance = mock.Mock(
return_value=None)
findlun2 = self.driver.common._find_lun(volume2)
# Not found.
self.assertIsNone(findlun2)
- self.driver.utils.get_instance_name(
+ instancename2 = self.driver.utils.get_instance_name(
provider_location2['classname'],
keybindings2)
- self.driver.common.conn.GetInstance.assert_called_once_with(
- keybindings2)
- self.driver.common.conn.GetInstance.reset_mock()
- self.driver.common.conn.GetInstance = verify_orig
+ self.driver.common.utils.get_existing_instance.assert_called_once_with(
+ self.driver.common.conn, instancename2)
+ self.driver.common.utils.get_existing_instance.reset_mock()
+ self.driver.common.utils.get_existing_instance = verify_orig
keybindings3 = {'CreationClassName': u'Symm_StorageVolume',
'SystemName': u'SYMMETRIX+000195900551',
# Bug 1403160 - make sure the masking view is cleanly deleted
def test_last_volume_delete_masking_view(self):
extraSpecs = {'volume_backend_name': 'ISCSINoFAST'}
- extraSpecs = (
- self.driver.common._get_job_extra_specs(self.config_file_path,
- extraSpecs))
conn = self.fake_ecom_connection()
controllerConfigService = (
self.driver.utils.find_controller_configuration_service(
volumeInstanceName = (
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
volumeName = "1403160-Vol"
- extraSpecs = {'volume_backend_name': 'ISCSINoFAST'}
- extraSpecs = (
- self.driver.common._get_job_extra_specs(self.config_file_path,
- extraSpecs))
+ extraSpecs = {'volume_backend_name': 'GOLD_BE',
+ 'isV3': False}
# Deleting Storage Group failed
self.assertRaises(
self.assertIsNone(foundPoolInstanceName2)
def test_get_volume_stats_1364232(self):
- self.create_fake_config_file_1364232()
+ file_name = self.create_fake_config_file_1364232()
+
+ arrayInfo = self.driver.utils.parse_file_to_get_array_map(file_name)
self.assertEqual(
- '000198700439',
- self.driver.utils.parse_array_name_from_file(
- self.config_file_1364232))
+ '000198700439', arrayInfo[0]['SerialNumber'])
self.assertEqual(
- 'FC_SLVR1',
- self.driver.utils.parse_pool_name_from_file(
- self.config_file_1364232))
+ 'FC_SLVR1', arrayInfo[0]['PoolName'])
self.assertEqual(
- 'SILVER1',
- self.driver.utils.parse_fast_policy_name_from_file(
- self.config_file_1364232))
+ 'SILVER1', arrayInfo[0]['FastPolicy'])
self.assertTrue(
- 'OS-PORTGROUP' in
- self.driver.utils.parse_file_to_get_port_group_name(
- self.config_file_1364232))
+ 'OS-PORTGROUP' in arrayInfo[0]['PortGroup'])
+ bExists = os.path.exists(file_name)
+ if bExists:
+ os.remove(file_name)
def test_intervals_and_retries_override(
self):
- self.create_fake_config_file_no_fast_with_add_ons()
+ file_name = (
+ self.create_fake_config_file_no_fast_with_interval_retries())
extraSpecs = {'volume_backend_name': 'ISCSINoFAST'}
- extraSpecs = (
- self.driver.common._get_job_extra_specs(self.config_file_path,
- extraSpecs))
+ pool = 'gold+1234567891011'
+ arrayInfo = self.driver.utils.parse_file_to_get_array_map(
+ self.config_file_path)
+ poolRec = self.driver.utils.extract_record(arrayInfo, pool)
+ extraSpecs = self.driver.common._set_v2_extra_specs(extraSpecs,
+ poolRec)
self.assertEqual(40,
self.driver.utils._get_max_job_retries(extraSpecs))
self.assertEqual(5,
self.driver.utils._get_interval_in_secs(extraSpecs))
+ bExists = os.path.exists(file_name)
+ if bExists:
+ os.remove(file_name)
+
def test_intervals_and_retries_default(self):
extraSpecs = {'volume_backend_name': 'ISCSINoFAST'}
- extraSpecs = (
- self.driver.common._get_job_extra_specs(self.config_file_path,
- extraSpecs))
+ pool = 'gold+1234567891011'
+ arrayInfo = self.driver.utils.parse_file_to_get_array_map(
+ self.config_file_path)
+ poolRec = self.driver.utils.extract_record(arrayInfo, pool)
+ extraSpecs = self.driver.common._set_v2_extra_specs(extraSpecs,
+ poolRec)
self.assertEqual(60,
self.driver.utils._get_max_job_retries(extraSpecs))
self.assertEqual(10,
def test_interval_only(self):
extraSpecs = {'volume_backend_name': 'ISCSINoFAST'}
- self.create_fake_config_file_no_fast_with_interval()
- extraSpecs = (
- self.driver.common._get_job_extra_specs(self.config_file_path,
- extraSpecs))
+ file_name = self.create_fake_config_file_no_fast_with_interval()
+ pool = 'gold+1234567891011'
+ arrayInfo = self.driver.utils.parse_file_to_get_array_map(
+ self.config_file_path)
+ poolRec = self.driver.utils.extract_record(arrayInfo, pool)
+ extraSpecs = self.driver.common._set_v2_extra_specs(extraSpecs,
+ poolRec)
self.assertEqual(60,
self.driver.utils._get_max_job_retries(extraSpecs))
self.assertEqual(20,
self.driver.utils._get_interval_in_secs(extraSpecs))
+ bExists = os.path.exists(file_name)
+ if bExists:
+ os.remove(file_name)
+
def test_retries_only(self):
extraSpecs = {'volume_backend_name': 'ISCSINoFAST'}
- self.create_fake_config_file_no_fast_with_retries()
- extraSpecs = (
- self.driver.common._get_job_extra_specs(self.config_file_path,
- extraSpecs))
+ file_name = self.create_fake_config_file_no_fast_with_retries()
+ pool = 'gold+1234567891011'
+ arrayInfo = self.driver.utils.parse_file_to_get_array_map(
+ self.config_file_path)
+ poolRec = self.driver.utils.extract_record(arrayInfo, pool)
+ extraSpecs = self.driver.common._set_v2_extra_specs(extraSpecs,
+ poolRec)
self.assertEqual(70,
self.driver.utils._get_max_job_retries(extraSpecs))
self.assertEqual(10,
self.driver.utils._get_interval_in_secs(extraSpecs))
+ bExists = os.path.exists(file_name)
+ if bExists:
+ os.remove(file_name)
+
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'isArrayV3',
return_value=False)
- @mock.patch.object(
- emc_vmax_utils.EMCVMAXUtils,
- 'parse_array_name_from_file',
- return_value="123456789")
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_pool_capacities',
mock_storage_system,
mock_is_fast_enabled,
mock_capacity,
- mock_array,
mock_is_v3):
self.driver.get_volume_stats(True)
notfound_delete_vol['SystemCreationClassName'] = 'Symm_StorageSystem'
notfound_delete_vol['volume_type_id'] = 'abc'
notfound_delete_vol['provider_location'] = None
+ notfound_delete_vol['host'] = self.data.fake_host
name = {}
name['classname'] = 'Symm_StorageVolume'
keys = {}
keys['CreationClassName'] = notfound_delete_vol['CreationClassName']
keys['SystemName'] = notfound_delete_vol['SystemName']
keys['DeviceID'] = notfound_delete_vol['DeviceID']
- keys['SystemCreationClassName'] = \
- notfound_delete_vol['SystemCreationClassName']
+ keys['SystemCreationClassName'] = (
+ notfound_delete_vol['SystemCreationClassName'])
name['keybindings'] = keys
self.driver.delete_volume(notfound_delete_vol)
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
- 'get_meta_members_capacity_in_byte',
+ 'get_meta_members_capacity_in_bit',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
- 'get_meta_members_capacity_in_byte',
+ 'get_meta_members_capacity_in_bit',
return_value=[1234567])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
- 'get_meta_members_capacity_in_byte',
+ 'get_meta_members_capacity_in_bit',
return_value=[1234567, 7654321])
@mock.patch.object(
FakeDB,
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
- 'get_meta_members_capacity_in_byte',
+ 'get_meta_members_capacity_in_bit',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
conn, volumeInstance, originalName)
self.assertEqual(originalName, volumeInstance['ElementName'])
- def test_get_smi_version(self):
- conn = self.fake_ecom_connection()
- utils = self.driver.common.utils
- version = utils.get_smi_version(conn)
- expected = int(str(self.data.majorVersion)
- + str(self.data.minorVersion)
- + str(self.data.revNumber))
- self.assertEqual(version, expected)
-
- def test_get_pool_name(self):
- conn = self.fake_ecom_connection()
- utils = self.driver.common.utils
- poolInstanceName = {}
- poolInstanceName['InstanceID'] = "SATA_GOLD1"
- poolInstanceName['CreationClassName'] = 'Symm_VirtualProvisioningPool'
- poolName = utils._get_pool_name(conn, poolInstanceName)
- self.assertEqual(poolName, self.data.poolname)
-
- def test_get_meta_members_capacity_in_byte(self):
- conn = self.fake_ecom_connection()
- utils = self.driver.common.utils
- memberVolumeInstanceNames = []
- volumeHead = EMC_StorageVolume()
- volumeHead.classname = 'Symm_StorageVolume'
- blockSize = self.data.block_size
- volumeHead['ConsumableBlocks'] = (
- self.data.metaHead_volume['ConsumableBlocks'])
- volumeHead['BlockSize'] = blockSize
- volumeHead['DeviceID'] = self.data.metaHead_volume['DeviceID']
- memberVolumeInstanceNames.append(volumeHead)
- metaMember1 = EMC_StorageVolume()
- metaMember1.classname = 'Symm_StorageVolume'
- metaMember1['ConsumableBlocks'] = (
- self.data.meta_volume1['ConsumableBlocks'])
- metaMember1['BlockSize'] = blockSize
- metaMember1['DeviceID'] = self.data.meta_volume1['DeviceID']
- memberVolumeInstanceNames.append(metaMember1)
- metaMember2 = EMC_StorageVolume()
- metaMember2.classname = 'Symm_StorageVolume'
- metaMember2['ConsumableBlocks'] = (
- self.data.meta_volume2['ConsumableBlocks'])
- metaMember2['BlockSize'] = blockSize
- metaMember2['DeviceID'] = self.data.meta_volume2['DeviceID']
- memberVolumeInstanceNames.append(metaMember2)
- capacities = utils.get_meta_members_capacity_in_byte(
- conn, memberVolumeInstanceNames)
- headSize = (
- volumeHead['ConsumableBlocks'] -
- metaMember1['ConsumableBlocks'] -
- metaMember2['ConsumableBlocks'])
- expected = [headSize * blockSize,
- metaMember1['ConsumableBlocks'] * blockSize,
- metaMember2['ConsumableBlocks'] * blockSize]
- self.assertEqual(capacities, expected)
-
- def test_get_composite_elements(self):
- conn = self.fake_ecom_connection()
- utils = self.driver.common.utils
- volumeInstanceName = (
- conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
- volumeInstance = conn.GetInstance(volumeInstanceName)
- memberVolumeInstanceNames = utils.get_composite_elements(
- conn, volumeInstance)
- expected = [self.data.metaHead_volume,
- self.data.meta_volume1,
- self.data.meta_volume2]
- self.assertEqual(memberVolumeInstanceNames, expected)
-
def _cleanup(self):
if self.config_file_path:
bExists = os.path.exists(self.config_file_path)
if bExists:
os.remove(self.config_file_path)
- if self.config_file_1364232:
- bExists = os.path.exists(self.config_file_1364232)
- if bExists:
- os.remove(self.config_file_1364232)
shutil.rmtree(self.tempdir)
emc.appendChild(pool)
pool.appendChild(pooltext)
- array = doc.createElement("Array")
- arraytext = doc.createTextNode("0123456789")
- emc.appendChild(array)
- array.appendChild(arraytext)
-
portgroups = doc.createElement("PortGroups")
portgroups.appendChild(portgroup)
emc.appendChild(portgroups)
def fake_is_v3(self, conn, serialNumber):
return False
- @mock.patch.object(
- emc_vmax_utils.EMCVMAXUtils,
- 'parse_array_name_from_file',
- return_value="123456789")
@mock.patch.object(
emc_vmax_fast.EMCVMAXFast,
'get_capacities_associated_to_policy',
mock_storage_system,
mock_is_fast_enabled,
mock_get_policy,
- mock_capacity,
- mock_array):
+ mock_capacity):
self.driver.get_volume_stats(True)
@mock.patch.object(
notfound_delete_vol['SystemName'] = self.data.storage_system
notfound_delete_vol['DeviceID'] = notfound_delete_vol['id']
notfound_delete_vol['SystemCreationClassName'] = 'Symm_StorageSystem'
+ notfound_delete_vol['host'] = self.data.fake_host
name = {}
name['classname'] = 'Symm_StorageVolume'
keys = {}
keys['CreationClassName'] = notfound_delete_vol['CreationClassName']
keys['SystemName'] = notfound_delete_vol['SystemName']
keys['DeviceID'] = notfound_delete_vol['DeviceID']
- keys['SystemCreationClassName'] = \
- notfound_delete_vol['SystemCreationClassName']
+ keys['SystemCreationClassName'] = (
+ notfound_delete_vol['SystemCreationClassName'])
name['keybindings'] = keys
notfound_delete_vol['volume_type_id'] = 'abc'
notfound_delete_vol['provider_location'] = None
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
- 'get_meta_members_capacity_in_byte',
+ 'get_meta_members_capacity_in_bit',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
- 'get_meta_members_capacity_in_byte',
+ 'get_meta_members_capacity_in_bit',
return_value=[1234567])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
- 'get_meta_members_capacity_in_byte',
+ 'get_meta_members_capacity_in_bit',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
emc.appendChild(pool)
pool.appendChild(pooltext)
- array = doc.createElement("Array")
- arraytext = doc.createTextNode("0123456789")
- emc.appendChild(array)
- array.appendChild(arraytext)
-
timeout = doc.createElement("Timeout")
timeouttext = doc.createTextNode("0")
emc.appendChild(timeout)
def fake_is_v3(self, conn, serialNumber):
return False
- @mock.patch.object(
- emc_vmax_utils.EMCVMAXUtils,
- 'isArrayV3',
- return_value=False)
- @mock.patch.object(
- emc_vmax_utils.EMCVMAXUtils,
- 'parse_array_name_from_file',
- return_value="123456789")
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_pool_capacities',
def test_get_volume_stats_no_fast(self,
mock_storage_system,
mock_is_fast_enabled,
- mock_capacity,
- mock_array,
- mock_is_v3):
+ mock_capacity):
self.driver.get_volume_stats(True)
@mock.patch.object(
notfound_delete_vol['SystemName'] = self.data.storage_system
notfound_delete_vol['DeviceID'] = notfound_delete_vol['id']
notfound_delete_vol['SystemCreationClassName'] = 'Symm_StorageSystem'
+ notfound_delete_vol['host'] = self.data.fake_host
name = {}
name['classname'] = 'Symm_StorageVolume'
keys = {}
keys['CreationClassName'] = notfound_delete_vol['CreationClassName']
keys['SystemName'] = notfound_delete_vol['SystemName']
keys['DeviceID'] = notfound_delete_vol['DeviceID']
- keys['SystemCreationClassName'] = \
- notfound_delete_vol['SystemCreationClassName']
+ keys['SystemCreationClassName'] = (
+ notfound_delete_vol['SystemCreationClassName'])
name['keybindings'] = keys
notfound_delete_vol['volume_type_id'] = 'abc'
notfound_delete_vol['provider_location'] = None
self.driver.delete_cgsnapshot(
self.data.test_ctxt, self.data.test_CG_snapshot)
- def create_fake_config_file_parse_port_group(self):
- filename = 'cinder_emc_config_file_parse_port_group.xml'
- self.config_file_parse_port_group = self.tempdir + '/' + filename
- text_file = open(self.config_file_parse_port_group, "w")
- text_file.write(
- "<?xml version='1.0' encoding='UTF-8'?>\n<EMC>\n"
- "<EcomServerIp>10.108.246.202</EcomServerIp>\n"
- "<EcomServerPort>5988</EcomServerPort>\n"
- "<EcomUserName>admin\t</EcomUserName>\n"
- "<EcomPassword>#1Password</EcomPassword>\n"
- "<PortGroups><PortGroup>OS-PORTGROUP1-PG\r\n"
- "</PortGroup>\n"
- "<PortGroup></PortGroup>\n"
- "<PortGroup> </PortGroup>\n"
- "<PortGroup></PortGroup>\n"
- "</PortGroups>\n<Array>000198700439"
- " \n</Array>\n<Pool>FC_SLVR1\n"
- "</Pool>\n<FastPolicy>SILVER1</FastPolicy>\n"
- "</EMC>")
- text_file.close()
-
- def test_get_port_group_parser(self):
- self.create_fake_config_file_parse_port_group()
- for _var in range(0, 10):
- self.assertEqual(
- u'OS-PORTGROUP1-PG',
- self.driver.utils.parse_file_to_get_port_group_name(
- self.config_file_parse_port_group))
- bExists = os.path.exists(self.config_file_parse_port_group)
- if bExists:
- os.remove(self.config_file_parse_port_group)
-
def test_manage_existing_get_size(self):
volume = {}
metadata = {'key': 'array',
'status': 'available',
'host': self.data.fake_host,
'NumberOfBlocks': 100,
- 'BlockSize': self.data.block_size
+ 'BlockSize': 512
}
common = self.driver.common
common._initial_setup = mock.Mock(
'status': 'available',
'host': self.data.fake_host,
'NumberOfBlocks': 100,
- 'BlockSize': self.data.block_size
+ 'BlockSize': 512
}
common = self.driver.common
common._initial_setup = mock.Mock(
pool.appendChild(pooltext)
array = doc.createElement("Array")
- arraytext = doc.createTextNode("0123456789")
+ arraytext = doc.createTextNode("1234567891011")
emc.appendChild(array)
array.appendChild(arraytext)
def fake_is_v3(self, conn, serialNumber):
return False
- @mock.patch.object(
- emc_vmax_utils.EMCVMAXUtils,
- 'parse_array_name_from_file',
- return_value="123456789")
@mock.patch.object(
emc_vmax_fast.EMCVMAXFast,
'get_capacities_associated_to_policy',
mock_storage_system,
mock_is_fast_enabled,
mock_get_policy,
- mock_capacity,
- mock_array):
+ mock_capacity):
self.driver.get_volume_stats(True)
@mock.patch.object(
notfound_delete_vol['SystemName'] = self.data.storage_system
notfound_delete_vol['DeviceID'] = notfound_delete_vol['id']
notfound_delete_vol['SystemCreationClassName'] = 'Symm_StorageSystem'
+ notfound_delete_vol['host'] = self.data.fake_host
name = {}
name['classname'] = 'Symm_StorageVolume'
keys = {}
keys['CreationClassName'] = notfound_delete_vol['CreationClassName']
keys['SystemName'] = notfound_delete_vol['SystemName']
keys['DeviceID'] = notfound_delete_vol['DeviceID']
- keys['SystemCreationClassName'] = \
- notfound_delete_vol['SystemCreationClassName']
+ keys['SystemCreationClassName'] = (
+ notfound_delete_vol['SystemCreationClassName'])
name['keybindings'] = keys
notfound_delete_vol['volume_type_id'] = 'abc'
notfound_delete_vol['provider_location'] = None
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
- 'get_meta_members_capacity_in_byte',
+ 'get_meta_members_capacity_in_bit',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
mock.Mock(return_value=True))
self.driver.create_snapshot(self.data.test_volume)
- 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,
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
- 'get_meta_members_capacity_in_byte',
+ 'get_meta_members_capacity_in_bit',
return_value=[1234567])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
- 'get_meta_members_capacity_in_byte',
+ 'get_meta_members_capacity_in_bit',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
self.tempdir = tempfile.mkdtemp()
super(EMCV3DriverTestCase, self).setUp()
self.config_file_path = None
- self.create_fake_config_file_fast()
+ self.create_fake_config_file_v3()
self.addCleanup(self._cleanup)
+ self.set_configuration()
+ def set_configuration(self):
configuration = mock.Mock()
configuration.cinder_emc_config_file = self.config_file_path
configuration.safe_get.return_value = 'V3'
driver.db = FakeDB()
self.driver = driver
- def create_fake_config_file_fast(self):
+ def create_fake_config_file_v3(self):
doc = minidom.Document()
emc = doc.createElement("EMC")
pool.appendChild(pooltext)
array = doc.createElement("Array")
- arraytext = doc.createTextNode("0123456789")
+ arraytext = doc.createTextNode("1234567891011")
emc.appendChild(array)
array.appendChild(arraytext)
def fake_is_v3(self, conn, serialNumber):
return True
+ def default_extraspec(self):
+ return {'storagetype:pool': 'SRP_1',
+ 'volume_backend_name': 'V3_BE',
+ 'storagetype:workload': 'DSS',
+ 'storagetype:slo': 'Bronze',
+ 'storagetype:array': '1234567891011',
+ 'isV3': True,
+ 'portgroupname': 'OS-portgroup-PG'}
+
+ def test_initial_setup(self):
+ self.driver.common._register_config_file_from_config_group = (
+ mock.Mock(return_value=self.config_file_path))
+ extraSpecs = (
+ self.driver.common._initial_setup(self.data.test_volume_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'])
+
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'isArrayV3',
return_value=True)
- @mock.patch.object(
- emc_vmax_utils.EMCVMAXUtils,
- 'parse_array_name_from_file',
- return_value="123456789")
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'find_storageSystem',
return_value=None)
def test_get_volume_stats_v3(
- self, mock_storage_system, mock_array, mock_is_v3):
+ self, mock_storage_system, mock_is_v3):
self.driver.get_volume_stats(True)
- @mock.patch.object(
- emc_vmax_provision_v3.EMCVMAXProvisionV3,
- '_get_supported_size_range_for_SLO',
- return_value={'MaximumVolumeSize': '30000000000',
- 'MinimumVolumeSize': '100000'})
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_get_pool_and_storage_system',
'get_volume_type_extra_specs',
return_value={'volume_backend_name': 'V3_BE'})
def test_create_volume_v3_success(
- self, _mock_volume_type, mock_storage_system, mock_range):
+ self, _mock_volume_type, mock_storage_system):
+ self.data.test_volume_v3['host'] = self.data.fake_host_v3
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
self.driver.create_volume(self.data.test_volume_v3)
- @mock.patch.object(
- emc_vmax_utils.EMCVMAXUtils,
- 'parse_slo_from_file',
- return_value='NONE')
- @mock.patch.object(
- emc_vmax_provision_v3.EMCVMAXProvisionV3,
- '_get_supported_size_range_for_SLO',
- return_value={'MaximumVolumeSize': '30000000000',
- 'MinimumVolumeSize': '100000'})
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_get_pool_and_storage_system',
'get_volume_type_extra_specs',
return_value={'volume_backend_name': 'V3_BE'})
def test_create_volume_v3_no_slo_success(
- self, _mock_volume_type, mock_storage_system,
- mock_range, mock_slo):
- self.driver.create_volume(self.data.test_volume_v3)
+ self, _mock_volume_type, mock_storage_system):
+ v3_vol = self.data.test_volume_v3
+ v3_vol['host'] = 'HostX@Backend#NONE+SRP_1+1234567891011'
+ extraSpecs = {'storagetype:pool': 'SRP_1',
+ 'volume_backend_name': 'V3_BE',
+ 'storagetype:workload': 'DSS',
+ 'storagetype:slo': 'NONE',
+ 'storagetype:array': '1234567891011',
+ 'isV3': True,
+ 'portgroupname': 'OS-portgroup-PG'}
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=extraSpecs)
+
+ self.driver.create_volume(v3_vol)
- @mock.patch.object(
- emc_vmax_utils.EMCVMAXUtils,
- 'parse_slo_from_file',
- return_value='Bogus')
- @mock.patch.object(
- emc_vmax_provision_v3.EMCVMAXProvisionV3,
- '_get_supported_size_range_for_SLO',
- return_value={'MaximumVolumeSize': '30000000000',
- 'MinimumVolumeSize': '100000'})
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_get_pool_and_storage_system',
'get_volume_type_extra_specs',
return_value={'volume_backend_name': 'V3_BE'})
def test_create_volume_v3_invalid_slo_failed(
- self, _mock_volume_type, mock_storage_system,
- mock_range, mock_slo):
+ self, _mock_volume_type, mock_storage_system):
+ extraSpecs = {'storagetype:pool': 'SRP_1',
+ 'volume_backend_name': 'V3_BE',
+ 'storagetype:workload': 'DSS',
+ 'storagetype:slo': 'Bogus',
+ 'storagetype:array': '1234567891011',
+ 'isV3': True,
+ 'portgroupname': 'OS-portgroup-PG'}
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=extraSpecs)
+
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_volume,
self.data.test_volume)
- @mock.patch.object(
- emc_vmax_provision_v3.EMCVMAXProvisionV3,
- '_get_supported_size_range_for_SLO',
- return_value={'MaximumVolumeSize': '30000000000',
- 'MinimumVolumeSize': '100000'})
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_get_pool_and_storage_system',
'get_volume_type_extra_specs',
return_value={'volume_backend_name': 'V3_BE'})
def test_create_volume_in_CG_v3_success(
- self, _mock_volume_type, mock_storage_system, mock_range):
- self.driver.create_volume(self.data.test_volume_CG)
+ self, _mock_volume_type, mock_storage_system):
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
+ self.driver.create_volume(self.data.test_volume_CG_v3)
@mock.patch.object(
volume_types,
'get_volume_type_extra_specs',
return_value={'volume_backend_name': 'V3_BE'})
def test_delete_volume_v3_success(self, _mock_volume_type):
- self.driver.delete_volume(self.data.test_volume)
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
+ self.driver.delete_volume(self.data.test_volume_v3)
- @mock.patch.object(
- emc_vmax_provision_v3.EMCVMAXProvisionV3,
- '_get_supported_size_range_for_SLO',
- return_value={'MaximumVolumeSize': '30000000000',
- 'MinimumVolumeSize': '100000'})
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_get_pool_and_storage_system',
@mock.patch.object(
FakeDB,
'volume_get',
- return_value=EMCVMAXCommonData.test_source_volume)
+ return_value=EMCVMAXCommonData.test_source_volume_v3)
def test_create_snapshot_v3_success(
- self, mock_volume_db, mock_type, moke_pool, mock_siz):
- self.data.test_volume['volume_name'] = "vmax-1234567"
- self.driver.create_snapshot(self.data.test_volume)
+ self, mock_volume_db, mock_type, moke_pool):
+ self.data.test_volume_v3['volume_name'] = "vmax-1234567"
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
+ self.driver.create_snapshot(self.data.test_volume_v3)
@mock.patch.object(
FakeDB,
'volume_get',
- return_value=EMCVMAXCommonData.test_source_volume)
+ return_value=EMCVMAXCommonData.test_source_volume_v3)
@mock.patch.object(
volume_types,
'get_volume_type_extra_specs',
return_value={'volume_backend_name': 'V3_BE'})
def test_delete_snapshot_v3_success(self, mock_volume_type, mock_db):
- self.data.test_volume['volume_name'] = "vmax-1234567"
- self.driver.delete_snapshot(self.data.test_volume)
+ self.data.test_volume_v3['volume_name'] = "vmax-1234567"
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
+ self.driver.delete_snapshot(self.data.test_volume_v3)
- @mock.patch.object(
- emc_vmax_provision_v3.EMCVMAXProvisionV3,
- '_get_supported_size_range_for_SLO',
- return_value={'MaximumVolumeSize': '30000000000',
- 'MinimumVolumeSize': '100000'})
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_get_pool_and_storage_system',
'volume_get',
return_value=EMCVMAXCommonData.test_source_volume)
def test_create_cloned_volume_v3_success(
- self, mock_volume_db, mock_type, moke_pool, mock_size):
- self.data.test_volume['volume_name'] = "vmax-1234567"
+ self, mock_volume_db, mock_type, moke_pool):
+ self.data.test_volume_v3['volume_name'] = "vmax-1234567"
cloneVol = {}
cloneVol['name'] = 'vol1'
cloneVol['id'] = '10'
cloneVol['volume_type_id'] = 'abc'
cloneVol['provider_location'] = None
cloneVol['NumberOfBlocks'] = 100
- cloneVol['BlockSize'] = self.data.block_size
- self.driver.create_cloned_volume(cloneVol, self.data.test_volume)
+ cloneVol['BlockSize'] = 512
+ cloneVol['host'] = self.data.fake_host_v3
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
+ self.driver.create_cloned_volume(cloneVol, self.data.test_volume_v3)
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
def test_create_CG_v3_success(
self, _mock_volume_type, _mock_storage_system):
self.driver.create_consistencygroup(
- self.data.test_ctxt, self.data.test_CG)
+ self.data.test_ctxt, self.data.test_volume_CG_v3)
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'get_volume_type_extra_specs',
return_value={'volume_backend_name': 'V3_BE'})
def test_migrate_volume_v3_success(self, _mock_volume_type):
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
self.driver.migrate_volume(self.data.test_ctxt, self.data.test_volume,
self.data.test_host)
def test_retype_volume_v3_success(
self, _mock_volume_type, mock_fast_settings,
mock_storage_group, mock_found_SG):
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
self.assertTrue(self.driver.retype(
- self.data.test_ctxt, self.data.test_volume, self.data.new_type,
+ self.data.test_ctxt, self.data.test_volume_v3, self.data.new_type,
self.data.diff, self.data.test_host_v3))
@mock.patch.object(
return_value={'volume_backend_name': 'V3_BE'})
def test_retype_volume_same_host_failure(
self, _mock_volume_type, mock_fast_settings):
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
self.assertFalse(self.driver.retype(
- self.data.test_ctxt, self.data.test_volume, self.data.new_type,
+ self.data.test_ctxt, self.data.test_volume_v3, self.data.new_type,
self.data.diff, self.data.test_host_v3))
@mock.patch.object(
common = self.driver.common
common.get_target_wwns = mock.Mock(
return_value=EMCVMAXCommonData.target_wwns)
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
data = self.driver.initialize_connection(
- self.data.test_volume, self.data.connector)
+ self.data.test_volume_v3, self.data.connector)
# Test the no lookup service, pre-zoned case.
common.get_target_wwns.assert_called_once_with(
EMCVMAXCommonData.storage_system, EMCVMAXCommonData.connector)
'get_volume_type_extra_specs',
return_value={'volume_backend_name': 'V3_BE'})
def test_map_v3_failed(self, _mock_volume_type, mock_wrap_device):
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.initialize_connection,
self.data.test_volume,
common = self.driver.common
common.get_target_wwns = mock.Mock(
return_value=EMCVMAXCommonData.target_wwns)
- data = self.driver.terminate_connection(self.data.test_volume,
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
+ data = self.driver.terminate_connection(self.data.test_volume_v3,
self.data.connector)
common.get_target_wwns.assert_called_once_with(
EMCVMAXCommonData.storage_system, EMCVMAXCommonData.connector)
self.assertEqual(numTargetWwns, len(data['data']))
# Bug https://bugs.launchpad.net/cinder/+bug/1440154
- @mock.patch.object(
- emc_vmax_provision_v3.EMCVMAXProvisionV3,
- '_get_supported_size_range_for_SLO',
- return_value={'MaximumVolumeSize': '30000000000',
- 'MinimumVolumeSize': '100000'})
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_get_pool_and_storage_system',
@mock.patch.object(
FakeDB,
'volume_get',
- return_value=EMCVMAXCommonData.test_source_volume)
+ return_value=EMCVMAXCommonData.test_source_volume_v3)
@mock.patch.object(
emc_vmax_provision_v3.EMCVMAXProvisionV3,
'create_element_replica')
return_value=(None, None))
def test_create_clone_v3_assert_clean_up_target_volume(
self, mock_sync, mock_create_replica, mock_volume_db,
- mock_type, moke_pool, mock_size):
+ mock_type, moke_pool):
self.data.test_volume['volume_name'] = "vmax-1234567"
e = exception.VolumeBackendAPIException('CreateElementReplica Ex')
common = self.driver.common
common._create_v3_volume = (
mock.Mock(return_value=(0, volumeDict, self.data.storage_system)))
conn = self.fake_ecom_connection()
+ storageConfigService = []
storageConfigService = {}
storageConfigService['SystemName'] = EMCVMAXCommonData.storage_system
- storageConfigService['CreationClassName'] = \
- self.data.stconf_service_creationclass
+ storageConfigService['CreationClassName'] = (
+ self.data.stconf_service_creationclass)
common._delete_from_pool_v3 = mock.Mock(return_value=0)
mock_create_replica.side_effect = e
self.assertRaises(exception.VolumeBackendAPIException,
self.driver.create_cloned_volume,
- self.data.test_volume,
- EMCVMAXCommonData.test_source_volume)
- extraSpecs = common._initial_setup(self.data.test_volume)
+ self.data.test_volume_v3,
+ EMCVMAXCommonData.test_source_volume_v3)
+ extraSpecs = common._initial_setup(self.data.test_volume_v3)
targetInstance = (
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
storageGroupName = common.utils.get_v3_storage_group_name('SRP_1',
storageGroupName,
extraSpecs)
+ def test_get_remaining_slo_capacity_wlp(self):
+ conn = self.fake_ecom_connection()
+ array_info = {'Workload': u'DSS', 'SLO': u'Bronze'}
+ storagesystem = self.data.storage_system_v3
+ srpPoolInstanceName = {}
+ srpPoolInstanceName['InstanceID'] = (
+ self.data.storage_system_v3 + '+U+' + 'SRP_1')
+ srpPoolInstanceName['CreationClassName'] = (
+ 'Symm_VirtualProvisioningPool')
+ srpPoolInstanceName['ElementName'] = 'SRP_1'
+
+ remainingCapacityGb = (
+ self.driver.common.provisionv3._get_remaining_slo_capacity_wlp(
+ conn, srpPoolInstanceName, array_info, storagesystem))
+ remainingSLOCapacityGb = self.driver.common.utils.convert_bits_to_gbs(
+ self.data.remainingSLOCapacity)
+ self.assertEqual(remainingSLOCapacityGb, remainingCapacityGb)
+
+ def _cleanup(self):
+ bExists = os.path.exists(self.config_file_path)
+ if bExists:
+ os.remove(self.config_file_path)
+ shutil.rmtree(self.tempdir)
+
+
+class EMCV2MultiPoolDriverTestCase(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.stubs.Set(emc_vmax_iscsi.EMCVMAXISCSIDriver,
+ 'smis_do_iscsi_discovery',
+ self.fake_do_iscsi_discovery)
+ self.stubs.Set(emc_vmax_common.EMCVMAXCommon, '_get_ecom_connection',
+ self.fake_ecom_connection)
+ instancename = FakeCIMInstanceName()
+ self.stubs.Set(emc_vmax_utils.EMCVMAXUtils, 'get_instance_name',
+ instancename.fake_getinstancename)
+ self.stubs.Set(time, 'sleep',
+ self.fake_sleep)
+ self.stubs.Set(emc_vmax_utils.EMCVMAXUtils, 'isArrayV3',
+ self.fake_is_v3)
+
+ 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_do_iscsi_discovery(self, volume):
+ output = []
+ item = '10.10.0.50: 3260,1 iqn.1992-04.com.emc: 50000973f006dd80'
+ output.append(item)
+ return output
+
+ def fake_sleep(self, seconds):
+ return
+
+ 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_initial_setup(self):
+ self.driver.common._register_config_file_from_config_group = (
+ mock.Mock(return_value=self.config_file_path))
+ extraSpecs = self.driver.common._initial_setup(self.vol_v2)
+ self.assertEqual('gold', extraSpecs['storagetype:pool'])
+ self.assertEqual(None, extraSpecs['storagetype:fastpolicy'])
+ self.assertEqual('concatenated',
+ extraSpecs['storagetype:compositetype'])
+ self.assertEqual('1234567891011', extraSpecs['storagetype:array'])
+ self.assertEqual('OS-portgroup-PG', extraSpecs['portgroupname'])
+ self.assertFalse(extraSpecs['isV3'])
+
+ 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.assertTrue(len(arrayInfo) == 2)
+ for arrayInfoRec in arrayInfo:
+ self.assertEqual(
+ '1234567891011', arrayInfoRec['SerialNumber'])
+ self.assertTrue(
+ self.data.port_group in 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(
+ emc_vmax_common.EMCVMAXCommon,
+ '_get_members_of_replication_group',
+ return_value=None)
+ @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, _mock_members):
+ 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['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)
+ 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'
+
+ self.stubs.Set(emc_vmax_common.EMCVMAXCommon, '_get_ecom_connection',
+ self.fake_ecom_connection)
+ instancename = FakeCIMInstanceName()
+ self.stubs.Set(emc_vmax_utils.EMCVMAXUtils, 'get_instance_name',
+ instancename.fake_getinstancename)
+ self.stubs.Set(time, 'sleep',
+ self.fake_sleep)
+ self.stubs.Set(emc_vmax_utils.EMCVMAXUtils, 'isArrayV3',
+ self.fake_is_v3)
+
+ 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):
+ 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)
+
+ 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("SLO")
+ vpool.appendChild(poolslo)
+ poolsloText = doc.createTextNode("Bronze")
+ poolslo.appendChild(poolsloText)
+ poolworkload = doc.createElement("Workload")
+ vpool.appendChild(poolworkload)
+ poolworkloadText = doc.createTextNode("DSS")
+ poolworkload.appendChild(poolworkloadText)
+
+ 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("SLO")
+ vpool2.appendChild(pool2slo)
+ pool2sloText = doc.createTextNode("Silver")
+ pool2slo.appendChild(pool2sloText)
+ pool2workload = doc.createElement("Workload")
+ vpool.appendChild(pool2workload)
+ pool2workloadText = doc.createTextNode("OLTP")
+ pool2workload.appendChild(pool2workloadText)
+
+ filename = 'cinder_emc_config_MULTI_SLO_V3.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_sleep(self, seconds):
+ return
+
+ def fake_is_v3(self, conn, serialNumber):
+ return True
+
+ 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 test_initial_setup(self):
+ self.driver.common._register_config_file_from_config_group = (
+ mock.Mock(return_value=self.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'])
+
+ def test_validate_pool(self):
+ v3_valid_pool = self.data.test_volume_v3.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)
+
+ # Cannot get the pool from the host
+ v3_valid_pool['host'] = 'HostX@Backend'
+ self.assertRaises(exception.VolumeBackendAPIException,
+ self.driver.common._validate_pool,
+ v3_valid_pool)
+ # Legacy test. Provider Location does not have the version
+ v3_valid_pool['host'] = self.data.fake_host_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.assertTrue(len(arrayInfo) == 2)
+ for arrayInfoRec in arrayInfo:
+ self.assertEqual(
+ '1234567891011', arrayInfoRec['SerialNumber'])
+ self.assertTrue(
+ self.data.port_group in arrayInfoRec['PortGroup'])
+ self.assertTrue('SRP_1' in arrayInfoRec['PoolName'])
+ self.assertTrue(
+ 'Bronze' in arrayInfoRec['SLO'] or
+ 'Silver' in arrayInfoRec['SLO'])
+
+ @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.vol_v3['provider_location'] = None
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
+ self.driver.create_volume(self.vol_v3)
+
+ @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.driver.delete_volume(self.vol_v3)
+
+ @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.data.test_volume_CG_v3['provider_location'] = None
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
+ self.driver.create_volume(self.data.test_volume_CG_v3)
+
+ @mock.patch.object(
+ emc_vmax_provision_v3.EMCVMAXProvisionV3,
+ '_find_new_storage_group',
+ return_value='Any')
+ @mock.patch.object(
+ emc_vmax_utils.EMCVMAXUtils,
+ 'wrap_get_storage_group_from_volume',
+ return_value=None)
+ @mock.patch.object(
+ 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):
+ self.driver.common._initial_setup = mock.Mock(
+ return_value=self.default_extraspec())
+ 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))
+
+ @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.driver.create_consistencygroup(
+ self.data.test_ctxt, self.data.test_CG)
+
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_get_members_of_replication_group',
+ return_value=None)
+ @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, _mock_members):
+ 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.stubs.Set(emc_vmax_common.EMCVMAXCommon, '_get_ecom_connection',
+ self.fake_ecom_connection)
+ instancename = FakeCIMInstanceName()
+ self.stubs.Set(emc_vmax_utils.EMCVMAXUtils, 'get_instance_name',
+ instancename.fake_getinstancename)
+ self.stubs.Set(time, 'sleep',
+ self.fake_sleep)
+ self.stubs.Set(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_sleep(self, seconds):
+ return
+
+ 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.assertTrue(len(arrayInfo) == 4)
+ 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.assertEqual(None, 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.assertTrue(len(arrayInfo) == 4)
+ 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)
+
+ @mock.patch.object(
+ emc_vmax_common.EMCVMAXCommon,
+ '_get_members_of_replication_group',
+ return_value=None)
+ @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_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)
+
+ @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 _cleanup(self):
bExists = os.path.exists(self.config_file_path)
if bExists:
from cinder.volume.drivers.emc import emc_vmax_provision
from cinder.volume.drivers.emc import emc_vmax_provision_v3
from cinder.volume.drivers.emc import emc_vmax_utils
+from cinder.volume import utils as volume_utils
LOG = logging.getLogger(__name__)
CINDER_EMC_CONFIG_FILE = '/etc/cinder/cinder_emc_config.xml'
CINDER_EMC_CONFIG_FILE_PREFIX = '/etc/cinder/cinder_emc_config_'
CINDER_EMC_CONFIG_FILE_POSTFIX = '.xml'
+BACKENDNAME = 'volume_backend_name'
+PREFIXBACKENDNAME = 'capabilities:volume_backend_name'
+PORTGROUPNAME = 'portgroupname'
EMC_ROOT = 'root/emc'
POOL = 'storagetype:pool'
ARRAY = 'storagetype:array'
MEMBERCOUNT = 'storagetype:membercount'
STRIPED = 'striped'
CONCATENATED = 'concatenated'
-SMI_VERSION_8 = 800
# V3
SLO = 'storagetype:slo'
WORKLOAD = 'storagetype:workload'
'vendor_name': 'EMC',
'volume_backend_name': None}
- pool_info = {'pool_name': None,
- 'fast_policy': None,
- 'backend_name': None,
- 'serial_number': None,
- 'is_v3': False,
- 'config_file': None}
+ pool_info = {'backend_name': None,
+ 'config_file': None,
+ 'arrays_info': {}}
- def __init__(self, prtcl, configuration=None):
+ def __init__(self, prtcl, version, configuration=None):
if not pywbemAvailable:
LOG.info(_LI(
self.fast = emc_vmax_fast.EMCVMAXFast(prtcl)
self.provision = emc_vmax_provision.EMCVMAXProvision(prtcl)
self.provisionv3 = emc_vmax_provision_v3.EMCVMAXProvisionV3(prtcl)
+ self.version = version
self._gather_info()
def _gather_info(self):
{'emcConfigFileName': self.pool_info['config_file'],
'backendName': self.pool_info['backend_name']})
- if self.conn is None:
- self._set_ecom_credentials(self.pool_info['config_file'])
-
- self.pool_info['serial_number'] = (
- self.utils.parse_array_name_from_file(
+ self.pool_info['arrays_info'] = (
+ self.utils.parse_file_to_get_array_map(
self.pool_info['config_file']))
- if self.pool_info['serial_number'] is None:
- LOG.error(_LE(
- "Array Serial Number %(arrayName)s must be in the file "
- "%(emcConfigFileName)s."),
- {'arrayName': self.pool_info['serial_number'],
- 'emcConfigFileName': self.pool_info['config_file']})
-
- self.pool_info['pool_name'] = (
- self.utils.parse_pool_name_from_file(
- self.pool_info['config_file']))
- if self.pool_info['pool_name'] is None:
- LOG.error(_LE(
- "PoolName %(poolName)s must be in the file "
- "%(emcConfigFileName)s."),
- {'poolName': self.pool_info['pool_name'],
- 'emcConfigFileName': self.pool_info['config_file']})
-
- self.pool_info['is_v3'] = (
- self.utils.isArrayV3(self.conn, self.pool_info['serial_number']))
-
def create_volume(self, volume):
"""Creates a EMC(VMAX) volume from a pre-existing storage pool.
{'volumeName': volumeName,
'rc': rc,
'name': volumeDict})
+ # Adding version information
+ volumeDict['version'] = self.version
return volumeDict
:raises: VolumeBackendAPIException
"""
LOG.debug("Entering create_volume_from_snapshot.")
- extraSpecs = self._initial_setup(volume)
+ snapshot['host'] = volume['host']
+ extraSpecs = self._initial_setup(snapshot)
self.conn = self._get_ecom_connection()
snapshotInstance = self._find_lun(snapshot)
storageSystem = snapshotInstance['SystemName']
self.provision.delete_clone_relationship(
self.conn, repservice, syncName, extraSpecs)
- return self._create_cloned_volume(volume, snapshot, False)
+ snapshot['host'] = volume['host']
+ return self._create_cloned_volume(volume, snapshot, extraSpecs, False)
def create_cloned_volume(self, cloneVolume, sourceVolume):
"""Creates a clone of the specified volume.
:param sourceVolume: volume object
:returns: cloneVolumeDict -- the cloned volume dictionary
"""
- return self._create_cloned_volume(cloneVolume, sourceVolume, False)
+ extraSpecs = self._initial_setup(sourceVolume)
+ return self._create_cloned_volume(cloneVolume, sourceVolume,
+ extraSpecs, False)
def delete_volume(self, volume):
"""Deletes a EMC(VMAX) volume.
:param volume: volume Object to create snapshot from
:returns: dict -- the cloned volume dictionary
"""
- return self._create_cloned_volume(snapshot, volume, True)
+ extraSpecs = self._initial_setup(volume)
+ return self._create_cloned_volume(snapshot, volume, extraSpecs, True)
def delete_snapshot(self, snapshot, volume):
"""Deletes a snapshot.
"""
LOG.info(_LI("Delete Snapshot: %(snapshotName)s."),
{'snapshotName': snapshot['name']})
+ snapshot['host'] = volume['host']
self._delete_snapshot(snapshot)
def _remove_members(self, controllerConfigService,
def update_volume_stats(self):
"""Retrieve stats info."""
-
- if self.pool_info['is_v3']:
- location_info, total_capacity_gb, free_capacity_gb = (
- self._update_srp_stats(self.pool_info['config_file'],
- self.pool_info['serial_number'],
- self.pool_info['pool_name']))
- else:
- # This is V2.
- location_info, total_capacity_gb, free_capacity_gb = (
- self._update_pool_stats(self.pool_info['config_file'],
- self.pool_info['backend_name'],
- self.pool_info['serial_number'],
- self.pool_info['pool_name']))
-
- data = {'total_capacity_gb': total_capacity_gb,
- 'free_capacity_gb': free_capacity_gb,
- 'reserved_percentage': 0,
- 'QoS_support': False,
+ pools = []
+ backendName = self.pool_info['backend_name']
+ for arrayInfo in self.pool_info['arrays_info']:
+ self._set_ecom_credentials(arrayInfo)
+ # Check what type of array it is
+ isV3 = self.utils.isArrayV3(self.conn, arrayInfo['SerialNumber'])
+ if isV3:
+ location_info, total_capacity_gb, free_capacity_gb = (
+ self._update_srp_stats(arrayInfo))
+ poolName = ("%(slo)s+%(poolName)s+%(array)s"
+ % {'slo': arrayInfo['SLO'],
+ 'poolName': arrayInfo['PoolName'],
+ 'array': arrayInfo['SerialNumber']})
+ else:
+ # This is V2
+ location_info, total_capacity_gb, free_capacity_gb = (
+ self._update_pool_stats(backendName, arrayInfo))
+ poolName = ("%(poolName)s+%(array)s"
+ % {'poolName': arrayInfo['PoolName'],
+ 'array': arrayInfo['SerialNumber']})
+
+ pool = {'pool_name': poolName,
+ 'total_capacity_gb': total_capacity_gb,
+ 'free_capacity_gb': free_capacity_gb,
+ 'reserved_percentage': 0,
+ 'QoS_support': False,
+ 'location_info': location_info,
+ 'consistencygroup_support': True}
+ pools.append(pool)
+
+ data = {'vendor_name': "EMC",
+ 'driver_version': self.version,
+ 'storage_protocol': 'unknown',
'volume_backend_name': self.pool_info['backend_name'] or
self.__class__.__name__,
- 'vendor_name': "EMC",
- 'driver_version': self.VERSION,
- 'storage_protocol': 'unknown',
- 'location_info': location_info,
- 'consistencygroup_support': True}
+ # Use zero capacities here so we always use a pool.
+ 'total_capacity_gb': 0,
+ 'free_capacity_gb': 0,
+ 'reserved_percentage': 0,
+ 'pools': pools}
return data
- def _update_srp_stats(self, emcConfigFileName, arrayName, poolName):
+ def _update_srp_stats(self, arrayInfo):
"""Update SRP stats.
- :param emcConfigFileName: the EMC configuration file
- :param arrayName: the array
- :param poolName: the pool
+ :param arrayInfo: array information
:returns: location_info
:returns: totalManagedSpaceGbs
:returns: remainingManagedSpaceGbs
"""
totalManagedSpaceGbs, remainingManagedSpaceGbs = (
- self.utils.get_srp_pool_stats(self.conn, arrayName, poolName))
+ self.provisionv3.get_srp_pool_stats(self.conn,
+ arrayInfo))
LOG.info(_LI(
"Capacity stats for SRP pool %(poolName)s on array "
"%(arrayName)s total_capacity_gb=%(total_capacity_gb)lu, "
- "free_capacity_gb=%(free_capacity_gb)lu."),
- {'poolName': poolName,
- 'arrayName': arrayName,
+ "free_capacity_gb=%(free_capacity_gb)lu"),
+ {'poolName': arrayInfo['PoolName'],
+ 'arrayName': arrayInfo['SerialNumber'],
'total_capacity_gb': totalManagedSpaceGbs,
'free_capacity_gb': remainingManagedSpaceGbs})
- slo = self.utils.parse_slo_from_file(emcConfigFileName)
- workload = self.utils.parse_workload_from_file(emcConfigFileName)
location_info = ("%(arrayName)s#%(poolName)s#%(slo)s#%(workload)s"
- % {'arrayName': arrayName,
- 'poolName': poolName,
- 'slo': slo,
- 'workload': workload})
+ % {'arrayName': arrayInfo['SerialNumber'],
+ 'poolName': arrayInfo['PoolName'],
+ 'slo': arrayInfo['SLO'],
+ 'workload': arrayInfo['Workload']})
return location_info, totalManagedSpaceGbs, remainingManagedSpaceGbs
# 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)
if isinstance(loc, six.string_types):
name = eval(loc)
- keys = name['keybindings']
- systemName = keys['SystemName']
-
- prefix1 = 'SYMMETRIX+'
- prefix2 = 'SYMMETRIX-+-'
- smiversion = self.utils.get_smi_version(self.conn)
- if smiversion > SMI_VERSION_8 and prefix1 in systemName:
- keys['SystemName'] = systemName.replace(prefix1, prefix2)
- name['keybindings'] = keys
instancename = self.utils.get_instance_name(
name['classname'], name['keybindings'])
+
# Handle the case where volume cannot be found.
- try:
- foundVolumeinstance = self.conn.GetInstance(instancename)
- except Exception:
- foundVolumeinstance = None
+ foundVolumeinstance = self.utils.get_existing_instance(
+ self.conn, instancename)
if foundVolumeinstance is None:
LOG.debug("Volume %(volumename)s not found on the array.",
:returns: string -- configurationFile - name of the configuration file
"""
if configGroupName is None:
- self._set_ecom_credentials(CINDER_EMC_CONFIG_FILE)
return CINDER_EMC_CONFIG_FILE
if hasattr(self.configuration, 'cinder_emc_config_file'):
configurationFile = self.configuration.cinder_emc_config_file
'configGroupName': configGroupName,
'postfix': CINDER_EMC_CONFIG_FILE_POSTFIX}))
- self._set_ecom_credentials(configurationFile)
- return configurationFile
-
- def _set_ecom_credentials(self, configurationFile):
- """Given the configuration file set the ecom credentials.
-
- :param configurationFile: name of the file (String)
- :raises: VolumeBackendAPIException
- """
if os.path.isfile(configurationFile):
LOG.debug("Configuration file : %(configurationFile)s exists.",
{'configurationFile': configurationFile})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(data=exceptionMessage)
- ip, port = self.utils.get_ecom_server(configurationFile)
- self.user, self.passwd = self.utils.get_ecom_cred(configurationFile)
- self.ecomUseSSL, self.ecomCACert, self.ecomNoVerification = (
- self.utils.get_ecom_cred_SSL(configurationFile))
+ return configurationFile
+
+ def _set_ecom_credentials(self, arrayInfo):
+ """Given the array record set the ecom credentials.
+
+ :param arrayInfo: record
+ :raises: VolumeBackendAPIException
+ """
+ ip = arrayInfo['EcomServerIp']
+ port = arrayInfo['EcomServerPort']
+ self.user = arrayInfo['EcomUserName']
+ self.passwd = arrayInfo['EcomPassword']
+ self.ecomUseSSL = arrayInfo['EcomUseSSL']
+ self.ecomCACert = arrayInfo['EcomCACert']
+ self.ecomNoVerification = arrayInfo['EcomNoVerification']
ip_port = ("%(ip)s:%(port)s"
% {'ip': ip,
'port': port})
self._set_config_file_and_get_extra_specs(
volume, volumeTypeId))
- arrayName = self.utils.parse_array_name_from_file(
+ pool = self._validate_pool(volume)
+ LOG.debug("Pool returned is %(pool)s.",
+ {'pool': pool})
+ arrayInfo = self.utils.parse_file_to_get_array_map(
configurationFile)
- if arrayName is None:
+ poolRecord = self.utils.extract_record(arrayInfo, pool)
+
+ if not poolRecord:
exceptionMessage = (_(
- "The array cannot be null. The pool must be configured "
- "either as a cinder extra spec for multi-backend or in "
- "the EMC configuration file for the default case."))
- LOG.error(exceptionMessage)
+ "Unable to get corresponding record for pool."))
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
- isV3 = self.utils.isArrayV3(self.conn, arrayName)
+ self._set_ecom_credentials(poolRecord)
+ isV3 = self.utils.isArrayV3(
+ self.conn, poolRecord['SerialNumber'])
if isV3:
- extraSpecs = self._set_v3_extra_specs(
- configurationFile, arrayName, extraSpecs)
+ extraSpecs = self._set_v3_extra_specs(extraSpecs, poolRecord)
else:
- # V2 extra specs.
- extraSpecs = self._set_v2_extra_specs(
- configurationFile, arrayName, extraSpecs)
+ # V2 extra specs
+ extraSpecs = self._set_v2_extra_specs(extraSpecs, poolRecord)
except Exception:
+ import sys
exceptionMessage = (_(
- "Unable to get configuration information necessary to create "
- "a volume. Please check that there is a configuration file "
- "for each config group, if multi-backend is enabled. "
- "The file should be in the following format "
- "/etc/cinder/cinder_emc_config_<CONFIG_GROUP>.xml."))
+ "Unable to get configuration information necessary to "
+ "create a volume: %(errorMessage)s.")
+ % {'errorMessage': sys.exc_info()[1]})
raise exception.VolumeBackendAPIException(data=exceptionMessage)
return extraSpecs
% {'shortHostName': shortHostName,
'poolName': poolName,
'protocol': protocol}))
- maskingViewDict['fastPolicy'] = (
- self.utils.parse_fast_policy_name_from_file(
- self.configuration.cinder_emc_config_file))
+ maskingViewDict['fastPolicy'] = extraSpecs[FASTPOLICY]
maskingViewDict['sgGroupName'] = ("%(prefix)s-SG"
% {'prefix': prefix})
self.utils.find_controller_configuration_service(
self.conn, storageSystemName))
# The portGroup is gotten from emc xml config file.
- maskingViewDict['pgGroupName'] = (
- self.utils.parse_file_to_get_port_group_name(
- self.configuration.cinder_emc_config_file))
+ maskingViewDict['pgGroupName'] = extraSpecs[PORTGROUPNAME]
maskingViewDict['igGroupName'] = (
("OS-%(shortHostName)s-%(protocol)s-IG"
if 'True' in isVolumeBound:
appendVolumeInstance = (
self._unbind_and_get_volume_from_storage_pool(
- conn, storageConfigService, appendVolumeInstance.path,
- 'appendVolume', extraSpecs))
+ conn, storageConfigService, assocPoolInstanceName,
+ appendVolumeInstance.path, 'appendVolume', extraSpecs))
return appendVolumeInstance
return volumeInstance
def _unbind_and_get_volume_from_storage_pool(
- self, conn, storageConfigService, volumeInstanceName,
- volumeName, extraSpecs):
+ self, conn, storageConfigService, poolInstanceName,
+ volumeInstanceName, volumeName, extraSpecs):
"""Unbind a volume from a pool and return the unbound volume.
:param conn: the connection information to the ecom server
:param storageConfigService: the storage config service instance name
+ :param poolInstanceName: the pool instance name
:param volumeInstanceName: the volume instance name
:param volumeName: string the volumeName
:param extraSpecs: extra specifications
:returns: unboundVolumeInstance -- the unbound volume instance
"""
- rc, job = (
+ _rc, job = (
self.provision.unbind_volume_from_storage_pool(
- conn, storageConfigService, volumeInstanceName,
+ conn, storageConfigService, poolInstanceName,
+ volumeInstanceName,
volumeName, extraSpecs))
- # Check that the volume is unbound
- volumeInstance = conn.GetInstance(volumeInstanceName)
- isVolumeBound = self.utils.is_volume_bound_to_pool(
- conn, volumeInstance)
- if 'False' not in isVolumeBound:
- exceptionMessage = (_(
- "Failed to unbind volume: %(volume)s")
- % {'volume': volumeInstanceName})
- LOG.error(exceptionMessage)
- raise exception.VolumeBackendAPIException(data=exceptionMessage)
-
+ volumeDict = self.provision.get_volume_dict_from_job(conn, job['Job'])
+ volumeInstance = self.utils.find_volume_instance(
+ self.conn, volumeDict, volumeName)
return volumeInstance
def _modify_and_get_composite_volume_instance(
return defaultStorageGroupInstanceName
def _create_cloned_volume(
- self, cloneVolume, sourceVolume, isSnapshot=False):
+ self, cloneVolume, sourceVolume, extraSpecs, isSnapshot=False):
"""Create a clone volume from the source volume.
:param cloneVolume: clone volume
:param sourceVolume: source of the clone volume
+ :param extraSpecs: extra specs
:param isSnapshot: boolean -- Defaults to False
:returns: dict -- cloneDict the cloned volume dictionary
+ :raises: VolumeBackendAPIException
"""
- extraSpecs = self._initial_setup(cloneVolume)
-
sourceName = sourceVolume['name']
cloneName = cloneVolume['name']
{'cloneName': cloneName,
'sourceName': sourceName,
'rc': rc})
+ # Adding version information
+ cloneDict['version'] = self.version
return cloneDict
storageConfigService = self.utils.find_storage_configuration_service(
self.conn, storageSystemName)
- # Check the SLO range.
- maximumVolumeSize, minimumVolumeSize = (
- self.provisionv3.get_volume_range(
- self.conn, storageConfigService, poolInstanceName,
- extraSpecs[SLO], extraSpecs[WORKLOAD],
- extraSpecs))
- if not self.utils.is_in_range(
- volumeSize, maximumVolumeSize, minimumVolumeSize):
- LOG.warning(_LW(
- "Volume: %(volume)s with size: %(volumeSize)s bits "
- "is not in the Performance Capacity range: "
- "%(minimumVolumeSize)s-%(maximumVolumeSize)s bits. "
- "for SLO:%(slo)s and workload:%(workload)s. "
- "Unpredictable results may occur."),
- {'volume': volumeName,
- 'volumeSize': volumeSize,
- 'minimumVolumeSize': minimumVolumeSize,
- 'maximumVolumeSize': maximumVolumeSize,
- 'slo': extraSpecs[SLO],
- 'workload': extraSpecs[WORKLOAD]
- })
-
# A volume created without specifying a storage group during
# creation time is allocated from the default SRP pool and
# assigned the optimized SLO.
storageGroupName))
storageSystemName = volumeInstance['SystemName']
-
if not isValid:
LOG.error(_LE(
"Volume %(name)s is not suitable for storage "
controllerConfigService = (
self.utils.find_controller_configuration_service(
self.conn, storageSystemName))
+
defaultSgName = self.utils.get_v3_storage_group_name(
extraSpecs[POOL], extraSpecs[SLO], extraSpecs[WORKLOAD])
return False
def _update_pool_stats(
- self, emcConfigFileName, backendName, arrayName, poolName):
+ self, backendName, arrayInfo):
"""Update pool statistics (V2).
- :param emcConfigFileName: the EMC configuration file
:param backendName: the backend name
- :param arrayName: the array name
- :param poolName: the pool name
+ :param arrayInfo: the arrayInfo
:returns: location_info, total_capacity_gb, free_capacity_gb
"""
- # This value can be None.
- fastPolicyName = self.utils.parse_fast_policy_name_from_file(
- emcConfigFileName)
- if fastPolicyName is not None:
+
+ if arrayInfo['FastPolicy']:
LOG.debug(
"Fast policy %(fastPolicyName)s is enabled on %(arrayName)s.",
- {'fastPolicyName': fastPolicyName,
- 'arrayName': arrayName})
+ {'fastPolicyName': arrayInfo['FastPolicy'],
+ 'arrayName': arrayInfo['SerialNumber']})
else:
LOG.debug(
"No Fast policy for Array:%(arrayName)s "
"backend:%(backendName)s.",
- {'arrayName': arrayName,
+ {'arrayName': arrayInfo['SerialNumber'],
'backendName': backendName})
storageSystemInstanceName = self.utils.find_storageSystem(
- self.conn, arrayName)
+ self.conn, arrayInfo['SerialNumber'])
isTieringPolicySupported = (
self.fast.is_tiering_policy_enabled_on_storage_system(
self.conn, storageSystemInstanceName))
- if (fastPolicyName is not None and
- isTieringPolicySupported is True): # FAST enabled.
+ if (arrayInfo['FastPolicy'] is not None and
+ isTieringPolicySupported is True): # FAST enabled
total_capacity_gb, free_capacity_gb = (
self.fast.get_capacities_associated_to_policy(
- self.conn, arrayName, fastPolicyName))
+ self.conn, arrayInfo['SerialNumber'],
+ arrayInfo['FastPolicy']))
LOG.info(_LI(
- "FAST: capacity stats for policy %(fastPolicyName)s on "
- "array: %(arrayName)s total_capacity_gb=%(total_capacity_gb)lu"
- ", free_capacity_gb=%(free_capacity_gb)lu."),
- {'fastPolicyName': fastPolicyName,
- 'arrayName': arrayName,
+ "FAST: capacity stats for policy %(fastPolicyName)s on array "
+ "%(arrayName)s. total_capacity_gb=%(total_capacity_gb)lu, "
+ "free_capacity_gb=%(free_capacity_gb)lu."),
+ {'fastPolicyName': arrayInfo['FastPolicy'],
+ 'arrayName': arrayInfo['SerialNumber'],
'total_capacity_gb': total_capacity_gb,
'free_capacity_gb': free_capacity_gb})
else: # NON-FAST
total_capacity_gb, free_capacity_gb = (
- self.utils.get_pool_capacities(self.conn, poolName, arrayName))
+ self.utils.get_pool_capacities(self.conn,
+ arrayInfo['PoolName'],
+ arrayInfo['SerialNumber']))
LOG.info(_LI(
- "NON-FAST: capacity stats for pool %(poolName)s on array: "
+ "NON-FAST: capacity stats for pool %(poolName)s on array "
"%(arrayName)s total_capacity_gb=%(total_capacity_gb)lu, "
"free_capacity_gb=%(free_capacity_gb)lu."),
- {'poolName': poolName,
- 'arrayName': arrayName,
+ {'poolName': arrayInfo['PoolName'],
+ 'arrayName': arrayInfo['SerialNumber'],
'total_capacity_gb': total_capacity_gb,
'free_capacity_gb': free_capacity_gb})
- if poolName is None:
- LOG.debug("Unable to get the poolName for location_info.")
- if arrayName is None:
- LOG.debug("Unable to get the arrayName for location_info.")
- if fastPolicyName is None:
- LOG.debug("FAST is not enabled for this configuration: "
- "%(emcConfigFileName)s.",
- {'emcConfigFileName': emcConfigFileName})
-
location_info = ("%(arrayName)s#%(poolName)s#%(policyName)s"
- % {'arrayName': arrayName,
- 'poolName': poolName,
- 'policyName': fastPolicyName})
+ % {'arrayName': arrayInfo['SerialNumber'],
+ 'poolName': arrayInfo['PoolName'],
+ 'policyName': arrayInfo['FastPolicy']})
return location_info, total_capacity_gb, free_capacity_gb
- def _set_v2_extra_specs(self, configurationFile, arrayName, extraSpecs):
+ def _set_v2_extra_specs(self, extraSpecs, poolRecord):
"""Set the VMAX V2 extra specs.
- :param configurationFile: the EMC configuration file
- :param arrayName: the array serial number
:param extraSpecs: extra specifications
+ :param poolRecord: pool record
:returns: dict -- the extraSpecs
:raises: VolumeBackendAPIException
"""
extraSpecs[COMPOSITETYPE] = CONCATENATED
LOG.debug("StripedMetaCount is not in the extra specs.")
- poolName = self.utils.parse_pool_name_from_file(configurationFile)
- if poolName is None:
- exceptionMessage = (_(
- "The pool cannot be null. The pool must be configured "
- "either in the extra specs or in the EMC configuration "
- "file corresponding to the Volume Type."))
- LOG.error(exceptionMessage)
- raise exception.VolumeBackendAPIException(
- data=exceptionMessage)
-
# Get the FAST policy from the file. This value can be None if the
# user doesn't want to associate with any FAST policy.
- fastPolicyName = self.utils.parse_fast_policy_name_from_file(
- configurationFile)
- if fastPolicyName is not None:
+ if poolRecord['FastPolicy']:
LOG.debug("The fast policy name is: %(fastPolicyName)s.",
- {'fastPolicyName': fastPolicyName})
-
- extraSpecs[POOL] = poolName
- extraSpecs[ARRAY] = arrayName
- extraSpecs[FASTPOLICY] = fastPolicyName
+ {'fastPolicyName': poolRecord['FastPolicy']})
+ extraSpecs[FASTPOLICY] = poolRecord['FastPolicy']
extraSpecs[ISV3] = False
- extraSpecs = self._get_job_extra_specs(configurationFile, extraSpecs)
+ extraSpecs = self._set_common_extraSpecs(extraSpecs, poolRecord)
LOG.debug("Pool is: %(pool)s "
"Array is: %(array)s "
'memberCount': extraSpecs[MEMBERCOUNT]})
return extraSpecs
- def _set_v3_extra_specs(self, configurationFile, arrayName, extraSpecs):
+ def _set_v3_extra_specs(self, extraSpecs, poolRecord):
"""Set the VMAX V3 extra specs.
If SLO or workload are not specified then the default
values are NONE and the Optimized SLO will be assigned to the
volume.
- :param configurationFile: the EMC configuration file
- :param arrayName: the array serial number
- :returns: dict -- the extraSpecs
+ :param extraSpecs: extra specifications
+ :param poolRecord: pool record
+ :returns: dict -- the extra specifications dictionary
"""
- extraSpecs[SLO] = self.utils.parse_slo_from_file(
- configurationFile)
- extraSpecs[WORKLOAD] = self.utils.parse_workload_from_file(
- configurationFile)
- extraSpecs[POOL] = self.utils.parse_pool_name_from_file(
- configurationFile)
- extraSpecs[ARRAY] = arrayName
+ extraSpecs[SLO] = poolRecord['SLO']
+ extraSpecs[WORKLOAD] = poolRecord['Workload']
extraSpecs[ISV3] = True
- extraSpecs = self._get_job_extra_specs(configurationFile, extraSpecs)
-
+ extraSpecs = self._set_common_extraSpecs(extraSpecs, poolRecord)
LOG.debug("Pool is: %(pool)s "
"Array is: %(array)s "
"SLO is: %(slo)s "
'workload': extraSpecs[WORKLOAD]})
return extraSpecs
- def _get_job_extra_specs(self, configurationFile, extraSpecs):
- """Get user defined extra specs around job intervals and retries.
+ def _set_common_extraSpecs(self, extraSpecs, poolRecord):
+ """Set common extra specs.
+
+ The extraSpecs are common to v2 and v3
- :param configurationFile: the EMC configuration file
- :param extraSpecs: extraSpecs (in)
- :returns: extraSpecs (out)
+ :param extraSpecs: extra specifications
+ :param poolRecord: pool record
+ :returns: dict -- the extra specifications dictionary
"""
- intervalInSecs = self.utils.parse_interval_from_file(
- configurationFile)
- if intervalInSecs is not None:
+ extraSpecs[POOL] = poolRecord['PoolName']
+ extraSpecs[ARRAY] = poolRecord['SerialNumber']
+ extraSpecs[PORTGROUPNAME] = poolRecord['PortGroup']
+ if 'Interval' in poolRecord and poolRecord['Interval']:
+ extraSpecs[INTERVAL] = poolRecord['Interval']
LOG.debug("The user defined interval is : %(intervalInSecs)s.",
- {'intervalInSecs': intervalInSecs})
- extraSpecs[INTERVAL] = intervalInSecs
-
- retries = self.utils.parse_retries_from_file(
- configurationFile)
- if retries is not None:
+ {'intervalInSecs': poolRecord['Interval']})
+ else:
+ LOG.debug("Interval not overridden, default of 10 assumed.")
+ if 'Retries' in poolRecord and poolRecord['Retries']:
+ extraSpecs[RETRIES] = poolRecord['Retries']
LOG.debug("The user defined retries is : %(retries)s.",
- {'retries': retries})
- extraSpecs[RETRIES] = retries
-
+ {'retries': poolRecord['Retries']})
+ else:
+ LOG.debug("Retries not overridden, default of 60 assumed.")
return extraSpecs
def _delete_from_pool(self, storageConfigService, volumeInstance,
controllerConfigurationService,
volumeInstance.path, volumeName, extraSpecs)
- LOG.debug("Deleting Volume: %(name)s with deviceId: %(deviceId)s.",
- {'name': volumeName,
+ LOG.debug("Delete Volume: %(name)s Method: EMCReturnToStoragePool "
+ "ConfigService: %(service)s TheElement: %(vol_instance)s "
+ "DeviceId: %(deviceId)s.",
+ {'service': storageConfigService,
+ 'name': volumeName,
+ 'vol_instance': volumeInstance.path,
'deviceId': deviceId})
try:
rc = self.provision.delete_volume_from_pool(
{'volumeName': volumeName})
LOG.exception(errorMessage)
raise exception.VolumeBackendAPIException(data=errorMessage)
-
return rc
def _delete_from_pool_v3(self, storageConfigService, volumeInstance,
else: # Composite volume with meta device members.
# Check if the meta members capacity.
metaMemberInstanceNames = (
- self.utils.get_composite_elements(
- self.conn, sourceInstance))
- volumeCapacities = self.utils.get_meta_members_capacity_in_byte(
+ self.utils.get_meta_members_of_composite_volume(
+ self.conn, metaHeadInstanceName))
+ volumeCapacities = self.utils.get_meta_members_capacity_in_bit(
self.conn, metaMemberInstanceNames)
LOG.debug("Volume capacities: %(metasizes)s.",
{'metasizes': volumeCapacities})
# Default operation 8: Detach for clone.
operation = self.utils.get_num(8, '16')
- # Create target volume
- extraSpecs = self._initial_setup(cloneVolume)
-
numOfBlocks = sourceInstance['NumberOfBlocks']
blockSize = sourceInstance['BlockSize']
volumeSizeInbits = numOfBlocks * blockSize
extraSpecs)
return rc
+ def _validate_pool(self, volume):
+ """Get the pool from volume['host'].
+
+ There may be backward compatibiliy concerns, so putting in a
+ check to see if a version has been added to provider_location.
+ If it has, we know we are at the current version, if not, we
+ assume it was created pre 'Pool Aware Scheduler' feature.
+
+ :param volume: the volume Object
+ :returns: string -- pool
+ :raises: VolumeBackendAPIException
+ """
+ pool = None
+ # Volume is None in CG ops.
+ if volume is None:
+ return pool
+
+ # This check is for all operations except a create.
+ # On a create provider_location is None
+ try:
+ if volume['provider_location']:
+ version = self._get_version_from_provider_location(
+ volume['provider_location'])
+ if not version:
+ return pool
+ except KeyError:
+ return pool
+ try:
+ pool = volume_utils.extract_host(volume['host'], 'pool')
+ if pool:
+ LOG.debug("Pool from volume['host'] is %(pool)s.",
+ {'pool': pool})
+ else:
+ exceptionMessage = (_(
+ "Pool from volume['host'] %(host)s not found.")
+ % {'host': volume['host']})
+ raise exception.VolumeBackendAPIException(
+ data=exceptionMessage)
+ except Exception as ex:
+ exceptionMessage = (_(
+ "Pool from volume['host'] failed with: %(ex)s.")
+ % {'ex': ex})
+ raise exception.VolumeBackendAPIException(
+ data=exceptionMessage)
+ return pool
+
+ def _get_version_from_provider_location(self, loc):
+ """Get the version from the provider location.
+
+ :param loc: the provider_location dict
+ :returns: version or None
+ """
+ version = None
+ try:
+ if isinstance(loc, six.string_types):
+ name = eval(loc)
+ version = name['version']
+ except KeyError:
+ pass
+ return version
+
def manage_existing(self, volume, external_ref):
"""Manages an existing VMAX Volume (import to Cinder).
:param conn: connection to the ecom server
:param volumeInstanceName: the volume instance name
+ :param sgName: the storage group name
:returns: foundStorageGroupInstanceName
"""
foundStorageGroupInstanceName = None
conn.Associators(controllerConfigService,
ResultClass='CIM_DeviceMaskingGroup'))
- for storageMaskingGroupInstance in \
- storageMaskingGroupInstances:
+ for storageMaskingGroupInstance in storageMaskingGroupInstances:
if storageGroupName == storageMaskingGroupInstance['ElementName']:
# Check that it has not been deleted recently.
return instanceName
- def get_ecom_server(self, filename):
- """Given the file name get the ecomPort and ecomIP from it.
-
- :param filename: the path and file name of the emc configuration file
- :returns: ecomIp - the ecom IP address
- :returns: ecomPort - the ecom port
- """
- ecomIp = self._parse_from_file(filename, 'EcomServerIp')
- ecomPort = self._parse_from_file(filename, 'EcomServerPort')
- if ecomIp is not None and ecomPort is not None:
- LOG.debug("Ecom IP: %(ecomIp)s Port: %(ecomPort)s.",
- {'ecomIp': ecomIp, 'ecomPort': ecomPort})
- return ecomIp, ecomPort
- else:
- LOG.debug("Ecom server not found.")
- return None
-
- def get_ecom_cred(self, filename):
- """Given the filename get the ecomUser and ecomPasswd.
-
- :param filename: the path and filename of the emc configuration file
- :returns: ecomUser - the ecom user
- :returns: ecomPasswd - the ecom password
- """
- ecomUser = self._parse_from_file(filename, 'EcomUserName')
- ecomPasswd = self._parse_from_file(filename, 'EcomPassword')
- if ecomUser is not None and ecomPasswd is not None:
- return ecomUser, ecomPasswd
- else:
- LOG.debug("Ecom user not found.")
- return None
-
- def get_ecom_cred_SSL(self, filename):
- """Given the filename get the ecomUser and ecomPasswd.
-
- :param filename: the path and filename of the emc configuration file
- :returns: string -- ecomUseSSL
- :returns: string -- ecomCACert
- :returns: string -- ecomNoVerification
- """
- ecomUseSSL = self._parse_from_file(filename, 'EcomUseSSL')
- ecomCACert = self._parse_from_file(filename, 'EcomCACert')
- ecomNoVerification = self._parse_from_file(
- filename, 'EcomNoVerification')
- if ecomUseSSL is not None and ecomUseSSL == 'True':
- ecomUseSSL = True
- if ecomNoVerification is not None and ecomNoVerification == 'True':
- ecomNoVerification = True
- return ecomUseSSL, ecomCACert, ecomNoVerification
- else:
- ecomUseSSL = False
- ecomNoVerification = False
- return ecomUseSSL, ecomCACert, ecomNoVerification
-
- def parse_file_to_get_port_group_name(self, fileName):
- """Parses a file and chooses a port group randomly.
-
- Given a file, parse it to get all the possible
- portGroupElements and choose one randomly.
-
- :param fileName: the path and name of the file
- :returns: string -- portGroupName - the name of the port group chosen
- :raises: VolumeBackendAPIException
- """
- portGroupName = None
- myFile = open(fileName, 'r')
- data = myFile.read()
- myFile.close()
- dom = minidom.parseString(data)
- portGroupElements = dom.getElementsByTagName('PortGroup')
-
- if portGroupElements is not None and len(portGroupElements) > 0:
- portGroupNames = []
- for portGroupElement in portGroupElements:
- if portGroupElement.hasChildNodes():
- portGroupName = portGroupElement.childNodes[0].nodeValue
- portGroupName = portGroupName.replace('\n', '')
- portGroupName = portGroupName.replace('\r', '')
- portGroupName = portGroupName.replace('\t', '')
- portGroupName = portGroupName.strip()
- if portGroupName:
- portGroupNames.append(portGroupName)
-
- LOG.debug("portGroupNames: %(portGroupNames)s.",
- {'portGroupNames': portGroupNames})
- numPortGroups = len(portGroupNames)
- if numPortGroups > 0:
- selectedPortGroupName = (
- portGroupNames[random.randint(0, numPortGroups - 1)])
- LOG.debug("Returning Selected Port Group: "
- "%(selectedPortGroupName)s.",
- {'selectedPortGroupName': selectedPortGroupName})
- return selectedPortGroupName
-
- # If reaches here without returning yet, raise exception.
- exception_message = (_("No Port Group elements found in config file."))
- LOG.error(exception_message)
- raise exception.VolumeBackendAPIException(data=exception_message)
-
- def _parse_from_file(self, fileName, stringToParse):
- """Parse the string from XML.
-
- Remove newlines, tabs and trailing spaces.
-
- :param fileName: the path and name of the file
- :param stringToParse: the name of the tag to get the value for
- :returns: string -- the returned string; value of the tag
- """
- retString = None
- myFile = open(fileName, 'r')
- data = myFile.read()
- myFile.close()
- dom = minidom.parseString(data)
- tag = dom.getElementsByTagName(stringToParse)
- if tag is not None and len(tag) > 0:
- strXml = tag[0].toxml()
- strXml = strXml.replace('<%s>' % stringToParse, '')
- strXml = strXml.replace('\n', '')
- strXml = strXml.replace('\r', '')
- strXml = strXml.replace('\t', '')
- retString = strXml.replace('</%s>' % stringToParse, '')
- retString = retString.strip()
- return retString
-
- def parse_fast_policy_name_from_file(self, fileName):
- """Parse the fast policy name from config file.
-
- If it is not there, then NON FAST is assumed.
-
- :param fileName: the path and name of the file
- :returns: fastPolicyName - the fast policy name
- """
-
- fastPolicyName = self._parse_from_file(fileName, 'FastPolicy')
- if fastPolicyName:
- LOG.debug("File %(fileName)s: Fast Policy is %(fastPolicyName)s.",
- {'fileName': fileName,
- 'fastPolicyName': fastPolicyName})
- return fastPolicyName
- else:
- LOG.info(_LI("Fast Policy not found."))
- return None
-
- def parse_array_name_from_file(self, fileName):
- """Parse the array name from config file.
-
- If it is not there then there should only be one array configured to
- the ecom. If there is more than one then erroneous results can occur.
-
- :param fileName: the path and name of the file
- :returns: string -- arrayName - the array name
- """
- arrayName = self._parse_from_file(fileName, 'Array')
- if arrayName:
- return arrayName
- else:
- LOG.debug("Array not found from config file.")
- return None
-
- def parse_pool_name_from_file(self, fileName):
- """Parse the pool name from config file.
-
- If it is not there then we will attempt to get it from extra specs.
-
- :param fileName: the path and name of the file
- :returns: string -- poolName - the pool name
- """
- poolName = self._parse_from_file(fileName, 'Pool')
- if poolName:
- return poolName
- else:
- LOG.debug("Pool not found from config file.")
- return None
-
- def parse_slo_from_file(self, fileName):
- """Parse the slo from config file.
-
- Please note that the string 'NONE' is returned if it is not found.
-
- :param fileName: the path and name of the file
- :returns: string -- the slo or 'NONE'
- """
- slo = self._parse_from_file(fileName, 'SLO')
- if slo:
- return slo
- else:
- LOG.debug("SLO not in config file. "
- "Defaulting to NONE.")
- return 'NONE'
-
- def parse_workload_from_file(self, fileName):
- """Parse the workload from config file.
-
- Please note that the string 'NONE' is returned if it is not found.
-
- :param fileName: the path and name of the file
- :returns: string -- the workload or 'NONE'
- """
- workload = self._parse_from_file(fileName, 'Workload')
- if workload:
- return workload
- else:
- LOG.debug("Workload not in config file. "
- "Defaulting to NONE.")
- return 'NONE'
-
- def parse_interval_from_file(self, fileName):
- """Parse the interval from config file.
-
- If it is not there then the default will be used.
-
- :param fileName: the path and name of the file
- :returns: interval - the interval in seconds
- """
- interval = self._parse_from_file(fileName, 'Interval')
- if interval:
- return interval
- else:
- LOG.debug("Interval not overridden, default of 10 assumed.")
- return None
-
- def parse_retries_from_file(self, fileName):
- """Parse the retries from config file.
-
- If it is not there then the default will be used.
-
- :param fileName: the path and name of the file
- :returns: retries - the max number of retries
- """
- retries = self._parse_from_file(fileName, 'Retries')
- if retries:
- return retries
- else:
- LOG.debug("Retries not overridden, default of 60 assumed.")
- return None
-
def parse_pool_instance_id(self, poolInstanceId):
"""Given the instance Id parse the pool name and system name from it.
LOG.debug(
"storagePoolName: %(poolName)s, storageSystemName: %(array)s.",
{'poolName': storagePoolName, 'array': storageSystemName})
- storageSystemInstanceName = self.find_storageSystem(conn,
- storageSystemName)
- poolInstanceNames = conn.AssociatorNames(
- storageSystemInstanceName,
- ResultClass='EMC_VirtualProvisioningPool')
+ poolInstanceNames = conn.EnumerateInstanceNames(
+ 'EMC_VirtualProvisioningPool')
for poolInstanceName in poolInstanceNames:
- poolName = self._get_pool_name(conn, poolInstanceName)
- if (poolName == storagePoolName):
+ poolName, systemName = (
+ self.parse_pool_instance_id(poolInstanceName['InstanceID']))
+ if (poolName == storagePoolName and
+ storageSystemName in systemName):
# Check that the pool hasn't been recently deleted.
instance = self.get_existing_instance(conn, poolInstanceName)
if instance is None:
:returns: foundPoolInstanceName
:returns: string -- systemNameStr
"""
+ foundPoolInstanceName = None
vpoolInstanceNames = conn.AssociatorNames(
storageSystemInstanceName,
ResultClass='EMC_VirtualProvisioningPool')
- return self._get_pool_instance_and_system_name(
- conn, vpoolInstanceNames, storageSystemInstanceName,
- poolNameInStr)
+ for vpoolInstanceName in vpoolInstanceNames:
+ poolInstanceId = vpoolInstanceName['InstanceID']
+ # Example: SYMMETRIX+000195900551+TP+Sol_Innov
+ poolnameStr, systemNameStr = self.parse_pool_instance_id(
+ poolInstanceId)
+ if poolnameStr is not None and systemNameStr is not None:
+ if six.text_type(poolNameInStr) == six.text_type(poolnameStr):
+ # check that the pool hasn't recently been deleted.
+ try:
+ conn.GetInstance(vpoolInstanceName)
+ foundPoolInstanceName = vpoolInstanceName
+ except Exception:
+ foundPoolInstanceName = None
+ break
+
+ return foundPoolInstanceName, systemNameStr
def get_pool_and_system_name_v3(
self, conn, storageSystemInstanceName, poolNameInStr):
:returns: foundPoolInstanceName
:returns: string -- systemNameStr
"""
+ foundPoolInstanceName = None
srpPoolInstanceNames = conn.AssociatorNames(
storageSystemInstanceName,
ResultClass='Symm_SRPStoragePool')
- return self._get_pool_instance_and_system_name(
- conn, srpPoolInstanceNames, storageSystemInstanceName,
- poolNameInStr)
-
- def _get_pool_instance_and_system_name(
- self, conn, poolInstanceNames, storageSystemInstanceName,
- poolname):
- """Get the pool instance and the system name
-
- :param conn: the ecom connection
- :param poolInstanceNames: list of pool instances
- :param storageSystemInstanceName: storage system instance name
- :param poolname: pool name (string)
- :returns: foundPoolInstanceName, systemNameStr
- """
- foundPoolInstanceName = None
- poolnameStr = None
- systemNameStr = storageSystemInstanceName['Name']
- for poolInstanceName in poolInstanceNames:
+ for srpPoolInstanceName in srpPoolInstanceNames:
+ poolInstanceID = srpPoolInstanceName['InstanceID']
# Example: SYMMETRIX-+-000196700535-+-SR-+-SRP_1
- # Example: SYMMETRIX+000195900551+TP+Sol_Innov
- poolnameStr = self._get_pool_name(conn, poolInstanceName)
- if poolnameStr is not None:
- if six.text_type(poolname) == six.text_type(poolnameStr):
- foundPoolInstanceName = poolInstanceName
+ poolnameStr, systemNameStr = self.parse_pool_instance_id_v3(
+ poolInstanceID)
+ if poolnameStr is not None and systemNameStr is not None:
+ if six.text_type(poolNameInStr) == six.text_type(poolnameStr):
+ try:
+ conn.GetInstance(srpPoolInstanceName)
+ foundPoolInstanceName = srpPoolInstanceName
+ except Exception:
+ foundPoolInstanceName = None
break
return foundPoolInstanceName, systemNameStr
- def _get_pool_name(self, conn, poolInstanceName):
- """The pool name from the instance
+ def get_pool_name(self, conn, poolInstanceName):
+ """Get the pool name from the instance
:param conn: the ecom connection
:param poolInstanceName: the pool instance
return poolnameStr
def find_storageSystem(self, conn, arrayStr):
- """Find an array instance name given the array name.
+ """Find an array instance name by the array name.
:param conn: the ecom connection
:param arrayStr: the array Serial number (string)
LOG.debug("metaMembers: %(members)s.", {'members': metaMembers})
return metaMembers
- def get_meta_members_capacity_in_byte(self, conn, volumeInstanceNames):
- """Get the capacity in byte of all meta device member volumes.
+ def get_meta_members_capacity_in_bit(self, conn, volumeInstanceNames):
+ """Get the capacity in bits of all meta device member volumes.
:param conn: the ecom connection
:param volumeInstanceNames: array contains meta device member volumes
:returns: array contains capacities of each member device in bits
"""
- capacitiesInByte = []
- headVolume = conn.GetInstance(volumeInstanceNames[0])
- totalSizeInByte = (
- headVolume['ConsumableBlocks'] * headVolume['BlockSize'])
- volumeInstanceNames.pop(0)
+ capacitiesInBit = []
for volumeInstanceName in volumeInstanceNames:
volumeInstance = conn.GetInstance(volumeInstanceName)
numOfBlocks = volumeInstance['ConsumableBlocks']
blockSize = volumeInstance['BlockSize']
- volumeSizeInByte = numOfBlocks * blockSize
- capacitiesInByte.append(volumeSizeInByte)
- totalSizeInByte = totalSizeInByte - volumeSizeInByte
-
- capacitiesInByte.insert(0, totalSizeInByte)
- return capacitiesInByte
+ volumeSizeInbits = numOfBlocks * blockSize
+ capacitiesInBit.append(volumeSizeInbits)
+ return capacitiesInBit
def get_existing_instance(self, conn, instanceName):
"""Check that the instance name still exists and return the instance.
LOG.warning(_LW("Cannot determine the hardware type."))
return hardwareTypeId
+ def _process_tag(self, element, tagName):
+ """Process the tag to get the value.
+
+ :param element: the parent element
+ :param tagName: the tag name
+ :returns: nodeValue(can be None)
+ """
+ nodeValue = None
+ try:
+ processedElement = element.getElementsByTagName(tagName)[0]
+ nodeValue = processedElement.childNodes[0].nodeValue
+ if nodeValue:
+ nodeValue = nodeValue.strip()
+ except IndexError:
+ pass
+ return nodeValue
+
+ def _get_connection_info(self, ecomElement):
+ """Given the filename get the ecomUser and ecomPasswd.
+
+ :param ecomElement: the ecom element
+ :returns: dict -- connargs - the connection info dictionary
+ :raises: VolumeBackendAPIException
+ """
+ connargs = {}
+ connargs['EcomServerIp'] = (
+ self._process_tag(ecomElement, 'EcomServerIp'))
+ connargs['EcomServerPort'] = (
+ self._process_tag(ecomElement, 'EcomServerPort'))
+ connargs['EcomUserName'] = (
+ self._process_tag(ecomElement, 'EcomUserName'))
+ connargs['EcomPassword'] = (
+ self._process_tag(ecomElement, 'EcomPassword'))
+
+ for k, __ in connargs.items():
+ if connargs[k] is None:
+ exceptionMessage = (_(
+ "EcomServerIp, EcomServerPort, EcomUserName, "
+ "EcomPassword must have valid values."))
+ LOG.error(exceptionMessage)
+ raise exception.VolumeBackendAPIException(
+ data=exceptionMessage)
+
+ # These can be None
+ connargs['EcomUseSSL'] = self._process_tag(ecomElement, 'EcomUseSSL')
+ connargs['EcomCACert'] = self._process_tag(ecomElement, 'EcomCACert')
+ connargs['EcomNoVerification'] = (
+ self._process_tag(ecomElement, 'EcomNoVerification'))
+
+ if connargs['EcomUseSSL'] and connargs['EcomUseSSL'] == 'True':
+ connargs['EcomUseSSL'] = True
+ if connargs['EcomNoVerification'] and (
+ connargs['EcomNoVerification'] == 'True'):
+ connargs['EcomNoVerification'] = True
+ else:
+ connargs['EcomUseSSL'] = False
+ connargs['EcomNoVerification'] = False
+
+ return connargs
+
+ def _fill_record(self, connargs, serialNumber, poolName,
+ portGroup, element):
+ """Fill a single record.
+
+ :param connargs: the connection info
+ :param serialNumber: the serial number of array
+ :param poolName: the poolname
+ :param portGroup: the portGroup
+ :param element: the parent element
+ :returns: dict -- kwargs
+ """
+ kwargs = {}
+ kwargs['EcomServerIp'] = connargs['EcomServerIp']
+ kwargs['EcomServerPort'] = connargs['EcomServerPort']
+ kwargs['EcomUserName'] = connargs['EcomUserName']
+ kwargs['EcomPassword'] = connargs['EcomPassword']
+ kwargs['EcomUseSSL'] = connargs['EcomUseSSL']
+ kwargs['EcomCACert'] = connargs['EcomCACert']
+ kwargs['EcomNoVerification'] = connargs['EcomNoVerification']
+
+ slo = self._process_tag(element, 'SLO')
+ if slo is None:
+ slo = 'NONE'
+ kwargs['SLO'] = slo
+ workload = self._process_tag(element, 'Workload')
+ if workload is None:
+ workload = 'NONE'
+ kwargs['Workload'] = workload
+ fastPolicy = self._process_tag(element, 'FastPolicy')
+ kwargs['FastPolicy'] = fastPolicy
+ kwargs['SerialNumber'] = serialNumber
+ kwargs['PoolName'] = poolName
+ kwargs['PortGroup'] = portGroup
+
+ return kwargs
+
+ def _multi_pool_support(self, fileName):
+ """Multi pool support.
+
+ <EMC>
+ <EcomServers>
+ <EcomServer>
+ <EcomServerIp>10.108.246.202</EcomServerIp>
+ ...
+ <Arrays>
+ <Array>
+ <SerialNumber>000198700439</SerialNumber>
+ ...
+ <Pools>
+ <Pool>
+ <PoolName>FC_SLVR1</PoolName>
+ ...
+ </Pool>
+ </Pools>
+ </Array>
+ </Arrays>
+ </EcomServer>
+ </EcomServers>
+ </EMC>
+
+ :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.
+
+ <EMC>
+ <EcomServerIp>10.108.246.202</EcomServerIp>
+ <EcomServerPort>5988</EcomServerPort>
+ <EcomUserName>admin</EcomUserName>
+ <EcomPassword>#1Password</EcomPassword>
+ <PortGroups>
+ <PortGroup>OS-PORTGROUP1-PG</PortGroup>
+ </PortGroups>
+ <Array>000198700439</Array>
+ <Pool>FC_SLVR1</Pool>
+ </EMC>
+
+ :param fileName: the configuration file
+ :returns: list
+ """
+ myList = []
+ kwargs = {}
+ connargs = {}
+ myFile = open(fileName, 'r')
+ data = myFile.read()
+ myFile.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 parse_file_to_get_array_map(self, fileName):
+ """Parses a file and gets array map.
+
+ Given a file, parse it to get array and any pool(s) or
+ fast policy(s), SLOs, Workloads that might exist.
+
+ :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)
+
+ return myList
+
+ def extract_record(self, arrayInfo, pool):
+ """Given pool string determine the correct record.
+
+ The poolName and the serialNumber will determine the
+ correct record to return in VMAX2.
+ The poolName, SLO and the serialNumber will determine the
+ correct record to return in VMAX3.
+
+ :param arrayInfo: list of records
+ :param pool: e.g 'SATA_BRONZE1+000198700439'
+ 'SRP_1+Bronze+000198700555'
+ :returns: single record
+ """
+ foundArrayInfoRec = {}
+ if pool:
+ for arrayInfoRec in arrayInfo:
+ if pool.count('+') == 2:
+ compString = ("%(slo)s+%(poolName)s+%(array)s"
+ % {'slo': arrayInfoRec['SLO'],
+ 'poolName': arrayInfoRec['PoolName'],
+ 'array': arrayInfoRec['SerialNumber']})
+ else:
+ compString = ("%(poolName)s+%(array)s"
+ % {'poolName': arrayInfoRec['PoolName'],
+ 'array': arrayInfoRec['SerialNumber']})
+ if compString == pool:
+ LOG.info(_LI(
+ "The pool_name from extraSpecs is %(pool)s."),
+ {'pool': pool})
+ foundArrayInfoRec = arrayInfoRec
+ break
+ else:
+ foundArrayInfoRec = self._get_serial_number(arrayInfo)
+
+ return foundArrayInfoRec
+
+ def _get_random_portgroup(self, element):
+ """Get a portgroup from list of portgroup.
+
+ Parse all available port groups under a particular
+ array and choose one.
+
+ :param element: the parent element
+ :returns: the randomly chosen port group
+ :raises: VolumeBackendAPIException
+ """
+ portGroupElements = element.getElementsByTagName('PortGroup')
+ if portGroupElements and len(portGroupElements) > 0:
+ portGroupNames = []
+ for __ in portGroupElements:
+ portGroupName = self._process_tag(
+ element, 'PortGroup')
+ if portGroupName:
+ portGroupNames.append(portGroupName)
+
+ LOG.debug("portGroupNames: %(portGroupNames)s.",
+ {'portGroupNames': portGroupNames})
+ numPortGroups = len(portGroupNames)
+ if numPortGroups > 0:
+ selectedPortGroupName = (
+ portGroupNames[random.randint(0, numPortGroups - 1)])
+ LOG.debug("Returning selected PortGroup: "
+ "%(selectedPortGroupName)s.",
+ {'selectedPortGroupName': selectedPortGroupName})
+ return selectedPortGroupName
+
+ exception_message = (_("No PortGroup elements found in config file."))
+ LOG.error(exception_message)
+ raise exception.VolumeBackendAPIException(data=exception_message)
+
+ def _get_serial_number(self, arrayInfo):
+ """If we don't have a pool then we just get the serial number.
+
+ If there is more then one serial number we must return an
+ error and a recommendation to edit the EMC conf file.
+
+ :param arrayInfo: list of records
+ :returns: any record where serial number exists
+ :raises: VolumeBackendAPIException
+ """
+ serialNumberList = []
+ foundRecord = {}
+
+ for arrayInfoRec in arrayInfo:
+ serialNumberList.append(arrayInfoRec['SerialNumber'])
+ foundRecord = arrayInfoRec
+
+ if len(set(serialNumberList)) > 1:
+ # We have more than one serial number in the dict.
+ exception_message = (_("Multiple SerialNumbers found, when only "
+ "one was expected for this operation. "
+ "Please change your EMC config file."))
+ raise exception.VolumeBackendAPIException(data=exception_message)
+
+ return foundRecord
+
+ def _get_pool_info(self, arrayElement, fileName, connargs, interval,
+ retries, myList):
+ """Get pool information from element.
+
+ :param arrayElement: arrayElement
+ :param fileName: configuration file
+ :param connargs: connection arguments
+ :param interval: interval, can be None
+ :param retries: retries, can be None
+ :param myList: list (input)
+ :returns: list (output)
+ :raises: VolumeBackendAPIException
+ """
+ kwargs = {}
+ portGroup = self._get_random_portgroup(arrayElement)
+ serialNumber = self._process_tag(
+ arrayElement, 'SerialNumber')
+ if serialNumber is None:
+ exceptionMessage = (_(
+ "SerialNumber must be in the file "
+ "%(fileName)s."),
+ {'fileName': fileName})
+ LOG.error(exceptionMessage)
+ raise exception.VolumeBackendAPIException(
+ data=exceptionMessage)
+
+ poolElements = arrayElement.getElementsByTagName('Pool')
+ if poolElements and len(poolElements) > 0:
+ for poolElement in poolElements:
+ poolName = self._process_tag(poolElement, 'PoolName')
+ if poolName is None:
+ exceptionMessage = (_(
+ "PoolName must be in the file "
+ "%(fileName)s."),
+ {'fileName': fileName})
+ LOG.error(exceptionMessage)
+ raise exception.VolumeBackendAPIException(
+ data=exceptionMessage)
+ kwargs = self._fill_record(connargs, serialNumber,
+ poolName, portGroup,
+ poolElement)
+ if interval:
+ kwargs['Interval'] = interval
+ if retries:
+ kwargs['Retries'] = retries
+ myList.append(kwargs)
+ return myList
+
def find_volume_by_device_id_on_array(self, conn, storageSystem, deviceID):
"""Find the volume by device ID on a specific array.
{'source': sourceDeviceId, 'storageSystem': storageSystem})
return foundSyncInstanceName
-
- def get_smi_version(self, conn):
- intVersion = 0
- swIndentityInstances = conn.EnumerateInstances(
- 'SE_ManagementServerSoftwareIdentity')
- if swIndentityInstances:
- swIndentityInstance = swIndentityInstances[0]
- majorVersion = swIndentityInstance['MajorVersion']
- minorVersion = swIndentityInstance['MinorVersion']
- revisionNumber = swIndentityInstance['RevisionNumber']
-
- intVersion = int(str(majorVersion) + str(minorVersion)
- + str(revisionNumber))
-
- LOG.debug("Major version: %(majV)lu, Minor version: %(minV)lu, "
- "Revision number: %(revNum)lu, Version: %(intV)lu.",
- {'majV': majorVersion,
- 'minV': minorVersion,
- 'revNum': revisionNumber,
- 'intV': intVersion})
- return intVersion
-
- def get_composite_elements(
- self, conn, volumeInstance):
- """Get the meta members of a composite volume.
-
- :param conn: ECOM connection
- :param volumeInstance: the volume instance
- :returns memberVolumes: a list of meta members
- """
- memberVolumes = None
- storageSystemName = volumeInstance['SystemName']
- elementCompositionService = self.find_element_composition_service(
- conn, storageSystemName)
- rc, ret = conn.InvokeMethod(
- 'GetCompositeElements',
- elementCompositionService,
- TheElement=volumeInstance.path)
-
- if 'OutElements' in ret:
- LOG.debug("Get composite elements of volume "
- "%(volume)s rc=%(rc)d, ret=%(ret)s",
- {'volume': volumeInstance.path, 'rc': rc, 'ret': ret})
- memberVolumes = ret['OutElements']
- return memberVolumes