Merge "EMC VMAX - Extend Volume for VMAX3"
This commit is contained in:
commit
189047d244
@ -724,17 +724,40 @@ class FakeEcomConnection(object):
|
||||
def AssociatorNames(self, objectpath,
|
||||
ResultClass='default', AssocClass='default'):
|
||||
result = None
|
||||
if objectpath == 'point_to_storage_instance_names':
|
||||
result = ['FirstStorageTierInstanceNames']
|
||||
|
||||
if ResultClass != 'default':
|
||||
result = self.ResultClassHelper(ResultClass, objectpath)
|
||||
|
||||
if result is None and AssocClass != 'default':
|
||||
result = self.AssocClassHelper(AssocClass, objectpath)
|
||||
if result is None:
|
||||
result = self._default_assocnames(objectpath)
|
||||
return result
|
||||
|
||||
def AssocClassHelper(self, AssocClass, objectpath):
|
||||
if AssocClass == 'CIM_HostedService':
|
||||
result = self._assocnames_hostedservice()
|
||||
elif AssocClass == 'CIM_AssociatedTierPolicy':
|
||||
result = self._assocnames_assoctierpolicy()
|
||||
elif AssocClass == 'CIM_OrderedMemberOfCollection':
|
||||
result = self._enum_storagevolumes()
|
||||
elif AssocClass == 'CIM_BindsTo':
|
||||
result = self._assocnames_bindsto()
|
||||
elif AssocClass == 'CIM_MemberOfCollection':
|
||||
result = self._assocnames_memberofcollection()
|
||||
else:
|
||||
result = None
|
||||
return result
|
||||
|
||||
def ResultClassHelper(self, ResultClass, objectpath):
|
||||
if ResultClass == 'EMC_LunMaskingSCSIProtocolController':
|
||||
result = self._assocnames_lunmaskctrl()
|
||||
elif AssocClass == 'CIM_HostedService':
|
||||
result = self._assocnames_hostedservice()
|
||||
elif ResultClass == 'CIM_TierPolicyServiceCapabilities':
|
||||
result = self._assocnames_policyCapabilities()
|
||||
elif ResultClass == 'Symm_TierPolicyRule':
|
||||
result = self._assocnames_policyrule()
|
||||
elif AssocClass == 'CIM_AssociatedTierPolicy':
|
||||
result = self._assocnames_assoctierpolicy()
|
||||
elif ResultClass == 'CIM_StoragePool':
|
||||
result = self._assocnames_storagepool()
|
||||
elif ResultClass == 'EMC_VirtualProvisioningPool':
|
||||
@ -757,8 +780,6 @@ class FakeEcomConnection(object):
|
||||
result = self._enum_repservcpbls()
|
||||
elif ResultClass == 'CIM_ReplicationGroup':
|
||||
result = self._enum_repgroups()
|
||||
elif AssocClass == 'CIM_OrderedMemberOfCollection':
|
||||
result = self._enum_storagevolumes()
|
||||
elif ResultClass == 'Symm_FCSCSIProtocolEndpoint':
|
||||
result = self._enum_fcscsiendpoint()
|
||||
elif ResultClass == 'Symm_SRPStoragePool':
|
||||
@ -775,12 +796,12 @@ class FakeEcomConnection(object):
|
||||
result = self._enum_maskingView()
|
||||
elif ResultClass == 'EMC_Meta':
|
||||
result = self._enum_metavolume()
|
||||
elif AssocClass == 'CIM_BindsTo':
|
||||
result = self._assocnames_bindsto()
|
||||
elif AssocClass == 'CIM_MemberOfCollection':
|
||||
result = self._assocnames_memberofcollection()
|
||||
elif ResultClass == 'EMC_FrontEndSCSIProtocolController':
|
||||
result = self._enum_maskingView()
|
||||
elif ResultClass == 'CIM_TierPolicyRule':
|
||||
result = self._assocnames_tierpolicy(objectpath)
|
||||
else:
|
||||
result = self._default_assocnames(objectpath)
|
||||
result = None
|
||||
return result
|
||||
|
||||
def ReferenceNames(self, objectpath,
|
||||
@ -6094,6 +6115,68 @@ class EMCV3DriverTestCase(test.TestCase):
|
||||
self.data.remainingSLOCapacity)
|
||||
self.assertEqual(remainingSLOCapacityGb, remainingCapacityGb)
|
||||
|
||||
@mock.patch.object(
|
||||
emc_vmax_utils.EMCVMAXUtils,
|
||||
'get_volume_size',
|
||||
return_value='2147483648')
|
||||
def test_extend_volume(self, mock_volume_size):
|
||||
newSize = '2'
|
||||
self.driver.common._initial_setup = mock.Mock(
|
||||
return_value=self.default_extraspec())
|
||||
self.driver.extend_volume(self.data.test_volume_v3, newSize)
|
||||
|
||||
def test_extend_volume_smaller_size_exception(self):
|
||||
test_local_volume = {'name': 'vol1',
|
||||
'size': 4,
|
||||
'volume_name': 'vol1',
|
||||
'id': 'vol1',
|
||||
'device_id': '1',
|
||||
'provider_auth': None,
|
||||
'project_id': 'project',
|
||||
'display_name': 'vol1',
|
||||
'display_description': 'test volume',
|
||||
'volume_type_id': 'abc',
|
||||
'provider_location': six.text_type(
|
||||
self.data.provider_location),
|
||||
'status': 'available',
|
||||
'host': self.data.fake_host_v3,
|
||||
'NumberOfBlocks': 100,
|
||||
'BlockSize': self.data.block_size
|
||||
}
|
||||
newSize = '2'
|
||||
self.driver.common._initial_setup = mock.Mock(
|
||||
return_value=self.default_extraspec())
|
||||
self.assertRaises(
|
||||
exception.VolumeBackendAPIException,
|
||||
self.driver.extend_volume,
|
||||
test_local_volume, newSize)
|
||||
|
||||
def test_extend_volume_exception(self):
|
||||
common = self.driver.common
|
||||
newsize = '2'
|
||||
common._initial_setup = mock.Mock(return_value=None)
|
||||
common._find_lun = mock.Mock(return_value=None)
|
||||
self.assertRaises(
|
||||
exception.VolumeBackendAPIException,
|
||||
common.extend_volume,
|
||||
self.data.test_volume, newsize)
|
||||
|
||||
def test_extend_volume_size_tally_exception(self):
|
||||
common = self.driver.common
|
||||
newsize = '2'
|
||||
self.driver.common._initial_setup = mock.Mock(
|
||||
return_value=self.data.extra_specs)
|
||||
vol = {'SystemName': self.data.storage_system}
|
||||
common._find_lun = mock.Mock(return_value=vol)
|
||||
common._extend_v3_volume = mock.Mock(return_value=(0, vol))
|
||||
common.utils.find_volume_instance = mock.Mock(
|
||||
return_value='2147483648')
|
||||
common.utils.get_volume_size = mock.Mock(return_value='2147483646')
|
||||
self.assertRaises(
|
||||
exception.VolumeBackendAPIException,
|
||||
common.extend_volume,
|
||||
self.data.test_volume, newsize)
|
||||
|
||||
def _cleanup(self):
|
||||
bExists = os.path.exists(self.config_file_path)
|
||||
if bExists:
|
||||
@ -7031,3 +7114,63 @@ class EMCVMAXProvisionV3Test(test.TestCase):
|
||||
self.assertRaises(exception.VolumeBackendAPIException,
|
||||
provisionv3.get_storage_pool_setting,
|
||||
conn, storagePoolCapability, slo, workload)
|
||||
|
||||
def test_extend_volume_in_SG(self):
|
||||
provisionv3 = self.driver.common.provisionv3
|
||||
conn = FakeEcomConnection()
|
||||
storageConfigService = {
|
||||
'CreationClassName': 'Symm_ElementCompositionService',
|
||||
'SystemName': 'SYMMETRIX+000195900551'}
|
||||
theVolumeInstanceName = (
|
||||
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
||||
inVolumeInstanceName = (
|
||||
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
||||
volumeSize = 3
|
||||
|
||||
extraSpecs = {'volume_backend_name': 'GOLD_BE',
|
||||
'isV3': True}
|
||||
job = {
|
||||
'Job': {'InstanceID': '9999', 'status': 'success', 'type': None}}
|
||||
conn.InvokeMethod = mock.Mock(return_value=(4096, job))
|
||||
provisionv3.utils.wait_for_job_complete = mock.Mock(return_value=(
|
||||
0, 'Success'))
|
||||
volumeDict = {'classname': u'Symm_StorageVolume',
|
||||
'keybindings': EMCVMAXCommonData.keybindings}
|
||||
provisionv3.get_volume_dict_from_job = (
|
||||
mock.Mock(return_value=volumeDict))
|
||||
result = provisionv3.extend_volume_in_SG(conn, storageConfigService,
|
||||
theVolumeInstanceName,
|
||||
inVolumeInstanceName,
|
||||
volumeSize, extraSpecs)
|
||||
self.assertEqual(
|
||||
({'classname': u'Symm_StorageVolume',
|
||||
'keybindings': {
|
||||
'CreationClassName': u'Symm_StorageVolume',
|
||||
'DeviceID': u'1',
|
||||
'SystemCreationClassName': u'Symm_StorageSystem',
|
||||
'SystemName': u'SYMMETRIX+000195900551'}}, 0), result)
|
||||
|
||||
def test_extend_volume_in_SG_with_Exception(self):
|
||||
provisionv3 = self.driver.common.provisionv3
|
||||
conn = FakeEcomConnection()
|
||||
storageConfigService = {
|
||||
'CreationClassName': 'Symm_ElementCompositionService',
|
||||
'SystemName': 'SYMMETRIX+000195900551'}
|
||||
theVolumeInstanceName = (
|
||||
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
||||
inVolumeInstanceName = (
|
||||
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
||||
volumeSize = 3
|
||||
|
||||
extraSpecs = {'volume_backend_name': 'GOLD_BE',
|
||||
'isV3': True}
|
||||
job = {
|
||||
'Job': {'InstanceID': '9999', 'status': 'success', 'type': None}}
|
||||
conn.InvokeMethod = mock.Mock(return_value=(4096, job))
|
||||
provisionv3.utils.wait_for_job_complete = mock.Mock(return_value=(
|
||||
2, 'Failure'))
|
||||
self.assertRaises(
|
||||
exception.VolumeBackendAPIException,
|
||||
provisionv3.extend_volume_in_SG, conn, storageConfigService,
|
||||
theVolumeInstanceName, inVolumeInstanceName, volumeSize,
|
||||
extraSpecs)
|
||||
|
@ -520,10 +520,14 @@ class EMCVMAXCommon(object):
|
||||
additionalVolumeSize = self.utils.convert_gb_to_bits(
|
||||
additionalVolumeSize)
|
||||
|
||||
# This is V2
|
||||
rc, modifiedVolumeDict = self._extend_composite_volume(
|
||||
volumeInstance, volumeName, newSize, additionalVolumeSize,
|
||||
extraSpecs)
|
||||
if extraSpecs[ISV3]:
|
||||
rc, modifiedVolumeDict = self._extend_v3_volume(
|
||||
volumeInstance, volumeName, newSize, extraSpecs)
|
||||
else:
|
||||
# This is V2.
|
||||
rc, modifiedVolumeDict = self._extend_composite_volume(
|
||||
volumeInstance, volumeName, newSize, additionalVolumeSize,
|
||||
extraSpecs)
|
||||
|
||||
# Check the occupied space of the new extended volume.
|
||||
extendedVolumeInstance = self.utils.find_volume_instance(
|
||||
@ -4362,3 +4366,23 @@ class EMCVMAXCommon(object):
|
||||
conn, ipendpointinstancename))
|
||||
foundipaddresses.append(ipaddress)
|
||||
return foundipaddresses
|
||||
|
||||
def _extend_v3_volume(self, volumeInstance, volumeName, newSize,
|
||||
extraSpecs):
|
||||
"""Extends a VMAX3 volume.
|
||||
|
||||
:param volumeInstance: volume instance
|
||||
:param volumeName: volume name
|
||||
:param newSize: new size the volume will be increased to
|
||||
:param extraSpecs: extra specifications
|
||||
:returns: int -- return code
|
||||
:returns: volumeDict
|
||||
"""
|
||||
new_size_in_bits = int(self.utils.convert_gb_to_bits(newSize))
|
||||
storageConfigService = self.utils.find_storage_configuration_service(
|
||||
self.conn, volumeInstance['SystemName'])
|
||||
volumeDict, rc = self.provisionv3.extend_volume_in_SG(
|
||||
self.conn, storageConfigService, volumeInstance.path,
|
||||
volumeName, new_size_in_bits, extraSpecs)
|
||||
|
||||
return rc, volumeDict
|
||||
|
@ -47,6 +47,8 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
|
||||
- get_short_host_name needs to be called in find_device_number
|
||||
(bug #1520635)
|
||||
- Proper error handling for invalid SLOs (bug #1512795)
|
||||
- Extend Volume for VMAX3, SE8.1.0.3
|
||||
https://blueprints.launchpad.net/cinder/+spec/vmax3-extend-volume
|
||||
"""
|
||||
|
||||
VERSION = "2.3.0"
|
||||
|
@ -55,6 +55,8 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
|
||||
- get_short_host_name needs to be called in find_device_number
|
||||
(bug #1520635)
|
||||
- Proper error handling for invalid SLOs (bug #1512795)
|
||||
- Extend Volume for VMAX3, SE8.1.0.3
|
||||
https://blueprints.launchpad.net/cinder/+spec/vmax3-extend-volume
|
||||
"""
|
||||
|
||||
VERSION = "2.3.0"
|
||||
|
@ -676,3 +676,52 @@ class EMCVMAXProvisionV3(object):
|
||||
except KeyError:
|
||||
pass
|
||||
return remainingCapacityGb
|
||||
|
||||
def extend_volume_in_SG(
|
||||
self, conn, storageConfigService, volumeInstanceName,
|
||||
volumeName, volumeSize, extraSpecs):
|
||||
"""Extend a volume instance.
|
||||
|
||||
:param conn: connection the the ecom server
|
||||
:param storageConfigservice: the storage configuration service
|
||||
:param volumeInstanceName: the volume instance name
|
||||
:param volumeName: the volume name (String)
|
||||
:param volumeSize: the volume size
|
||||
:param extraSpecs: additional info
|
||||
:returns: volumeDict
|
||||
:returns: int -- return code
|
||||
:raises: VolumeBackendAPIException
|
||||
"""
|
||||
startTime = time.time()
|
||||
|
||||
rc, job = conn.InvokeMethod(
|
||||
'CreateOrModifyElementFromStoragePool',
|
||||
storageConfigService, TheElement=volumeInstanceName,
|
||||
Size=self.utils.get_num(volumeSize, '64'))
|
||||
|
||||
LOG.debug("Extend Volume: %(volumename)s. Return code: %(rc)lu.",
|
||||
{'volumename': volumeName,
|
||||
'rc': rc})
|
||||
|
||||
if rc != 0:
|
||||
rc, error_desc = self.utils.wait_for_job_complete(conn, job,
|
||||
extraSpecs)
|
||||
if rc != 0:
|
||||
exceptionMessage = (_(
|
||||
"Error Extend Volume: %(volumeName)s. "
|
||||
"Return code: %(rc)lu. Error: %(error)s.")
|
||||
% {'volumeName': volumeName,
|
||||
'rc': rc,
|
||||
'error': error_desc})
|
||||
LOG.error(exceptionMessage)
|
||||
raise exception.VolumeBackendAPIException(
|
||||
data=exceptionMessage)
|
||||
|
||||
LOG.debug("InvokeMethod CreateOrModifyElementFromStoragePool "
|
||||
"took: %(delta)s H:MM:SS.",
|
||||
{'delta': self.utils.get_time_delta(startTime,
|
||||
time.time())})
|
||||
|
||||
# Find the newly created volume.
|
||||
volumeDict = self.get_volume_dict_from_job(conn, job['Job'])
|
||||
return volumeDict, rc
|
||||
|
Loading…
x
Reference in New Issue
Block a user