Merge "EMC VMAX - Recreating SG when it has been deleted"
This commit is contained in:
@@ -484,6 +484,10 @@ class EMCVMAXCommonData(object):
|
|||||||
test_host_v3 = {'capabilities': location_info_v3,
|
test_host_v3 = {'capabilities': location_info_v3,
|
||||||
'host': fake_host_2_v3}
|
'host': fake_host_2_v3}
|
||||||
initiatorNames = ["123456789012345", "123456789054321"]
|
initiatorNames = ["123456789012345", "123456789054321"]
|
||||||
|
storagegroups = [{'CreationClassName': storagegroup_creationclass,
|
||||||
|
'ElementName': storagegroupname},
|
||||||
|
{'CreationClassName': storagegroup_creationclass,
|
||||||
|
'ElementName': 'OS-SRP_1-Bronze-DSS-SG'}]
|
||||||
test_ctxt = {}
|
test_ctxt = {}
|
||||||
new_type = {}
|
new_type = {}
|
||||||
diff = {}
|
diff = {}
|
||||||
@@ -2038,42 +2042,6 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
|
|||||||
self.assertRaises(exception.VolumeBackendAPIException,
|
self.assertRaises(exception.VolumeBackendAPIException,
|
||||||
self.driver.common.utils._get_random_portgroup, dom)
|
self.driver.common.utils._get_random_portgroup, dom)
|
||||||
|
|
||||||
def test_cleanup_last_vol(self):
|
|
||||||
conn = FakeEcomConnection()
|
|
||||||
masking = self.driver.common.masking
|
|
||||||
extraSpecs = {'volume_backend_name': 'GOLD_BE',
|
|
||||||
'isV3': True}
|
|
||||||
controllerConfigService = (
|
|
||||||
self.driver.utils.find_controller_configuration_service(
|
|
||||||
conn, self.data.storage_system))
|
|
||||||
storageGroupName = self.data.storagegroupname
|
|
||||||
storageGroupInstanceName = (
|
|
||||||
self.driver.utils.find_storage_masking_group(
|
|
||||||
conn, controllerConfigService, storageGroupName))
|
|
||||||
volumeInstance = EMC_StorageVolume()
|
|
||||||
volumeInstance.path = (
|
|
||||||
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
|
||||||
|
|
||||||
volumeName = self.data.test_volume['name']
|
|
||||||
masking._last_volume_delete_masking_view = mock.Mock()
|
|
||||||
storageSystemInstanceName = (
|
|
||||||
conn.EnumerateInstanceNames("EMC_StorageSystem")[0])
|
|
||||||
# Failure case, an exception is thrown in
|
|
||||||
# _remove_last_vol_and_delete_sg so the returning the vol to
|
|
||||||
# the default SG cannot continue
|
|
||||||
self.assertRaises(
|
|
||||||
exception.VolumeBackendAPIException,
|
|
||||||
masking._cleanup_last_vol, conn, controllerConfigService,
|
|
||||||
storageGroupInstanceName, storageGroupName, volumeInstance,
|
|
||||||
volumeName, storageSystemInstanceName, False, extraSpecs)
|
|
||||||
|
|
||||||
# Success case, the last vol is removed and the SG is deleted
|
|
||||||
masking._remove_last_vol_and_delete_sg = mock.Mock(return_value=True)
|
|
||||||
masking._cleanup_last_vol(
|
|
||||||
conn, controllerConfigService, storageGroupInstanceName,
|
|
||||||
storageGroupName, volumeInstance, volumeName,
|
|
||||||
storageSystemInstanceName, False, extraSpecs)
|
|
||||||
|
|
||||||
def test_is_sync_complete(self):
|
def test_is_sync_complete(self):
|
||||||
conn = self.fake_ecom_connection()
|
conn = self.fake_ecom_connection()
|
||||||
syncname = SE_ConcreteJob()
|
syncname = SE_ConcreteJob()
|
||||||
@@ -6307,6 +6275,8 @@ class EMCV3DriverTestCase(test.TestCase):
|
|||||||
common = self.driver.common
|
common = self.driver.common
|
||||||
common.get_target_wwns = mock.Mock(
|
common.get_target_wwns = mock.Mock(
|
||||||
return_value=EMCVMAXCommonData.target_wwns)
|
return_value=EMCVMAXCommonData.target_wwns)
|
||||||
|
common.masking.utils.find_storage_masking_group = mock.Mock(
|
||||||
|
return_value=self.data.storagegroups[0])
|
||||||
self.driver.common._initial_setup = mock.Mock(
|
self.driver.common._initial_setup = mock.Mock(
|
||||||
return_value=self.default_extraspec())
|
return_value=self.default_extraspec())
|
||||||
data = self.driver.terminate_connection(self.data.test_volume_v3,
|
data = self.driver.terminate_connection(self.data.test_volume_v3,
|
||||||
@@ -6361,15 +6331,11 @@ class EMCV3DriverTestCase(test.TestCase):
|
|||||||
extraSpecs = common._initial_setup(self.data.test_volume_v3)
|
extraSpecs = common._initial_setup(self.data.test_volume_v3)
|
||||||
targetInstance = (
|
targetInstance = (
|
||||||
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
||||||
storageGroupName = common.utils.get_v3_storage_group_name('SRP_1',
|
|
||||||
'Bronze',
|
|
||||||
'DSS')
|
|
||||||
deviceID = targetInstance['DeviceID']
|
deviceID = targetInstance['DeviceID']
|
||||||
common._delete_from_pool_v3.assert_called_with(storageConfigService,
|
common._delete_from_pool_v3.assert_called_with(storageConfigService,
|
||||||
targetInstance,
|
targetInstance,
|
||||||
targetInstance['Name'],
|
targetInstance['Name'],
|
||||||
deviceID,
|
deviceID,
|
||||||
storageGroupName,
|
|
||||||
extraSpecs)
|
extraSpecs)
|
||||||
|
|
||||||
def test_get_remaining_slo_capacity_wlp(self):
|
def test_get_remaining_slo_capacity_wlp(self):
|
||||||
@@ -7514,6 +7480,99 @@ class EMCVMAXMaskingTest(test.TestCase):
|
|||||||
controllerConfigService, maskingviewdict['volumeName']))
|
controllerConfigService, maskingviewdict['volumeName']))
|
||||||
self.assertIsNone(result)
|
self.assertIsNone(result)
|
||||||
|
|
||||||
|
def test_return_volume_to_default_storage_group_v3(self):
|
||||||
|
masking = self.driver.common.masking
|
||||||
|
conn = self.fake_ecom_connection()
|
||||||
|
volumeInstanceName = (
|
||||||
|
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
||||||
|
volumeInstance = conn.GetInstance(volumeInstanceName)
|
||||||
|
volumeName = "V3-Vol"
|
||||||
|
extraSpecs = {'volume_backend_name': 'V3_BE',
|
||||||
|
'isV3': True,
|
||||||
|
'storagetype:pool': 'SRP_1',
|
||||||
|
'storagetype:workload': 'DSS',
|
||||||
|
'storagetype:slo': 'Bronze'}
|
||||||
|
controllerConfigService = (
|
||||||
|
self.driver.utils.find_controller_configuration_service(
|
||||||
|
conn, self.data.storage_system))
|
||||||
|
masking.provisionv3.create_storage_group_v3 = mock.Mock(
|
||||||
|
return_value={'Value'})
|
||||||
|
masking._is_volume_in_storage_group = mock.Mock(
|
||||||
|
return_value=True)
|
||||||
|
masking.return_volume_to_default_storage_group_v3 = mock.Mock()
|
||||||
|
masking._return_back_to_default_sg(
|
||||||
|
conn, controllerConfigService, volumeInstance, volumeName,
|
||||||
|
extraSpecs)
|
||||||
|
masking.return_volume_to_default_storage_group_v3.assert_called_with(
|
||||||
|
conn, controllerConfigService,
|
||||||
|
volumeInstance, volumeName, extraSpecs)
|
||||||
|
|
||||||
|
def test_return_volume_to_default_storage_group_v3_exception(self):
|
||||||
|
masking = self.driver.common.masking
|
||||||
|
conn = self.fake_ecom_connection()
|
||||||
|
volumeInstanceName = (
|
||||||
|
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
||||||
|
volumeInstance = conn.GetInstance(volumeInstanceName)
|
||||||
|
volumeName = "V3-Vol"
|
||||||
|
extraSpecs = {'volume_backend_name': 'V3_BE',
|
||||||
|
'isV3': True,
|
||||||
|
'storagetype:pool': 'SRP_1',
|
||||||
|
'storagetype:workload': 'DSS',
|
||||||
|
'storagetype:slo': 'Bronze'}
|
||||||
|
controllerConfigService = (
|
||||||
|
self.driver.utils.find_controller_configuration_service(
|
||||||
|
conn, self.data.storage_system))
|
||||||
|
|
||||||
|
self.assertRaises(
|
||||||
|
exception.VolumeBackendAPIException,
|
||||||
|
masking.return_volume_to_default_storage_group_v3,
|
||||||
|
conn, controllerConfigService,
|
||||||
|
volumeInstance, volumeName, extraSpecs)
|
||||||
|
|
||||||
|
def test_add_volume_to_sg_and_verify(self):
|
||||||
|
masking = self.driver.common.masking
|
||||||
|
conn = self.fake_ecom_connection()
|
||||||
|
volumeInstanceName = (
|
||||||
|
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
||||||
|
volumeInstance = conn.GetInstance(volumeInstanceName)
|
||||||
|
volumeName = "V3-Vol"
|
||||||
|
storageGroupInstanceName = self.data.storagegroups[0]
|
||||||
|
sgGroupName = self.data.storagegroupname
|
||||||
|
extraSpecs = {'volume_backend_name': 'V3_BE',
|
||||||
|
'isV3': True,
|
||||||
|
'storagetype:pool': 'SRP_1',
|
||||||
|
'storagetype:workload': 'DSS',
|
||||||
|
'storagetype:slo': 'Bronze'}
|
||||||
|
controllerConfigService = (
|
||||||
|
self.driver.utils.find_controller_configuration_service(
|
||||||
|
conn, self.data.storage_system))
|
||||||
|
msg = masking._add_volume_to_sg_and_verify(
|
||||||
|
conn, controllerConfigService, storageGroupInstanceName,
|
||||||
|
volumeInstance, volumeName, sgGroupName, extraSpecs)
|
||||||
|
self.assertIsNone(msg)
|
||||||
|
|
||||||
|
def test_remove_volume_from_sg(self):
|
||||||
|
masking = self.driver.common.masking
|
||||||
|
conn = self.fake_ecom_connection()
|
||||||
|
volumeInstanceName = (
|
||||||
|
conn.EnumerateInstanceNames("EMC_StorageVolume")[0])
|
||||||
|
volumeInstance = conn.GetInstance(volumeInstanceName)
|
||||||
|
storageGroupInstanceName = self.data.storagegroups[1]
|
||||||
|
extraSpecs = {'volume_backend_name': 'V3_BE',
|
||||||
|
'isV3': True,
|
||||||
|
'storagetype:pool': 'SRP_1',
|
||||||
|
'storagetype:workload': 'DSS',
|
||||||
|
'storagetype:slo': 'Bronze'}
|
||||||
|
controllerConfigService = (
|
||||||
|
self.driver.utils.find_controller_configuration_service(
|
||||||
|
conn, self.data.storage_system))
|
||||||
|
masking._remove_volume_from_sg = mock.Mock()
|
||||||
|
masking._cleanup_deletion_v3(
|
||||||
|
conn, controllerConfigService, volumeInstance, extraSpecs)
|
||||||
|
masking._remove_volume_from_sg.assert_called_with(
|
||||||
|
conn, controllerConfigService, storageGroupInstanceName,
|
||||||
|
volumeInstance, extraSpecs)
|
||||||
|
|
||||||
|
|
||||||
class EMCVMAXFCTest(test.TestCase):
|
class EMCVMAXFCTest(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|||||||
@@ -414,7 +414,7 @@ class EMCVMAXCommon(object):
|
|||||||
return deviceInfoDict
|
return deviceInfoDict
|
||||||
|
|
||||||
def _attach_volume(self, volume, connector, extraSpecs,
|
def _attach_volume(self, volume, connector, extraSpecs,
|
||||||
maskingViewDict, isLiveMigration=None):
|
maskingViewDict, isLiveMigration=False):
|
||||||
"""Attach a volume to a host.
|
"""Attach a volume to a host.
|
||||||
|
|
||||||
If live migration is being undertaken then the volume
|
If live migration is being undertaken then the volume
|
||||||
@@ -2198,12 +2198,9 @@ class EMCVMAXCommon(object):
|
|||||||
deviceId = volumeInstance['DeviceID']
|
deviceId = volumeInstance['DeviceID']
|
||||||
|
|
||||||
if extraSpecs[ISV3]:
|
if extraSpecs[ISV3]:
|
||||||
storageGroupName = self.utils.get_v3_storage_group_name(
|
|
||||||
extraSpecs[POOL], extraSpecs[SLO],
|
|
||||||
extraSpecs[WORKLOAD])
|
|
||||||
rc = self._delete_from_pool_v3(
|
rc = self._delete_from_pool_v3(
|
||||||
storageConfigService, volumeInstance, volumeName,
|
storageConfigService, volumeInstance, volumeName,
|
||||||
deviceId, storageGroupName, extraSpecs)
|
deviceId, extraSpecs)
|
||||||
else:
|
else:
|
||||||
rc = self._delete_from_pool(storageConfigService, volumeInstance,
|
rc = self._delete_from_pool(storageConfigService, volumeInstance,
|
||||||
volumeName, deviceId,
|
volumeName, deviceId,
|
||||||
@@ -3432,15 +3429,13 @@ class EMCVMAXCommon(object):
|
|||||||
return rc
|
return rc
|
||||||
|
|
||||||
def _delete_from_pool_v3(self, storageConfigService, volumeInstance,
|
def _delete_from_pool_v3(self, storageConfigService, volumeInstance,
|
||||||
volumeName, deviceId, storageGroupName,
|
volumeName, deviceId, extraSpecs):
|
||||||
extraSpecs):
|
|
||||||
"""Delete from pool (v3).
|
"""Delete from pool (v3).
|
||||||
|
|
||||||
:param storageConfigService: the storage config service
|
:param storageConfigService: the storage config service
|
||||||
:param volumeInstance: the volume instance
|
:param volumeInstance: the volume instance
|
||||||
:param volumeName: the volume Name
|
:param volumeName: the volume Name
|
||||||
:param deviceId: the device ID of the volume
|
:param deviceId: the device ID of the volume
|
||||||
:param storageGroupName: the name of the default SG
|
|
||||||
:param extraSpecs: extra specifications
|
:param extraSpecs: extra specifications
|
||||||
:returns: int -- return code
|
:returns: int -- return code
|
||||||
:raises: VolumeBackendAPIException
|
:raises: VolumeBackendAPIException
|
||||||
@@ -3452,9 +3447,9 @@ class EMCVMAXCommon(object):
|
|||||||
|
|
||||||
# Check if it is part of a storage group and delete it
|
# Check if it is part of a storage group and delete it
|
||||||
# extra logic for case when volume is the last member.
|
# extra logic for case when volume is the last member.
|
||||||
sgFromVolInstanceName = self.masking.remove_and_reset_members(
|
self.masking.remove_and_reset_members(
|
||||||
self.conn, controllerConfigurationService, volumeInstance,
|
self.conn, controllerConfigurationService, volumeInstance,
|
||||||
volumeName, extraSpecs, None, 'noReset')
|
volumeName, extraSpecs, None, False)
|
||||||
|
|
||||||
LOG.debug("Delete Volume: %(name)s Method: EMCReturnToStoragePool "
|
LOG.debug("Delete Volume: %(name)s Method: EMCReturnToStoragePool "
|
||||||
"ConfigServic: %(service)s TheElement: %(vol_instance)s "
|
"ConfigServic: %(service)s TheElement: %(vol_instance)s "
|
||||||
@@ -3472,23 +3467,9 @@ class EMCVMAXCommon(object):
|
|||||||
# If we cannot successfully delete the volume, then we want to
|
# If we cannot successfully delete the volume, then we want to
|
||||||
# return the volume to the default storage group,
|
# return the volume to the default storage group,
|
||||||
# which should be the SG it previously belonged to.
|
# which should be the SG it previously belonged to.
|
||||||
storageGroupInstanceName = self.utils.find_storage_masking_group(
|
self.masking.return_volume_to_default_storage_group_v3(
|
||||||
self.conn, controllerConfigurationService, storageGroupName)
|
|
||||||
|
|
||||||
if sgFromVolInstanceName is not storageGroupInstanceName:
|
|
||||||
LOG.debug(
|
|
||||||
"Volume: %(volumeName)s was not previously part of "
|
|
||||||
" %(storageGroupInstanceName)s. "
|
|
||||||
"Returning to %(storageGroupName)s.",
|
|
||||||
{'volumeName': volumeName,
|
|
||||||
'storageGroupInstanceName': storageGroupInstanceName,
|
|
||||||
'storageGroupName': storageGroupName})
|
|
||||||
|
|
||||||
if storageGroupInstanceName is not None:
|
|
||||||
self.masking.add_volume_to_storage_group(
|
|
||||||
self.conn, controllerConfigurationService,
|
self.conn, controllerConfigurationService,
|
||||||
storageGroupInstanceName, volumeInstance, volumeName,
|
volumeInstance, volumeName, extraSpecs)
|
||||||
storageGroupName, extraSpecs)
|
|
||||||
|
|
||||||
errorMessage = (_("Failed to delete volume %(volumeName)s.") %
|
errorMessage = (_("Failed to delete volume %(volumeName)s.") %
|
||||||
{'volumeName': volumeName})
|
{'volumeName': volumeName})
|
||||||
@@ -3854,12 +3835,9 @@ class EMCVMAXCommon(object):
|
|||||||
self.conn, storageSystem))
|
self.conn, storageSystem))
|
||||||
deviceId = targetInstance['DeviceID']
|
deviceId = targetInstance['DeviceID']
|
||||||
volumeName = targetInstance['Name']
|
volumeName = targetInstance['Name']
|
||||||
storageGroupName = self.utils.get_v3_storage_group_name(
|
|
||||||
extraSpecs[POOL], extraSpecs[SLO],
|
|
||||||
extraSpecs[WORKLOAD])
|
|
||||||
rc = self._delete_from_pool_v3(
|
rc = self._delete_from_pool_v3(
|
||||||
storageConfigService, targetInstance, volumeName,
|
storageConfigService, targetInstance, volumeName,
|
||||||
deviceId, storageGroupName, extraSpecs)
|
deviceId, extraSpecs)
|
||||||
# Re-throw the exception.
|
# Re-throw the exception.
|
||||||
raise
|
raise
|
||||||
|
|
||||||
|
|||||||
@@ -519,19 +519,39 @@ class EMCVMAXMasking(object):
|
|||||||
if self._is_volume_in_storage_group(
|
if self._is_volume_in_storage_group(
|
||||||
conn, storageGroupInstanceName,
|
conn, storageGroupInstanceName,
|
||||||
volumeInstance, sgGroupName):
|
volumeInstance, sgGroupName):
|
||||||
LOG.debug(
|
LOG.warning(_LW(
|
||||||
"Volume: %(volumeName)s is already part "
|
"Volume: %(volumeName)s is already part "
|
||||||
"of storage group %(sgGroupName)s.",
|
"of storage group %(sgGroupName)s."),
|
||||||
{'volumeName': volumeName,
|
{'volumeName': volumeName,
|
||||||
'sgGroupName': sgGroupName})
|
'sgGroupName': sgGroupName})
|
||||||
else:
|
else:
|
||||||
|
msg = self._add_volume_to_sg_and_verify(
|
||||||
|
conn, controllerConfigService, storageGroupInstanceName,
|
||||||
|
volumeInstance, volumeName, sgGroupName,
|
||||||
|
maskingViewDict['extraSpecs'])
|
||||||
|
|
||||||
|
return msg
|
||||||
|
|
||||||
|
def _add_volume_to_sg_and_verify(
|
||||||
|
self, conn, controllerConfigService, storageGroupInstanceName,
|
||||||
|
volumeInstance, volumeName, sgGroupName, extraSpecs):
|
||||||
|
"""Add the volume to the storage group and double check it is there.
|
||||||
|
|
||||||
|
:param conn: the ecom connection
|
||||||
|
:param controllerConfigService: controller service
|
||||||
|
:param storageGroupInstanceName: storage group instance name
|
||||||
|
:param volumeInstance: the volume instance
|
||||||
|
:param volumeName: the volume name
|
||||||
|
:param sgGroupName: the storage group name
|
||||||
|
:param extraSpecs: the extra specifications
|
||||||
|
:returns: string -- the error message
|
||||||
|
"""
|
||||||
|
msg = None
|
||||||
self.add_volume_to_storage_group(
|
self.add_volume_to_storage_group(
|
||||||
conn, controllerConfigService,
|
conn, controllerConfigService, storageGroupInstanceName,
|
||||||
storageGroupInstanceName, volumeInstance, volumeName,
|
volumeInstance, volumeName, sgGroupName, extraSpecs)
|
||||||
sgGroupName, maskingViewDict['extraSpecs'])
|
|
||||||
if not self._is_volume_in_storage_group(
|
if not self._is_volume_in_storage_group(
|
||||||
conn, storageGroupInstanceName,
|
conn, storageGroupInstanceName, volumeInstance, sgGroupName):
|
||||||
volumeInstance, sgGroupName):
|
|
||||||
# This may be used in exception hence _ instead of _LE.
|
# This may be used in exception hence _ instead of _LE.
|
||||||
msg = (_(
|
msg = (_(
|
||||||
"Volume: %(volumeName)s was not added "
|
"Volume: %(volumeName)s was not added "
|
||||||
@@ -540,11 +560,10 @@ class EMCVMAXMasking(object):
|
|||||||
'sgGroupName': sgGroupName})
|
'sgGroupName': sgGroupName})
|
||||||
LOG.error(msg)
|
LOG.error(msg)
|
||||||
else:
|
else:
|
||||||
LOG.info(_LI(
|
LOG.info(_LI("Successfully added %(volumeName)s to "
|
||||||
"Successfully added %(volumeName)s to %(sgGroupName)s."),
|
"%(sgGroupName)s."),
|
||||||
{'volumeName': volumeName,
|
{'volumeName': volumeName,
|
||||||
'sgGroupName': sgGroupName})
|
'sgGroupName': sgGroupName})
|
||||||
|
|
||||||
return msg
|
return msg
|
||||||
|
|
||||||
def _get_and_remove_from_storage_group_v2(
|
def _get_and_remove_from_storage_group_v2(
|
||||||
@@ -1747,14 +1766,11 @@ class EMCVMAXMasking(object):
|
|||||||
|
|
||||||
def remove_and_reset_members(
|
def remove_and_reset_members(
|
||||||
self, conn, controllerConfigService, volumeInstance,
|
self, conn, controllerConfigService, volumeInstance,
|
||||||
volumeName, extraSpecs, connector=None, noReset=None):
|
volumeName, extraSpecs, connector=None, reset=True):
|
||||||
"""Part of unmap device or rollback.
|
"""This is called on a delete, unmap device or rollback.
|
||||||
|
|
||||||
Removes volume from the Device Masking Group that belongs to a
|
If the connector is not None get the associated SG and remove volume
|
||||||
Masking View. Check if fast policy is in the extra specs, if it isn't
|
from the storage group, otherwise it is a VMAX3 deletion.
|
||||||
we do not need to do any thing for FAST. Assume that
|
|
||||||
isTieringPolicySupported is False unless the FAST policy is in
|
|
||||||
the extra specs and tiering is enabled on the array.
|
|
||||||
|
|
||||||
:param conn: connection the the ecom server
|
:param conn: connection the the ecom server
|
||||||
:param controllerConfigService: the controller configuration service
|
:param controllerConfigService: the controller configuration service
|
||||||
@@ -1762,33 +1778,72 @@ class EMCVMAXMasking(object):
|
|||||||
:param volumeName: the volume name
|
:param volumeName: the volume name
|
||||||
:param extraSpecs: additional info
|
:param extraSpecs: additional info
|
||||||
:param connector: optional
|
:param connector: optional
|
||||||
:param noReset: optional, if none, then reset
|
:param reset: reset, return to original SG (optional)
|
||||||
:returns: storageGroupInstanceName
|
:returns: storageGroupInstanceName
|
||||||
"""
|
"""
|
||||||
fastPolicyName = extraSpecs.get(FASTPOLICY, None)
|
|
||||||
isV3 = extraSpecs[ISV3]
|
|
||||||
storageGroupInstanceName = None
|
storageGroupInstanceName = None
|
||||||
if connector is not None:
|
if connector is not None:
|
||||||
storageGroupInstanceName = self._get_sg_associated_with_connector(
|
storageGroupInstanceName = self._get_sg_associated_with_connector(
|
||||||
conn, controllerConfigService, volumeInstance.path,
|
conn, controllerConfigService, volumeInstance.path,
|
||||||
volumeName, connector)
|
volumeName, connector)
|
||||||
if storageGroupInstanceName is None:
|
if storageGroupInstanceName:
|
||||||
return None
|
self._remove_volume_from_sg(
|
||||||
|
conn, controllerConfigService, storageGroupInstanceName,
|
||||||
|
volumeInstance, extraSpecs)
|
||||||
else: # Connector is None in V3 volume deletion case.
|
else: # Connector is None in V3 volume deletion case.
|
||||||
|
self._cleanup_deletion_v3(
|
||||||
|
conn, controllerConfigService, volumeInstance, extraSpecs)
|
||||||
|
if reset:
|
||||||
|
self._return_back_to_default_sg(
|
||||||
|
conn, controllerConfigService, volumeInstance, volumeName,
|
||||||
|
extraSpecs)
|
||||||
|
|
||||||
|
return storageGroupInstanceName
|
||||||
|
|
||||||
|
def _cleanup_deletion_v3(
|
||||||
|
self, conn, controllerConfigService, volumeInstance, extraSpecs):
|
||||||
|
"""Pre cleanup before VMAX3 deletion operation
|
||||||
|
|
||||||
|
:param conn: the ecom connection
|
||||||
|
:param controllerConfigService: storage system instance name
|
||||||
|
:param volumeInstance: the volume instance
|
||||||
|
:param extraSpecs: the extra specifications
|
||||||
|
"""
|
||||||
storageGroupInstanceNames = (
|
storageGroupInstanceNames = (
|
||||||
self.get_associated_masking_groups_from_device(
|
self.get_associated_masking_groups_from_device(
|
||||||
conn, volumeInstance.path))
|
conn, volumeInstance.path))
|
||||||
|
|
||||||
if storageGroupInstanceNames:
|
if storageGroupInstanceNames:
|
||||||
storageGroupInstanceName = storageGroupInstanceNames[0]
|
sgNum = len(storageGroupInstanceNames)
|
||||||
else:
|
if len(storageGroupInstanceNames) > 1:
|
||||||
return None
|
LOG.warning(_LW("Volume %(volumeName)s is belong to "
|
||||||
|
"%(sgNum)s storage groups."),
|
||||||
|
{'volumeName': volumeInstance['ElementName'],
|
||||||
|
'sgNum': sgNum})
|
||||||
|
for storageGroupInstanceName in storageGroupInstanceNames:
|
||||||
|
self._remove_volume_from_sg(
|
||||||
|
conn, controllerConfigService,
|
||||||
|
storageGroupInstanceName,
|
||||||
|
volumeInstance,
|
||||||
|
extraSpecs)
|
||||||
|
|
||||||
|
def _remove_volume_from_sg(
|
||||||
|
self, conn, controllerConfigService, storageGroupInstanceName,
|
||||||
|
volumeInstance, extraSpecs):
|
||||||
|
|
||||||
|
"""Remove volume from storage group
|
||||||
|
|
||||||
|
:param conn: the ecom connection
|
||||||
|
:param controllerConfigService: storage system instance name
|
||||||
|
:param storageGroupInstanceName: the SG instance name
|
||||||
|
:param volumeInstance: the volume instance
|
||||||
|
:param extraSpecs: the extra specifications
|
||||||
|
"""
|
||||||
instance = conn.GetInstance(storageGroupInstanceName, LocalOnly=False)
|
instance = conn.GetInstance(storageGroupInstanceName, LocalOnly=False)
|
||||||
storageGroupName = instance['ElementName']
|
storageGroupName = instance['ElementName']
|
||||||
|
|
||||||
volumeInstanceNames = self.get_devices_from_storage_group(
|
volumeInstanceNames = self.get_devices_from_storage_group(
|
||||||
conn, storageGroupInstanceName)
|
conn, storageGroupInstanceName)
|
||||||
storageSystemInstanceName = self.utils.find_storage_system(
|
|
||||||
conn, controllerConfigService)
|
|
||||||
|
|
||||||
numVolInStorageGroup = len(volumeInstanceNames)
|
numVolInStorageGroup = len(volumeInstanceNames)
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
@@ -1797,25 +1852,18 @@ class EMCVMAXMasking(object):
|
|||||||
{'numVol': numVolInStorageGroup,
|
{'numVol': numVolInStorageGroup,
|
||||||
'maskingGroup': storageGroupInstanceName})
|
'maskingGroup': storageGroupInstanceName})
|
||||||
|
|
||||||
if not isV3:
|
|
||||||
isTieringPolicySupported, __ = (
|
|
||||||
self._get_tiering_info(conn, storageSystemInstanceName,
|
|
||||||
fastPolicyName))
|
|
||||||
|
|
||||||
if numVolInStorageGroup == 1:
|
if numVolInStorageGroup == 1:
|
||||||
# Last volume in the storage group.
|
# Last volume in the storage group.
|
||||||
self._last_vol_in_SG(
|
self._last_vol_in_SG(
|
||||||
conn, controllerConfigService, storageGroupInstanceName,
|
conn, controllerConfigService, storageGroupInstanceName,
|
||||||
storageGroupName, volumeInstance, volumeName, extraSpecs)
|
storageGroupName, volumeInstance,
|
||||||
|
volumeInstance['ElementName'], extraSpecs)
|
||||||
else:
|
else:
|
||||||
# Not the last volume so remove it from storage group in
|
# Not the last volume so remove it from storage group
|
||||||
# the masking view.
|
|
||||||
self._multiple_vols_in_SG(
|
self._multiple_vols_in_SG(
|
||||||
conn, controllerConfigService, storageGroupInstanceName,
|
conn, controllerConfigService, storageGroupInstanceName,
|
||||||
storageGroupName, volumeInstance, volumeName,
|
volumeInstance, volumeInstance['ElementName'],
|
||||||
numVolInStorageGroup, fastPolicyName, extraSpecs)
|
numVolInStorageGroup, extraSpecs)
|
||||||
|
|
||||||
return storageGroupInstanceName
|
|
||||||
|
|
||||||
def _last_vol_in_SG(
|
def _last_vol_in_SG(
|
||||||
self, conn, controllerConfigService, storageGroupInstanceName,
|
self, conn, controllerConfigService, storageGroupInstanceName,
|
||||||
@@ -1881,29 +1929,20 @@ class EMCVMAXMasking(object):
|
|||||||
|
|
||||||
def _multiple_vols_in_SG(
|
def _multiple_vols_in_SG(
|
||||||
self, conn, controllerConfigService, storageGroupInstanceName,
|
self, conn, controllerConfigService, storageGroupInstanceName,
|
||||||
storageGroupName, volumeInstance, volumeName, numVolsInSG,
|
volumeInstance, volumeName, numVolsInSG, extraSpecs):
|
||||||
fastPolicyName, extraSpecs):
|
"""If the volume is not the last in the storage group
|
||||||
"""Necessary steps if the volume is not the last in the SG.
|
|
||||||
|
|
||||||
1. Remove the volume from the SG.
|
Remove the volume from the SG.
|
||||||
2. Return the volume to default SG if necessary.
|
|
||||||
|
|
||||||
:param conn: the ecom connection
|
:param conn: the ecom connection
|
||||||
:param controllerConfigService: storage system instance name
|
:param controllerConfigService: storage system instance name
|
||||||
:param storageGroupInstanceName: the SG instance name
|
:param storageGroupInstanceName: the SG instance name
|
||||||
:param storageGroupName: the Storage group name (String)
|
|
||||||
:param volumeInstance: the volume instance
|
:param volumeInstance: the volume instance
|
||||||
:param volumeName: the volume name
|
:param volumeName: the volume name
|
||||||
:param numVolsInSG: the number of volumes in the SG
|
:param numVolsInSG: the number of volumes in the SG
|
||||||
:param fastPolicyName: the FAST policy name
|
|
||||||
:param extraSpecs: the extra specifications
|
:param extraSpecs: the extra specifications
|
||||||
"""
|
"""
|
||||||
storageSystemInstanceName = self.utils.find_storage_system(
|
|
||||||
conn, controllerConfigService)
|
|
||||||
if not extraSpecs[ISV3]:
|
|
||||||
isTieringPolicySupported, __ = (
|
|
||||||
self._get_tiering_info(conn, storageSystemInstanceName,
|
|
||||||
fastPolicyName))
|
|
||||||
LOG.debug("Start: number of volumes in masking storage group: "
|
LOG.debug("Start: number of volumes in masking storage group: "
|
||||||
"%(numVol)d", {'numVol': numVolsInSG})
|
"%(numVol)d", {'numVol': numVolsInSG})
|
||||||
self.provision.remove_device_from_storage_group(
|
self.provision.remove_device_from_storage_group(
|
||||||
@@ -1914,19 +1953,6 @@ class EMCVMAXMasking(object):
|
|||||||
"RemoveMembers for volume %(volumeName)s completed "
|
"RemoveMembers for volume %(volumeName)s completed "
|
||||||
"successfully.", {'volumeName': volumeName})
|
"successfully.", {'volumeName': volumeName})
|
||||||
|
|
||||||
# Add it back to the default storage group.
|
|
||||||
if extraSpecs[ISV3]:
|
|
||||||
self._return_volume_to_default_storage_group_v3(
|
|
||||||
conn, controllerConfigService, storageGroupName,
|
|
||||||
volumeInstance, volumeName, storageSystemInstanceName,
|
|
||||||
extraSpecs)
|
|
||||||
else:
|
|
||||||
# V2 if FAST POLICY enabled, move the volume to the default SG.
|
|
||||||
if fastPolicyName is not None and isTieringPolicySupported:
|
|
||||||
self._cleanup_tiering(
|
|
||||||
conn, controllerConfigService, fastPolicyName,
|
|
||||||
volumeInstance, volumeName, extraSpecs)
|
|
||||||
|
|
||||||
volumeInstanceNames = self.get_devices_from_storage_group(
|
volumeInstanceNames = self.get_devices_from_storage_group(
|
||||||
conn, storageGroupInstanceName)
|
conn, storageGroupInstanceName)
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
@@ -1939,10 +1965,6 @@ class EMCVMAXMasking(object):
|
|||||||
volumeInstance, volumeName, extraSpecs):
|
volumeInstance, volumeName, extraSpecs):
|
||||||
"""Delete the Masking view, the storage Group and the initiator group.
|
"""Delete the Masking view, the storage Group and the initiator group.
|
||||||
|
|
||||||
Also does necessary cleanup like removing the policy from the
|
|
||||||
storage group for V2 and returning the volume to the default
|
|
||||||
storage group.
|
|
||||||
|
|
||||||
:param conn: connection the the ecom server
|
:param conn: connection the the ecom server
|
||||||
:param controllerConfigService: the controller configuration service
|
:param controllerConfigService: the controller configuration service
|
||||||
:param mvInstanceName: masking view instance name
|
:param mvInstanceName: masking view instance name
|
||||||
@@ -1978,56 +2000,49 @@ class EMCVMAXMasking(object):
|
|||||||
storageSystemInstanceName['Name'],
|
storageSystemInstanceName['Name'],
|
||||||
storageGroupInstanceName, extraSpecs)
|
storageGroupInstanceName, extraSpecs)
|
||||||
|
|
||||||
self._cleanup_last_vol(
|
|
||||||
conn, controllerConfigService, storageGroupInstanceName,
|
|
||||||
storageGroupName, volumeInstance, volumeName,
|
|
||||||
storageSystemInstanceName, isTieringPolicySupported, extraSpecs)
|
|
||||||
|
|
||||||
def _cleanup_last_vol(
|
|
||||||
self, conn, controllerConfigService, storageGroupInstanceName,
|
|
||||||
storageGroupName, volumeInstance, volumeName,
|
|
||||||
storageSystemInstanceName, isTieringPolicySupported, extraSpecs):
|
|
||||||
"""Do necessary cleanup when the volume is the last in the SG.
|
|
||||||
|
|
||||||
This includes removing the last volume from the SG and deleting the
|
|
||||||
SG. It also means moving the volume to the default SG for VMAX3 and
|
|
||||||
FAST for VMAX2.
|
|
||||||
|
|
||||||
:param conn: connection the the ecom server
|
|
||||||
:param controllerConfigService: the controller configuration service
|
|
||||||
:param storageGroupInstanceName: storage group instance name
|
|
||||||
:param storageGroupName: the storage group name
|
|
||||||
:param volumeInstance: the volume Instance
|
|
||||||
:param volumeName: the volume name
|
|
||||||
:param storageSystemInstanceName: the storage system instance name
|
|
||||||
:param isTieringPolicySupported: tiering policy supported flag
|
|
||||||
:param extraSpecs: extra specs
|
|
||||||
"""
|
|
||||||
# Remove the last volume and delete the storage group.
|
|
||||||
self._remove_last_vol_and_delete_sg(
|
self._remove_last_vol_and_delete_sg(
|
||||||
conn, controllerConfigService, storageGroupInstanceName,
|
conn, controllerConfigService, storageGroupInstanceName,
|
||||||
storageGroupName, volumeInstance.path, volumeName,
|
storageGroupName, volumeInstance.path, volumeName,
|
||||||
extraSpecs)
|
extraSpecs)
|
||||||
|
|
||||||
|
LOG.debug(
|
||||||
|
"Volume %(volumeName)s successfully removed from SG and "
|
||||||
|
"Storage Group %(storageGroupName)s successfully deleted. ",
|
||||||
|
{'volumeName': volumeName,
|
||||||
|
'storageGroupName': storageGroupName})
|
||||||
|
|
||||||
|
def _return_back_to_default_sg(
|
||||||
|
self, conn, controllerConfigService, volumeInstance, volumeName,
|
||||||
|
extraSpecs):
|
||||||
|
"""Return volume to default storage group
|
||||||
|
|
||||||
|
Moving the volume to the default SG for VMAX3 and
|
||||||
|
FAST for VMAX2.
|
||||||
|
|
||||||
|
:param conn: connection the the ecom server
|
||||||
|
:param controllerConfigService: the controller configuration service
|
||||||
|
:param volumeInstance: the volume Instance
|
||||||
|
:param volumeName: the volume name
|
||||||
|
:param extraSpecs: extra specs
|
||||||
|
"""
|
||||||
# Add it back to the default storage group.
|
# Add it back to the default storage group.
|
||||||
if extraSpecs[ISV3]:
|
if extraSpecs[ISV3]:
|
||||||
self._return_volume_to_default_storage_group_v3(
|
self.return_volume_to_default_storage_group_v3(
|
||||||
conn, controllerConfigService, storageGroupName,
|
conn, controllerConfigService,
|
||||||
volumeInstance, volumeName, storageSystemInstanceName,
|
volumeInstance, volumeName, extraSpecs)
|
||||||
extraSpecs)
|
|
||||||
else:
|
else:
|
||||||
# V2 if FAST POLICY enabled, move the volume to the default
|
# V2 if FAST POLICY enabled, move the volume to the default
|
||||||
# SG.
|
# SG.
|
||||||
fastPolicyName = extraSpecs.get(FASTPOLICY, None)
|
fastPolicyName = extraSpecs.get(FASTPOLICY, None)
|
||||||
|
storageSystemInstanceName = self.utils.find_storage_system(
|
||||||
|
conn, controllerConfigService)
|
||||||
|
isTieringPolicySupported, __ = (
|
||||||
|
self._get_tiering_info(conn, storageSystemInstanceName,
|
||||||
|
fastPolicyName))
|
||||||
if fastPolicyName is not None and isTieringPolicySupported:
|
if fastPolicyName is not None and isTieringPolicySupported:
|
||||||
self._cleanup_tiering(
|
self._cleanup_tiering(
|
||||||
conn, controllerConfigService, fastPolicyName,
|
conn, controllerConfigService, fastPolicyName,
|
||||||
volumeInstance, volumeName, extraSpecs)
|
volumeInstance, volumeName, extraSpecs)
|
||||||
LOG.debug(
|
|
||||||
"Volume %(volumeName)s successfully removed from SG and "
|
|
||||||
"returned to default storage group where applicable. "
|
|
||||||
"Storage Group %(storageGroupName)s successfully deleted. ",
|
|
||||||
{'volumeName': volumeName,
|
|
||||||
'storageGroupName': storageGroupName})
|
|
||||||
|
|
||||||
def _get_sg_associated_with_connector(
|
def _get_sg_associated_with_connector(
|
||||||
self, conn, controllerConfigService, volumeInstanceName,
|
self, conn, controllerConfigService, volumeInstanceName,
|
||||||
@@ -2141,51 +2156,41 @@ class EMCVMAXMasking(object):
|
|||||||
conn, tierPolicyServiceInstanceName,
|
conn, tierPolicyServiceInstanceName,
|
||||||
storageGroupInstanceName, tierPolicyInstanceName, extraSpecs)
|
storageGroupInstanceName, tierPolicyInstanceName, extraSpecs)
|
||||||
|
|
||||||
def _return_volume_to_default_storage_group_v3(
|
def return_volume_to_default_storage_group_v3(
|
||||||
self, conn, controllerConfigService, storageGroupName,
|
self, conn, controllerConfigurationService,
|
||||||
volumeInstance, volumeName, storageSystemInstanceName,
|
volumeInstance, volumeName, extraSpecs):
|
||||||
extraSpecs):
|
|
||||||
"""Return volume to the default storage group in v3.
|
"""Return volume to the default storage group in v3.
|
||||||
|
|
||||||
:param conn: the ecom connection
|
:param conn: the ecom connection
|
||||||
:param controllerConfigService: controller config service
|
:param controllerConfigService: controller config service
|
||||||
:param storageGroupName: storage group name
|
|
||||||
:param volumeInstance: volumeInstance
|
:param volumeInstance: volumeInstance
|
||||||
:param volumeName: the volume name
|
:param volumeName: the volume name
|
||||||
:param storageSystemInstanceName: the storage system instance name
|
|
||||||
:param extraSpecs: additional info
|
:param extraSpecs: additional info
|
||||||
:raises: VolumeBackendAPIException
|
:raises: VolumeBackendAPIException
|
||||||
"""
|
"""
|
||||||
# First strip the shortHostname from the storage group name.
|
storageGroupName = self.utils.get_v3_storage_group_name(
|
||||||
defaultStorageGroupName, shorthostName = (
|
extraSpecs[self.utils.POOL], extraSpecs[self.utils.SLO],
|
||||||
self.utils.strip_short_host_name(storageGroupName))
|
extraSpecs[self.utils.WORKLOAD])
|
||||||
|
storageGroupInstanceName = self.utils.find_storage_masking_group(
|
||||||
|
conn, controllerConfigurationService, storageGroupName)
|
||||||
|
|
||||||
# Check if host name exists which signifies detach operation.
|
if not storageGroupInstanceName:
|
||||||
if shorthostName is not None:
|
storageGroupInstanceName = (
|
||||||
# Populate maskingViewDict and storageGroupInstanceName.
|
self.provisionv3.create_storage_group_v3(
|
||||||
maskingViewDict = {}
|
conn, controllerConfigurationService, storageGroupName,
|
||||||
maskingViewDict['sgGroupName'] = defaultStorageGroupName
|
extraSpecs[self.utils.POOL], extraSpecs[self.utils.SLO],
|
||||||
maskingViewDict['volumeInstance'] = volumeInstance
|
extraSpecs[self.utils.WORKLOAD], extraSpecs))
|
||||||
maskingViewDict['volumeName'] = volumeName
|
if not storageGroupInstanceName:
|
||||||
maskingViewDict['controllerConfigService'] = (
|
errorMessage = (_("Failed to create storage group "
|
||||||
controllerConfigService)
|
"%(storageGroupName)s.") %
|
||||||
maskingViewDict['storageSystemName'] = (
|
{'storageGroupName': storageGroupName})
|
||||||
storageSystemInstanceName)
|
|
||||||
sgInstanceName = self.utils.find_storage_masking_group(
|
|
||||||
conn, controllerConfigService, defaultStorageGroupName)
|
|
||||||
if sgInstanceName is not None:
|
|
||||||
errorMessage = (
|
|
||||||
self._check_adding_volume_to_storage_group(
|
|
||||||
conn, maskingViewDict,
|
|
||||||
sgInstanceName))
|
|
||||||
else:
|
|
||||||
errorMessage = (_(
|
|
||||||
"Storage group %(sgGroupName)s "
|
|
||||||
"does not exist.")
|
|
||||||
% {'sgGroupName': defaultStorageGroupName})
|
|
||||||
LOG.error(errorMessage)
|
LOG.error(errorMessage)
|
||||||
raise exception.VolumeBackendAPIException(
|
raise exception.VolumeBackendAPIException(data=errorMessage)
|
||||||
data=errorMessage)
|
|
||||||
|
self._add_volume_to_sg_and_verify(
|
||||||
|
conn, controllerConfigurationService,
|
||||||
|
storageGroupInstanceName, volumeInstance, volumeName,
|
||||||
|
storageGroupName, extraSpecs)
|
||||||
|
|
||||||
def _cleanup_tiering(
|
def _cleanup_tiering(
|
||||||
self, conn, controllerConfigService, fastPolicyName,
|
self, conn, controllerConfigService, fastPolicyName,
|
||||||
|
|||||||
@@ -64,6 +64,9 @@ class EMCVMAXUtils(object):
|
|||||||
This Utility class is for EMC volume drivers based on SMI-S.
|
This Utility class is for EMC volume drivers based on SMI-S.
|
||||||
It supports VMAX arrays.
|
It supports VMAX arrays.
|
||||||
"""
|
"""
|
||||||
|
SLO = 'storagetype:slo'
|
||||||
|
WORKLOAD = 'storagetype:workload'
|
||||||
|
POOL = 'storagetype:pool'
|
||||||
|
|
||||||
def __init__(self, prtcl):
|
def __init__(self, prtcl):
|
||||||
if not pywbemAvailable:
|
if not pywbemAvailable:
|
||||||
@@ -1528,16 +1531,6 @@ class EMCVMAXUtils(object):
|
|||||||
'workload': workload})
|
'workload': workload})
|
||||||
return storageGroupName
|
return storageGroupName
|
||||||
|
|
||||||
def strip_short_host_name(self, storageGroupName):
|
|
||||||
tempList = storageGroupName.split("-")
|
|
||||||
if len(tempList) == 6:
|
|
||||||
shorthostName = tempList.pop(1)
|
|
||||||
updatedStorageGroup = "-".join(tempList)
|
|
||||||
return updatedStorageGroup, shorthostName
|
|
||||||
else:
|
|
||||||
shorthostName = None
|
|
||||||
return storageGroupName, shorthostName
|
|
||||||
|
|
||||||
def _get_fast_settings_from_storage_group(self, storageGroupInstance):
|
def _get_fast_settings_from_storage_group(self, storageGroupInstance):
|
||||||
"""Get the emc FAST setting from the storage group.
|
"""Get the emc FAST setting from the storage group.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user