Support SMI-S provider v8.0.3 in VMAX driver

This patch made changes in the VMAX driver to support
interface changes in SMI-S provider v8.0.3.

* Accommodate System Name prefix change from 'SYMMETRIX+' to
  'SYMMETRIX-+-'.
* Delete composite volume - changed from using
  ReturnElementsToStoragePool to EMCReturnToStoragePool.
* Extend volume - removed InPool parameter from EMCUnBindElements
  method.
* Create snapshot/clone with multi meta members -
  EMC_PartialAllocOfConcreteExtentCIM was deprecated;
  used 'GetCompositeElements' instead and changed how we get meta
  members' capacities.
* Create CG snapshot - A new WaitForCopyState parameter was added.
* Added helper function to determine SMI-S provider and SE version
  Note: SMI-S provider is bundled with SE (Solutions Enabler) and
  they have the same version number starting from v8.0.
  The EMC SMI-S Provider supports the SNIA Storage Management Initiative
  (SMI), an ANSI standard for storage management. It supports the VMAX
  storage system. SE is a software product that discovers and manages
  VMAX storage system.

Closes-Bug: #1463217
Change-Id: Ia6333396a8111f110f540c367e5018cfad6e93e4
This commit is contained in:
Xing Yang 2015-06-19 09:47:59 -04:00
parent cdfbcdca31
commit f6d5eb70a9
7 changed files with 353 additions and 124 deletions

View File

@ -294,6 +294,7 @@ class EMCVMAXCommonData(object):
properties = {'ConsumableBlocks': '12345',
'BlockSize': '512'}
block_size = 512
test_volume = {'name': 'vol1',
'size': 1,
'volume_name': 'vol1',
@ -308,7 +309,7 @@ class EMCVMAXCommonData(object):
'status': 'available',
'host': 'fake-host',
'NumberOfBlocks': 100,
'BlockSize': 512
'BlockSize': block_size
}
test_volume_v2 = {'name': 'vol1',
@ -325,7 +326,7 @@ class EMCVMAXCommonData(object):
'status': 'available',
'host': 'fake-host',
'NumberOfBlocks': 100,
'BlockSize': 512
'BlockSize': block_size
}
test_volume_v3 = {'name': 'vol1',
@ -342,9 +343,17 @@ class EMCVMAXCommonData(object):
'status': 'available',
'host': 'fake-host',
'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',
'consistencygroup_id': 'abc',
'size': 1,
@ -435,6 +444,9 @@ class EMCVMAXCommonData(object):
'storagetype:slo': u'Bronze',
'storagetype:array': u'0123456789',
'isV3': True}
majorVersion = 1
minorVersion = 2
revNumber = 3
class FakeLookupService(object):
@ -466,7 +478,8 @@ class FakeEcomConnection(object):
Type=None, EMCSRP=None, EMCSLO=None, EMCWorkload=None,
EMCCollections=None, InitiatorMaskingGroup=None,
DeviceMaskingGroup=None, TargetMaskingGroup=None,
ProtocolController=None, StorageID=None, IDType=None):
ProtocolController=None, StorageID=None, IDType=None,
WaitForCopyState=None):
rc = 0
myjob = SE_ConcreteJob()
@ -491,7 +504,7 @@ class FakeEcomConnection(object):
elif TheElements and \
TheElements[0]['DeviceID'] == '99999' and \
MethodName == 'EMCReturnToStoragePool':
MethodName == 'ReturnElementsToStoragePool':
rc = 10
myjob['status'] = 'failure'
elif HardwareId:
@ -519,6 +532,13 @@ class FakeEcomConnection(object):
rc = 0
ret['HardwareID'] = self.data.iscsi_initiator
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}
return rc, job
@ -561,6 +581,8 @@ class FakeEcomConnection(object):
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
@ -571,6 +593,8 @@ class FakeEcomConnection(object):
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
@ -960,7 +984,7 @@ class FakeEcomConnection(object):
def _getinstance_pool(self, objectpath):
pool = {}
pool['CreationClassName'] = 'Symm_VirtualProvisioningPool'
pool['ElementName'] = 'gold'
pool['ElementName'] = self.data.poolname
pool['SystemName'] = self.data.storage_system
pool['TotalManagedSpace'] = self.data.totalmanagedspace_bits
pool['EMCSubscribedCapacity'] = self.data.subscribedcapacity_bits
@ -977,6 +1001,7 @@ class FakeEcomConnection(object):
srpstoragepool = SYMM_SrpStoragePool()
srpstoragepool['CreationClassName'] = (
self.data.srpstoragepool_creationclass)
srpstoragepool['ElementName'] = 'SRP_1'
classcimproperty = Fake_CIMProperty()
totalManagedSpace = (
@ -1213,6 +1238,31 @@ class FakeEcomConnection(object):
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):
@ -1463,6 +1513,15 @@ class FakeEcomConnection(object):
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 = {}
@ -1688,18 +1747,39 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
self.assertEqual(storageHardwareIDInstanceNames[0],
self.data.iscsi_initiator)
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_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_get_hardware_type(self):
iqn_initiator = 'iqn.1992-04.com.emc: 50000973f006dd80'
@ -1916,19 +1996,19 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
volume2 = EMC_StorageVolume()
volume2['name'] = 'myVol'
volume2['provider_location'] = six.text_type(provider_location2)
verify_orig = self.driver.common.utils.get_existing_instance
self.driver.common.utils.get_existing_instance = mock.Mock(
verify_orig = self.driver.common.conn.GetInstance
self.driver.common.conn.GetInstance = mock.Mock(
return_value=None)
findlun2 = self.driver.common._find_lun(volume2)
# Not found.
self.assertIsNone(findlun2)
instancename2 = self.driver.utils.get_instance_name(
self.driver.utils.get_instance_name(
provider_location2['classname'],
keybindings2)
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
self.driver.common.conn.GetInstance.assert_called_once_with(
keybindings2)
self.driver.common.conn.GetInstance.reset_mock()
self.driver.common.conn.GetInstance = verify_orig
keybindings3 = {'CreationClassName': u'Symm_StorageVolume',
'SystemName': u'SYMMETRIX+000195900551',
@ -2652,7 +2732,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit',
'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
@ -2687,7 +2767,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit',
'get_meta_members_capacity_in_byte',
return_value=[1234567])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
@ -2748,7 +2828,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit',
'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321])
@mock.patch.object(
FakeDB,
@ -2908,7 +2988,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit',
'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
@ -3019,6 +3099,74 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
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)
@ -3393,7 +3541,7 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit',
'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
@ -3431,7 +3579,7 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit',
'get_meta_members_capacity_in_byte',
return_value=[1234567])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
@ -3481,7 +3629,7 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit',
'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
@ -4184,7 +4332,7 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
'status': 'available',
'host': self.data.fake_host,
'NumberOfBlocks': 100,
'BlockSize': 512
'BlockSize': self.data.block_size
}
common = self.driver.common
common._initial_setup = mock.Mock(
@ -4217,7 +4365,7 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
'status': 'available',
'host': self.data.fake_host,
'NumberOfBlocks': 100,
'BlockSize': 512
'BlockSize': self.data.block_size
}
common = self.driver.common
common._initial_setup = mock.Mock(
@ -4601,7 +4749,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit',
'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
@ -4639,7 +4787,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit',
'get_meta_members_capacity_in_byte',
return_value=[1234567])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
@ -4708,7 +4856,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
return_value=(None, EMCVMAXCommonData.storage_system))
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
'get_meta_members_capacity_in_bit',
'get_meta_members_capacity_in_byte',
return_value=[1234567, 7654321])
@mock.patch.object(
emc_vmax_utils.EMCVMAXUtils,
@ -5167,7 +5315,7 @@ class EMCV3DriverTestCase(test.TestCase):
cloneVol['volume_type_id'] = 'abc'
cloneVol['provider_location'] = None
cloneVol['NumberOfBlocks'] = 100
cloneVol['BlockSize'] = 512
cloneVol['BlockSize'] = self.data.block_size
self.driver.create_cloned_volume(cloneVol, self.data.test_volume)
@mock.patch.object(

View File

@ -55,6 +55,7 @@ STRIPECOUNT = 'storagetype:stripecount'
MEMBERCOUNT = 'storagetype:membercount'
STRIPED = 'striped'
CONCATENATED = 'concatenated'
SMI_VERSION_8 = 800
# V3
SLO = 'storagetype:slo'
WORKLOAD = 'storagetype:workload'
@ -1319,13 +1320,23 @@ class EMCVMAXCommon(object):
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.
foundVolumeinstance = self.utils.get_existing_instance(
self.conn, instancename)
try:
foundVolumeinstance = self.conn.GetInstance(instancename)
except Exception:
foundVolumeinstance = None
if foundVolumeinstance is None:
LOG.debug("Volume %(volumename)s not found on the array.",
@ -1835,8 +1846,8 @@ class EMCVMAXCommon(object):
if 'True' in isVolumeBound:
appendVolumeInstance = (
self._unbind_and_get_volume_from_storage_pool(
conn, storageConfigService, assocPoolInstanceName,
appendVolumeInstance.path, 'appendVolume', extraSpecs))
conn, storageConfigService, appendVolumeInstance.path,
'appendVolume', extraSpecs))
return appendVolumeInstance
@ -1862,27 +1873,33 @@ class EMCVMAXCommon(object):
return volumeInstance
def _unbind_and_get_volume_from_storage_pool(
self, conn, storageConfigService, poolInstanceName,
volumeInstanceName, volumeName, extraSpecs):
self, conn, storageConfigService, 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, poolInstanceName,
volumeInstanceName,
conn, storageConfigService, volumeInstanceName,
volumeName, extraSpecs))
volumeDict = self.provision.get_volume_dict_from_job(conn, job['Job'])
volumeInstance = self.utils.find_volume_instance(
self.conn, volumeDict, volumeName)
# 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)
return volumeInstance
def _modify_and_get_composite_volume_instance(
@ -3334,12 +3351,8 @@ class EMCVMAXCommon(object):
controllerConfigurationService,
volumeInstance.path, volumeName, extraSpecs)
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,
LOG.debug("Deleting Volume: %(name)s with deviceId: %(deviceId)s.",
{'name': volumeName,
'deviceId': deviceId})
try:
rc = self.provision.delete_volume_from_pool(
@ -3466,9 +3479,9 @@ class EMCVMAXCommon(object):
else: # Composite volume with meta device members.
# Check if the meta members capacity.
metaMemberInstanceNames = (
self.utils.get_meta_members_of_composite_volume(
self.conn, metaHeadInstanceName))
volumeCapacities = self.utils.get_meta_members_capacity_in_bit(
self.utils.get_composite_elements(
self.conn, sourceInstance))
volumeCapacities = self.utils.get_meta_members_capacity_in_byte(
self.conn, metaMemberInstanceNames)
LOG.debug("Volume capacities: %(metasizes)s.",
{'metasizes': volumeCapacities})

View File

@ -38,9 +38,10 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
2.1.2 - Clean up failed clones (bug #1440154)
2.1.3 - Fixed a problem with FAST support (bug #1435069)
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):

View File

@ -14,18 +14,18 @@
# under the License.
import base64
import httplib
import os
import socket
import ssl
import string
import struct
import urllib
from eventlet import patcher
import OpenSSL
from oslo_log import log as logging
import six
from six.moves import http_client
from six.moves import urllib
from cinder.i18n import _, _LI
@ -74,7 +74,7 @@ def get_default_ca_certs():
class OpenSSLConnectionDelegator(object):
"""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.
Note: Since it is not possible to inherit from OpenSSL.SSL.Connection
a delegator must be used.
@ -89,7 +89,7 @@ class OpenSSLConnectionDelegator(object):
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,
strict=None, ca_certs=None, no_verification=False):
if not pywbemAvailable:
@ -101,9 +101,9 @@ class HTTPSConnection(http_client.HTTPSConnection):
else:
excp_lst = ()
try:
http_client.HTTPSConnection.__init__(self, host, port,
key_file=key_file,
cert_file=cert_file)
httplib.HTTPSConnection.__init__(self, host, port,
key_file=key_file,
cert_file=cert_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
@ -255,7 +255,7 @@ def wbem_request(url, data, creds, headers=None, debug=0, x509=None,
"""Send request over HTTP.
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
files.
"""
@ -274,7 +274,7 @@ def wbem_request(url, data, creds, headers=None, debug=0, x509=None,
localAuthHeader = None
tryLimit = 5
if isinstance(data, six.text_type):
if isinstance(data, unicode):
data = data.encode('utf-8')
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)
for hdr in headers:
if isinstance(hdr, six.text_type):
if isinstance(hdr, unicode):
hdr = hdr.encode('utf-8')
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:
h.endheaders()
@ -328,7 +328,7 @@ def wbem_request(url, data, creds, headers=None, debug=0, x509=None,
if response.status != 200:
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.")
% {'arg': arg})
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.3 - Fixed a problem with FAST support (bug #1435069)
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):

View File

@ -64,7 +64,7 @@ class EMCVMAXProvision(object):
theElements = [volumeInstanceName]
rc, job = conn.InvokeMethod(
'EMCReturnToStoragePool', storageConfigservice,
'ReturnElementsToStoragePool', storageConfigservice,
TheElements=theElements)
if rc != 0:
@ -338,14 +338,13 @@ class EMCVMAXProvision(object):
time.time())})
def unbind_volume_from_storage_pool(
self, conn, storageConfigService, poolInstanceName,
volumeInstanceName, volumeName, extraSpecs):
self, conn, storageConfigService, 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 configuration service
instance name
:param poolInstanceName: the pool instance name
:param volumeInstanceName: the volume instance name
:param volumeName: the volume name
:param extraSpecs: additional info
@ -358,7 +357,6 @@ class EMCVMAXProvision(object):
rc, job = conn.InvokeMethod(
'EMCUnBindElement',
storageConfigService,
InPool=poolInstanceName,
TheElement=volumeInstanceName)
if rc != 0:
@ -1070,14 +1068,16 @@ class EMCVMAXProvision(object):
'relationName': relationName,
'srcGroup': srcGroupInstanceName,
'tgtGroup': tgtGroupInstanceName})
# 8 for clone.
# SyncType 8 - clone.
# CopyState 4 - Synchronized.
rc, job = conn.InvokeMethod(
'CreateGroupReplica',
replicationService,
RelationshipName=relationName,
SourceGroup=srcGroupInstanceName,
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:
rc, errordesc = self.utils.wait_for_job_complete(conn, job,

View File

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