Merge "Support SMI-S provider v8.0.3 in VMAX driver"

This commit is contained in:
Jenkins 2015-07-17 04:40:46 +00:00 committed by Gerrit Code Review
commit 427af64e4b
7 changed files with 353 additions and 124 deletions

View File

@ -292,6 +292,7 @@ class EMCVMAXCommonData(object):
properties = {'ConsumableBlocks': '12345', properties = {'ConsumableBlocks': '12345',
'BlockSize': '512'} 'BlockSize': '512'}
block_size = 512
test_volume = {'name': 'vol1', test_volume = {'name': 'vol1',
'size': 1, 'size': 1,
'volume_name': 'vol1', 'volume_name': 'vol1',
@ -306,7 +307,7 @@ class EMCVMAXCommonData(object):
'status': 'available', 'status': 'available',
'host': 'fake-host', 'host': 'fake-host',
'NumberOfBlocks': 100, 'NumberOfBlocks': 100,
'BlockSize': 512 'BlockSize': block_size
} }
test_volume_v2 = {'name': 'vol1', test_volume_v2 = {'name': 'vol1',
@ -323,7 +324,7 @@ class EMCVMAXCommonData(object):
'status': 'available', 'status': 'available',
'host': 'fake-host', 'host': 'fake-host',
'NumberOfBlocks': 100, 'NumberOfBlocks': 100,
'BlockSize': 512 'BlockSize': block_size
} }
test_volume_v3 = {'name': 'vol1', test_volume_v3 = {'name': 'vol1',
@ -340,9 +341,17 @@ class EMCVMAXCommonData(object):
'status': 'available', 'status': 'available',
'host': 'fake-host', 'host': 'fake-host',
'NumberOfBlocks': 100, 'NumberOfBlocks': 100,
'BlockSize': 512 'BlockSize': block_size
} }
metaHead_volume = {'DeviceID': 10,
'ConsumableBlocks': 1000
}
meta_volume1 = {'DeviceID': 11,
'ConsumableBlocks': 200
}
meta_volume2 = {'DeviceID': 12,
'ConsumableBlocks': 300
}
test_volume_CG = {'name': 'volInCG', test_volume_CG = {'name': 'volInCG',
'consistencygroup_id': 'abc', 'consistencygroup_id': 'abc',
'size': 1, 'size': 1,
@ -433,6 +442,9 @@ class EMCVMAXCommonData(object):
'storagetype:slo': u'Bronze', 'storagetype:slo': u'Bronze',
'storagetype:array': u'0123456789', 'storagetype:array': u'0123456789',
'isV3': True} 'isV3': True}
majorVersion = 1
minorVersion = 2
revNumber = 3
class FakeLookupService(object): class FakeLookupService(object):
@ -464,7 +476,8 @@ class FakeEcomConnection(object):
Type=None, EMCSRP=None, EMCSLO=None, EMCWorkload=None, Type=None, EMCSRP=None, EMCSLO=None, EMCWorkload=None,
EMCCollections=None, InitiatorMaskingGroup=None, EMCCollections=None, InitiatorMaskingGroup=None,
DeviceMaskingGroup=None, TargetMaskingGroup=None, DeviceMaskingGroup=None, TargetMaskingGroup=None,
ProtocolController=None, StorageID=None, IDType=None): ProtocolController=None, StorageID=None, IDType=None,
WaitForCopyState=None):
rc = 0 rc = 0
myjob = SE_ConcreteJob() myjob = SE_ConcreteJob()
@ -489,7 +502,7 @@ class FakeEcomConnection(object):
elif TheElements and \ elif TheElements and \
TheElements[0]['DeviceID'] == '99999' and \ TheElements[0]['DeviceID'] == '99999' and \
MethodName == 'EMCReturnToStoragePool': MethodName == 'ReturnElementsToStoragePool':
rc = 10 rc = 10
myjob['status'] = 'failure' myjob['status'] = 'failure'
elif HardwareId: elif HardwareId:
@ -517,6 +530,13 @@ class FakeEcomConnection(object):
rc = 0 rc = 0
ret['HardwareID'] = self.data.iscsi_initiator ret['HardwareID'] = self.data.iscsi_initiator
return rc, ret return rc, ret
elif MethodName == 'GetCompositeElements':
ret = {}
rc = 0
ret['OutElements'] = [self.data.metaHead_volume,
self.data.meta_volume1,
self.data.meta_volume2]
return rc, ret
job = {'Job': myjob} job = {'Job': myjob}
return rc, job return rc, job
@ -559,6 +579,8 @@ class FakeEcomConnection(object):
result = self._enum_repservcpbls() result = self._enum_repservcpbls()
elif name == 'SE_StorageSynchronized_SV_SV': elif name == 'SE_StorageSynchronized_SV_SV':
result = self._enum_storageSyncSvSv() result = self._enum_storageSyncSvSv()
elif name == 'Symm_SRPStoragePool':
result = self._enum_srpstoragepool()
else: else:
result = self._default_enum() result = self._default_enum()
return result return result
@ -569,6 +591,8 @@ class FakeEcomConnection(object):
result = self._enum_pool_details() result = self._enum_pool_details()
elif name == 'SE_StorageHardwareID': elif name == 'SE_StorageHardwareID':
result = self._enum_storhdwids() result = self._enum_storhdwids()
elif name == 'SE_ManagementServerSoftwareIdentity':
result = self._enum_sw_identity()
else: else:
result = self._default_enum() result = self._default_enum()
return result return result
@ -958,7 +982,7 @@ class FakeEcomConnection(object):
def _getinstance_pool(self, objectpath): def _getinstance_pool(self, objectpath):
pool = {} pool = {}
pool['CreationClassName'] = 'Symm_VirtualProvisioningPool' pool['CreationClassName'] = 'Symm_VirtualProvisioningPool'
pool['ElementName'] = 'gold' pool['ElementName'] = self.data.poolname
pool['SystemName'] = self.data.storage_system pool['SystemName'] = self.data.storage_system
pool['TotalManagedSpace'] = self.data.totalmanagedspace_bits pool['TotalManagedSpace'] = self.data.totalmanagedspace_bits
pool['EMCSubscribedCapacity'] = self.data.subscribedcapacity_bits pool['EMCSubscribedCapacity'] = self.data.subscribedcapacity_bits
@ -975,6 +999,7 @@ class FakeEcomConnection(object):
srpstoragepool = SYMM_SrpStoragePool() srpstoragepool = SYMM_SrpStoragePool()
srpstoragepool['CreationClassName'] = ( srpstoragepool['CreationClassName'] = (
self.data.srpstoragepool_creationclass) self.data.srpstoragepool_creationclass)
srpstoragepool['ElementName'] = 'SRP_1'
classcimproperty = Fake_CIMProperty() classcimproperty = Fake_CIMProperty()
totalManagedSpace = ( totalManagedSpace = (
@ -1211,6 +1236,31 @@ class FakeEcomConnection(object):
vols.append(failed_vol) 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 return vols
def _enum_initiatorMaskingGroup(self): def _enum_initiatorMaskingGroup(self):
@ -1461,6 +1511,15 @@ class FakeEcomConnection(object):
svInstances.append(svInstance) svInstances.append(svInstance)
return svInstances 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): def _default_enum(self):
names = [] names = []
name = {} name = {}
@ -1686,18 +1745,39 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
self.assertEqual(storageHardwareIDInstanceNames[0], self.assertEqual(storageHardwareIDInstanceNames[0],
self.data.iscsi_initiator) self.data.iscsi_initiator)
def test_format_system_name(self): def test_get_pool_instance_and_system_name(self):
v2array = ['SYMMETRIX', '000195900551', 'U', 'gold'] conn = self.fake_ecom_connection()
systemnameV2 = self.driver.utils._format_system_name(v2array[0], # V2 - old '+' separator
v2array[1], storagesystem = {}
'+') storagesystem['SystemName'] = self.data.storage_system
self.assertEqual('SYMMETRIX+000195900551', systemnameV2) storagesystem['Name'] = self.data.storage_system
pools = conn.EnumerateInstanceNames("EMC_VirtualProvisioningPool")
v3array = ['SYMMETRIX', '000197200056', 'SRP_1'] poolname = 'gold'
systemnameV3 = self.driver.utils._format_system_name(v3array[0], poolinstancename, systemname = (
v3array[1], self.driver.common.utils._get_pool_instance_and_system_name(
'-+-') conn, pools, storagesystem, poolname))
self.assertEqual('SYMMETRIX-+-000197200056', systemnameV3) 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_get_hardware_type(self): def test_get_hardware_type(self):
iqn_initiator = 'iqn.1992-04.com.emc: 50000973f006dd80' iqn_initiator = 'iqn.1992-04.com.emc: 50000973f006dd80'
@ -1914,19 +1994,19 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
volume2 = EMC_StorageVolume() volume2 = EMC_StorageVolume()
volume2['name'] = 'myVol' volume2['name'] = 'myVol'
volume2['provider_location'] = six.text_type(provider_location2) volume2['provider_location'] = six.text_type(provider_location2)
verify_orig = self.driver.common.utils.get_existing_instance verify_orig = self.driver.common.conn.GetInstance
self.driver.common.utils.get_existing_instance = mock.Mock( self.driver.common.conn.GetInstance = mock.Mock(
return_value=None) return_value=None)
findlun2 = self.driver.common._find_lun(volume2) findlun2 = self.driver.common._find_lun(volume2)
# Not found. # Not found.
self.assertIsNone(findlun2) self.assertIsNone(findlun2)
instancename2 = self.driver.utils.get_instance_name( self.driver.utils.get_instance_name(
provider_location2['classname'], provider_location2['classname'],
keybindings2) keybindings2)
self.driver.common.utils.get_existing_instance.assert_called_once_with( self.driver.common.conn.GetInstance.assert_called_once_with(
self.driver.common.conn, instancename2) keybindings2)
self.driver.common.utils.get_existing_instance.reset_mock() self.driver.common.conn.GetInstance.reset_mock()
self.driver.common.utils.get_existing_instance = verify_orig self.driver.common.conn.GetInstance = verify_orig
keybindings3 = {'CreationClassName': u'Symm_StorageVolume', keybindings3 = {'CreationClassName': u'Symm_StorageVolume',
'SystemName': u'SYMMETRIX+000195900551', 'SystemName': u'SYMMETRIX+000195900551',
@ -2650,7 +2730,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system)) return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit', 'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321]) return_value=[1234567, 7654321])
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
@ -2685,7 +2765,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit', 'get_meta_members_capacity_in_byte',
return_value=[1234567]) return_value=[1234567])
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
@ -2746,7 +2826,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system)) return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit', 'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321]) return_value=[1234567, 7654321])
@mock.patch.object( @mock.patch.object(
FakeDB, FakeDB,
@ -2906,7 +2986,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system)) return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit', 'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321]) return_value=[1234567, 7654321])
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
@ -3017,6 +3097,74 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
conn, volumeInstance, originalName) conn, volumeInstance, originalName)
self.assertEqual(originalName, volumeInstance['ElementName']) 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): def _cleanup(self):
if self.config_file_path: if self.config_file_path:
bExists = os.path.exists(self.config_file_path) bExists = os.path.exists(self.config_file_path)
@ -3391,7 +3539,7 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system)) return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit', 'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321]) return_value=[1234567, 7654321])
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
@ -3429,7 +3577,7 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit', 'get_meta_members_capacity_in_byte',
return_value=[1234567]) return_value=[1234567])
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
@ -3479,7 +3627,7 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system)) return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit', 'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321]) return_value=[1234567, 7654321])
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
@ -4182,7 +4330,7 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
'status': 'available', 'status': 'available',
'host': self.data.fake_host, 'host': self.data.fake_host,
'NumberOfBlocks': 100, 'NumberOfBlocks': 100,
'BlockSize': 512 'BlockSize': self.data.block_size
} }
common = self.driver.common common = self.driver.common
common._initial_setup = mock.Mock( common._initial_setup = mock.Mock(
@ -4215,7 +4363,7 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
'status': 'available', 'status': 'available',
'host': self.data.fake_host, 'host': self.data.fake_host,
'NumberOfBlocks': 100, 'NumberOfBlocks': 100,
'BlockSize': 512 'BlockSize': self.data.block_size
} }
common = self.driver.common common = self.driver.common
common._initial_setup = mock.Mock( common._initial_setup = mock.Mock(
@ -4599,7 +4747,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system)) return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit', 'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321]) return_value=[1234567, 7654321])
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
@ -4637,7 +4785,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit', 'get_meta_members_capacity_in_byte',
return_value=[1234567]) return_value=[1234567])
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
@ -4706,7 +4854,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system)) return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit', 'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321]) return_value=[1234567, 7654321])
@mock.patch.object( @mock.patch.object(
emc_vmax_utils.EMCVMAXUtils, emc_vmax_utils.EMCVMAXUtils,
@ -5165,7 +5313,7 @@ class EMCV3DriverTestCase(test.TestCase):
cloneVol['volume_type_id'] = 'abc' cloneVol['volume_type_id'] = 'abc'
cloneVol['provider_location'] = None cloneVol['provider_location'] = None
cloneVol['NumberOfBlocks'] = 100 cloneVol['NumberOfBlocks'] = 100
cloneVol['BlockSize'] = 512 cloneVol['BlockSize'] = self.data.block_size
self.driver.create_cloned_volume(cloneVol, self.data.test_volume) self.driver.create_cloned_volume(cloneVol, self.data.test_volume)
@mock.patch.object( @mock.patch.object(

View File

@ -55,6 +55,7 @@ STRIPECOUNT = 'storagetype:stripecount'
MEMBERCOUNT = 'storagetype:membercount' MEMBERCOUNT = 'storagetype:membercount'
STRIPED = 'striped' STRIPED = 'striped'
CONCATENATED = 'concatenated' CONCATENATED = 'concatenated'
SMI_VERSION_8 = 800
# V3 # V3
SLO = 'storagetype:slo' SLO = 'storagetype:slo'
WORKLOAD = 'storagetype:workload' WORKLOAD = 'storagetype:workload'
@ -1319,13 +1320,23 @@ class EMCVMAXCommon(object):
if isinstance(loc, six.string_types): if isinstance(loc, six.string_types):
name = eval(loc) 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( instancename = self.utils.get_instance_name(
name['classname'], name['keybindings']) name['classname'], name['keybindings'])
# Handle the case where volume cannot be found. # Handle the case where volume cannot be found.
foundVolumeinstance = self.utils.get_existing_instance( try:
self.conn, instancename) foundVolumeinstance = self.conn.GetInstance(instancename)
except Exception:
foundVolumeinstance = None
if foundVolumeinstance is None: if foundVolumeinstance is None:
LOG.debug("Volume %(volumename)s not found on the array.", LOG.debug("Volume %(volumename)s not found on the array.",
@ -1835,8 +1846,8 @@ class EMCVMAXCommon(object):
if 'True' in isVolumeBound: if 'True' in isVolumeBound:
appendVolumeInstance = ( appendVolumeInstance = (
self._unbind_and_get_volume_from_storage_pool( self._unbind_and_get_volume_from_storage_pool(
conn, storageConfigService, assocPoolInstanceName, conn, storageConfigService, appendVolumeInstance.path,
appendVolumeInstance.path, 'appendVolume', extraSpecs)) 'appendVolume', extraSpecs))
return appendVolumeInstance return appendVolumeInstance
@ -1862,27 +1873,33 @@ class EMCVMAXCommon(object):
return volumeInstance return volumeInstance
def _unbind_and_get_volume_from_storage_pool( def _unbind_and_get_volume_from_storage_pool(
self, conn, storageConfigService, poolInstanceName, self, conn, storageConfigService, volumeInstanceName,
volumeInstanceName, volumeName, extraSpecs): volumeName, extraSpecs):
"""Unbind a volume from a pool and return the unbound volume. """Unbind a volume from a pool and return the unbound volume.
:param conn: the connection information to the ecom server :param conn: the connection information to the ecom server
:param storageConfigService: the storage config service instance name :param storageConfigService: the storage config service instance name
:param poolInstanceName: the pool instance name
:param volumeInstanceName: the volume instance name :param volumeInstanceName: the volume instance name
:param volumeName: string the volumeName :param volumeName: string the volumeName
:param extraSpecs: extra specifications :param extraSpecs: extra specifications
:returns: unboundVolumeInstance -- the unbound volume instance :returns: unboundVolumeInstance -- the unbound volume instance
""" """
_rc, job = ( rc, job = (
self.provision.unbind_volume_from_storage_pool( self.provision.unbind_volume_from_storage_pool(
conn, storageConfigService, poolInstanceName, conn, storageConfigService, volumeInstanceName,
volumeInstanceName,
volumeName, extraSpecs)) volumeName, extraSpecs))
volumeDict = self.provision.get_volume_dict_from_job(conn, job['Job']) # Check that the volume is unbound
volumeInstance = self.utils.find_volume_instance( volumeInstance = conn.GetInstance(volumeInstanceName)
self.conn, volumeDict, volumeName) 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)
return volumeInstance return volumeInstance
def _modify_and_get_composite_volume_instance( def _modify_and_get_composite_volume_instance(
@ -3334,12 +3351,8 @@ class EMCVMAXCommon(object):
controllerConfigurationService, controllerConfigurationService,
volumeInstance.path, volumeName, extraSpecs) volumeInstance.path, volumeName, extraSpecs)
LOG.debug("Delete Volume: %(name)s Method: EMCReturnToStoragePool " LOG.debug("Deleting Volume: %(name)s with deviceId: %(deviceId)s.",
"ConfigService: %(service)s TheElement: %(vol_instance)s " {'name': volumeName,
"DeviceId: %(deviceId)s.",
{'service': storageConfigService,
'name': volumeName,
'vol_instance': volumeInstance.path,
'deviceId': deviceId}) 'deviceId': deviceId})
try: try:
rc = self.provision.delete_volume_from_pool( rc = self.provision.delete_volume_from_pool(
@ -3466,9 +3479,9 @@ class EMCVMAXCommon(object):
else: # Composite volume with meta device members. else: # Composite volume with meta device members.
# Check if the meta members capacity. # Check if the meta members capacity.
metaMemberInstanceNames = ( metaMemberInstanceNames = (
self.utils.get_meta_members_of_composite_volume( self.utils.get_composite_elements(
self.conn, metaHeadInstanceName)) self.conn, sourceInstance))
volumeCapacities = self.utils.get_meta_members_capacity_in_bit( volumeCapacities = self.utils.get_meta_members_capacity_in_byte(
self.conn, metaMemberInstanceNames) self.conn, metaMemberInstanceNames)
LOG.debug("Volume capacities: %(metasizes)s.", LOG.debug("Volume capacities: %(metasizes)s.",
{'metasizes': volumeCapacities}) {'metasizes': volumeCapacities})

View File

@ -38,9 +38,10 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
2.1.2 - Clean up failed clones (bug #1440154) 2.1.2 - Clean up failed clones (bug #1440154)
2.1.3 - Fixed a problem with FAST support (bug #1435069) 2.1.3 - Fixed a problem with FAST support (bug #1435069)
2.2.0 - Add manage/unmanage 2.2.0 - Add manage/unmanage
2.2.1 - Support for SE 8.0.3
""" """
VERSION = "2.2.0" VERSION = "2.2.1"
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@ -14,18 +14,18 @@
# under the License. # under the License.
import base64 import base64
import httplib
import os import os
import socket import socket
import ssl import ssl
import string import string
import struct import struct
import urllib
from eventlet import patcher from eventlet import patcher
import OpenSSL import OpenSSL
from oslo_log import log as logging from oslo_log import log as logging
import six import six
from six.moves import http_client
from six.moves import urllib
from cinder.i18n import _, _LI from cinder.i18n import _, _LI
@ -74,7 +74,7 @@ def get_default_ca_certs():
class OpenSSLConnectionDelegator(object): class OpenSSLConnectionDelegator(object):
"""An OpenSSL.SSL.Connection delegator. """An OpenSSL.SSL.Connection delegator.
Supplies an additional 'makefile' method which http_client requires Supplies an additional 'makefile' method which httplib requires
and is not present in OpenSSL.SSL.Connection. and is not present in OpenSSL.SSL.Connection.
Note: Since it is not possible to inherit from OpenSSL.SSL.Connection Note: Since it is not possible to inherit from OpenSSL.SSL.Connection
a delegator must be used. a delegator must be used.
@ -89,7 +89,7 @@ class OpenSSLConnectionDelegator(object):
return socket._fileobject(self.connection, *args, **kwargs) return socket._fileobject(self.connection, *args, **kwargs)
class HTTPSConnection(http_client.HTTPSConnection): class HTTPSConnection(httplib.HTTPSConnection):
def __init__(self, host, port=None, key_file=None, cert_file=None, def __init__(self, host, port=None, key_file=None, cert_file=None,
strict=None, ca_certs=None, no_verification=False): strict=None, ca_certs=None, no_verification=False):
if not pywbemAvailable: if not pywbemAvailable:
@ -101,9 +101,9 @@ class HTTPSConnection(http_client.HTTPSConnection):
else: else:
excp_lst = () excp_lst = ()
try: try:
http_client.HTTPSConnection.__init__(self, host, port, httplib.HTTPSConnection.__init__(self, host, port,
key_file=key_file, key_file=key_file,
cert_file=cert_file) cert_file=cert_file)
self.key_file = None if key_file is None else key_file self.key_file = None if key_file is None else key_file
self.cert_file = None if cert_file is None else cert_file self.cert_file = None if cert_file is None else cert_file
@ -255,7 +255,7 @@ def wbem_request(url, data, creds, headers=None, debug=0, x509=None,
"""Send request over HTTP. """Send request over HTTP.
Send XML data over HTTP to the specified url. Return the Send XML data over HTTP to the specified url. Return the
response in XML. Uses Python's build-in http_client. x509 may be a response in XML. Uses Python's build-in httplib. x509 may be a
dictionary containing the location of the SSL certificate and key dictionary containing the location of the SSL certificate and key
files. files.
""" """
@ -274,7 +274,7 @@ def wbem_request(url, data, creds, headers=None, debug=0, x509=None,
localAuthHeader = None localAuthHeader = None
tryLimit = 5 tryLimit = 5
if isinstance(data, six.text_type): if isinstance(data, unicode):
data = data.encode('utf-8') data = data.encode('utf-8')
data = '<?xml version="1.0" encoding="utf-8" ?>\n' + data data = '<?xml version="1.0" encoding="utf-8" ?>\n' + data
@ -309,10 +309,10 @@ def wbem_request(url, data, creds, headers=None, debug=0, x509=None,
h.putheader('PegasusAuthorization', 'Local "%s"' % locallogin) h.putheader('PegasusAuthorization', 'Local "%s"' % locallogin)
for hdr in headers: for hdr in headers:
if isinstance(hdr, six.text_type): if isinstance(hdr, unicode):
hdr = hdr.encode('utf-8') hdr = hdr.encode('utf-8')
s = map(lambda x: string.strip(x), string.split(hdr, ":", 1)) s = map(lambda x: string.strip(x), string.split(hdr, ":", 1))
h.putheader(urllib.parse.quote(s[0]), urllib.parse.quote(s[1])) h.putheader(urllib.quote(s[0]), urllib.quote(s[1]))
try: try:
h.endheaders() h.endheaders()
@ -328,7 +328,7 @@ def wbem_request(url, data, creds, headers=None, debug=0, x509=None,
if response.status != 200: if response.status != 200:
raise pywbem.cim_http.Error('HTTP error') raise pywbem.cim_http.Error('HTTP error')
except http_client.BadStatusLine as arg: except httplib.BadStatusLine as arg:
msg = (_("Bad Status line returned: %(arg)s.") msg = (_("Bad Status line returned: %(arg)s.")
% {'arg': arg}) % {'arg': arg})
raise pywbem.cim_http.Error(msg) raise pywbem.cim_http.Error(msg)

View File

@ -46,9 +46,10 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
2.1.2 - Clean up failed clones (bug #1440154) 2.1.2 - Clean up failed clones (bug #1440154)
2.1.3 - Fixed a problem with FAST support (bug #1435069) 2.1.3 - Fixed a problem with FAST support (bug #1435069)
2.2.0 - Add manage/unmanage 2.2.0 - Add manage/unmanage
2.2.1 - Support for SE 8.0.3
""" """
VERSION = "2.2.0" VERSION = "2.2.1"
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):

View File

@ -64,7 +64,7 @@ class EMCVMAXProvision(object):
theElements = [volumeInstanceName] theElements = [volumeInstanceName]
rc, job = conn.InvokeMethod( rc, job = conn.InvokeMethod(
'EMCReturnToStoragePool', storageConfigservice, 'ReturnElementsToStoragePool', storageConfigservice,
TheElements=theElements) TheElements=theElements)
if rc != 0: if rc != 0:
@ -338,14 +338,13 @@ class EMCVMAXProvision(object):
time.time())}) time.time())})
def unbind_volume_from_storage_pool( def unbind_volume_from_storage_pool(
self, conn, storageConfigService, poolInstanceName, self, conn, storageConfigService, volumeInstanceName,
volumeInstanceName, volumeName, extraSpecs): volumeName, extraSpecs):
"""Unbind a volume from a pool and return the unbound volume. """Unbind a volume from a pool and return the unbound volume.
:param conn: the connection information to the ecom server :param conn: the connection information to the ecom server
:param storageConfigService: the storage configuration service :param storageConfigService: the storage configuration service
instance name instance name
:param poolInstanceName: the pool instance name
:param volumeInstanceName: the volume instance name :param volumeInstanceName: the volume instance name
:param volumeName: the volume name :param volumeName: the volume name
:param extraSpecs: additional info :param extraSpecs: additional info
@ -358,7 +357,6 @@ class EMCVMAXProvision(object):
rc, job = conn.InvokeMethod( rc, job = conn.InvokeMethod(
'EMCUnBindElement', 'EMCUnBindElement',
storageConfigService, storageConfigService,
InPool=poolInstanceName,
TheElement=volumeInstanceName) TheElement=volumeInstanceName)
if rc != 0: if rc != 0:
@ -1070,14 +1068,16 @@ class EMCVMAXProvision(object):
'relationName': relationName, 'relationName': relationName,
'srcGroup': srcGroupInstanceName, 'srcGroup': srcGroupInstanceName,
'tgtGroup': tgtGroupInstanceName}) 'tgtGroup': tgtGroupInstanceName})
# 8 for clone. # SyncType 8 - clone.
# CopyState 4 - Synchronized.
rc, job = conn.InvokeMethod( rc, job = conn.InvokeMethod(
'CreateGroupReplica', 'CreateGroupReplica',
replicationService, replicationService,
RelationshipName=relationName, RelationshipName=relationName,
SourceGroup=srcGroupInstanceName, SourceGroup=srcGroupInstanceName,
TargetGroup=tgtGroupInstanceName, TargetGroup=tgtGroupInstanceName,
SyncType=self.utils.get_num(8, '16')) SyncType=self.utils.get_num(8, '16'),
WaitForCopyState=self.utils.get_num(4, '16'))
if rc != 0: if rc != 0:
rc, errordesc = self.utils.wait_for_job_complete(conn, job, rc, errordesc = self.utils.wait_for_job_complete(conn, job,

View File

@ -1224,13 +1224,14 @@ class EMCVMAXUtils(object):
LOG.debug( LOG.debug(
"storagePoolName: %(poolName)s, storageSystemName: %(array)s.", "storagePoolName: %(poolName)s, storageSystemName: %(array)s.",
{'poolName': storagePoolName, 'array': storageSystemName}) {'poolName': storagePoolName, 'array': storageSystemName})
poolInstanceNames = conn.EnumerateInstanceNames( storageSystemInstanceName = self.find_storageSystem(conn,
'EMC_VirtualProvisioningPool') storageSystemName)
poolInstanceNames = conn.AssociatorNames(
storageSystemInstanceName,
ResultClass='EMC_VirtualProvisioningPool')
for poolInstanceName in poolInstanceNames: for poolInstanceName in poolInstanceNames:
poolName, systemName = ( poolName = self._get_pool_name(conn, poolInstanceName)
self.parse_pool_instance_id(poolInstanceName['InstanceID'])) if (poolName == storagePoolName):
if (poolName == storagePoolName and
storageSystemName in systemName):
# Check that the pool hasn't been recently deleted. # Check that the pool hasn't been recently deleted.
instance = self.get_existing_instance(conn, poolInstanceName) instance = self.get_existing_instance(conn, poolInstanceName)
if instance is None: if instance is None:
@ -1622,27 +1623,13 @@ class EMCVMAXUtils(object):
:returns: foundPoolInstanceName :returns: foundPoolInstanceName
:returns: string -- systemNameStr :returns: string -- systemNameStr
""" """
foundPoolInstanceName = None
vpoolInstanceNames = conn.AssociatorNames( vpoolInstanceNames = conn.AssociatorNames(
storageSystemInstanceName, storageSystemInstanceName,
ResultClass='EMC_VirtualProvisioningPool') ResultClass='EMC_VirtualProvisioningPool')
for vpoolInstanceName in vpoolInstanceNames: return self._get_pool_instance_and_system_name(
poolInstanceId = vpoolInstanceName['InstanceID'] conn, vpoolInstanceNames, storageSystemInstanceName,
# Example: SYMMETRIX+000195900551+TP+Sol_Innov poolNameInStr)
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( def get_pool_and_system_name_v3(
self, conn, storageSystemInstanceName, poolNameInStr): self, conn, storageSystemInstanceName, poolNameInStr):
@ -1654,27 +1641,54 @@ class EMCVMAXUtils(object):
:returns: foundPoolInstanceName :returns: foundPoolInstanceName
:returns: string -- systemNameStr :returns: string -- systemNameStr
""" """
foundPoolInstanceName = None
srpPoolInstanceNames = conn.AssociatorNames( srpPoolInstanceNames = conn.AssociatorNames(
storageSystemInstanceName, storageSystemInstanceName,
ResultClass='Symm_SRPStoragePool') ResultClass='Symm_SRPStoragePool')
for srpPoolInstanceName in srpPoolInstanceNames: return self._get_pool_instance_and_system_name(
poolInstanceID = srpPoolInstanceName['InstanceID'] 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:
# Example: SYMMETRIX-+-000196700535-+-SR-+-SRP_1 # Example: SYMMETRIX-+-000196700535-+-SR-+-SRP_1
poolnameStr, systemNameStr = self.parse_pool_instance_id_v3( # Example: SYMMETRIX+000195900551+TP+Sol_Innov
poolInstanceID) poolnameStr = self._get_pool_name(conn, poolInstanceName)
if poolnameStr is not None and systemNameStr is not None: if poolnameStr is not None:
if six.text_type(poolNameInStr) == six.text_type(poolnameStr): if six.text_type(poolname) == six.text_type(poolnameStr):
try: foundPoolInstanceName = poolInstanceName
conn.GetInstance(srpPoolInstanceName)
foundPoolInstanceName = srpPoolInstanceName
except Exception:
foundPoolInstanceName = None
break break
return foundPoolInstanceName, systemNameStr return foundPoolInstanceName, systemNameStr
def _get_pool_name(self, conn, poolInstanceName):
"""The pool name from the instance
:param conn: the ecom connection
:param poolInstanceName: the pool instance
:returns: poolnameStr
"""
poolnameStr = None
try:
poolInstance = conn.GetInstance(poolInstanceName)
poolnameStr = poolInstance['ElementName']
except Exception:
pass
return poolnameStr
def find_storageSystem(self, conn, arrayStr): def find_storageSystem(self, conn, arrayStr):
"""Find an array instance name given the array name. """Find an array instance name given the array name.
@ -1831,21 +1845,28 @@ class EMCVMAXUtils(object):
LOG.debug("metaMembers: %(members)s.", {'members': metaMembers}) LOG.debug("metaMembers: %(members)s.", {'members': metaMembers})
return metaMembers return metaMembers
def get_meta_members_capacity_in_bit(self, conn, volumeInstanceNames): def get_meta_members_capacity_in_byte(self, conn, volumeInstanceNames):
"""Get the capacity in bits of all meta device member volumes. """Get the capacity in byte of all meta device member volumes.
:param conn: the ecom connection :param conn: the ecom connection
:param volumeInstanceNames: array contains meta device member volumes :param volumeInstanceNames: array contains meta device member volumes
:returns: array contains capacities of each member device in bits :returns: array contains capacities of each member device in bits
""" """
capacitiesInBit = [] capacitiesInByte = []
headVolume = conn.GetInstance(volumeInstanceNames[0])
totalSizeInByte = (
headVolume['ConsumableBlocks'] * headVolume['BlockSize'])
volumeInstanceNames.pop(0)
for volumeInstanceName in volumeInstanceNames: for volumeInstanceName in volumeInstanceNames:
volumeInstance = conn.GetInstance(volumeInstanceName) volumeInstance = conn.GetInstance(volumeInstanceName)
numOfBlocks = volumeInstance['ConsumableBlocks'] numOfBlocks = volumeInstance['ConsumableBlocks']
blockSize = volumeInstance['BlockSize'] blockSize = volumeInstance['BlockSize']
volumeSizeInbits = numOfBlocks * blockSize volumeSizeInByte = numOfBlocks * blockSize
capacitiesInBit.append(volumeSizeInbits) capacitiesInByte.append(volumeSizeInByte)
return capacitiesInBit totalSizeInByte = totalSizeInByte - volumeSizeInByte
capacitiesInByte.insert(0, totalSizeInByte)
return capacitiesInByte
def get_existing_instance(self, conn, instanceName): def get_existing_instance(self, conn, instanceName):
"""Check that the instance name still exists and return the instance. """Check that the instance name still exists and return the instance.
@ -2112,3 +2133,48 @@ class EMCVMAXUtils(object):
{'source': sourceDeviceId, 'storageSystem': storageSystem}) {'source': sourceDeviceId, 'storageSystem': storageSystem})
return foundSyncInstanceName 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