Merge "VMAX driver - failed rollback on VMAX3 when MV issue"
This commit is contained in:
commit
5e128a5740
@ -2334,14 +2334,17 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
|
|||||||
rollbackDict['defaultStorageGroupInstanceName'] = (
|
rollbackDict['defaultStorageGroupInstanceName'] = (
|
||||||
self.data.default_storage_group)
|
self.data.default_storage_group)
|
||||||
rollbackDict['sgName'] = self.data.storagegroupname
|
rollbackDict['sgName'] = self.data.storagegroupname
|
||||||
|
rollbackDict['sgGroupName'] = self.data.storagegroupname
|
||||||
rollbackDict['volumeName'] = 'vol1'
|
rollbackDict['volumeName'] = 'vol1'
|
||||||
rollbackDict['fastPolicyName'] = 'GOLD1'
|
rollbackDict['fastPolicyName'] = 'GOLD1'
|
||||||
rollbackDict['volumeInstance'] = vol
|
rollbackDict['volumeInstance'] = vol
|
||||||
rollbackDict['controllerConfigService'] = controllerConfigService
|
rollbackDict['controllerConfigService'] = controllerConfigService
|
||||||
rollbackDict['extraSpecs'] = extraSpecs
|
rollbackDict['extraSpecs'] = extraSpecs
|
||||||
|
rollbackDict['igGroupName'] = self.data.initiatorgroup_name
|
||||||
|
rollbackDict['connector'] = self.data.connector
|
||||||
# Path 1 - The volume is in another storage group that isn't the
|
# Path 1 - The volume is in another storage group that isn't the
|
||||||
# default storage group
|
# default storage group
|
||||||
expectedmessage = (_("V2 rollback - Volume in another storage "
|
expectedmessage = (_("Rollback - Volume in another storage "
|
||||||
"group besides default storage group."))
|
"group besides default storage group."))
|
||||||
message = (
|
message = (
|
||||||
self.driver.common.masking.
|
self.driver.common.masking.
|
||||||
@ -2350,6 +2353,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
|
|||||||
self.assertEqual(expectedmessage, message)
|
self.assertEqual(expectedmessage, message)
|
||||||
# Path 2 - The volume is not in any storage group
|
# Path 2 - The volume is not in any storage group
|
||||||
rollbackDict['sgName'] = 'sq_not_exist'
|
rollbackDict['sgName'] = 'sq_not_exist'
|
||||||
|
rollbackDict['sgGroupName'] = 'sq_not_exist'
|
||||||
expectedmessage = (_("V2 rollback, volume is not in any storage "
|
expectedmessage = (_("V2 rollback, volume is not in any storage "
|
||||||
"group."))
|
"group."))
|
||||||
message = (
|
message = (
|
||||||
@ -7841,6 +7845,113 @@ class EMCVMAXMaskingTest(test.TestCase):
|
|||||||
conn, controllerConfigService, storageGroupInstanceName,
|
conn, controllerConfigService, storageGroupInstanceName,
|
||||||
volumeInstance, extraSpecs)
|
volumeInstance, extraSpecs)
|
||||||
|
|
||||||
|
# Bug 1552426 - failed rollback on V3 when MV issue
|
||||||
|
def test_check_ig_rollback(self):
|
||||||
|
# called on masking view rollback
|
||||||
|
masking = self.driver.common.masking
|
||||||
|
conn = self.fake_ecom_connection()
|
||||||
|
controllerConfigService = (
|
||||||
|
self.driver.utils.find_controller_configuration_service(
|
||||||
|
conn, self.data.storage_system))
|
||||||
|
connector = self.data.connector
|
||||||
|
extraSpecs = {'volume_backend_name': 'V3_BE',
|
||||||
|
'isV3': True,
|
||||||
|
'slo': 'Bronze',
|
||||||
|
'pool': 'SRP_1',
|
||||||
|
}
|
||||||
|
igGroupName = self.data.initiatorgroup_name
|
||||||
|
host = igGroupName.split("-")[1]
|
||||||
|
igInstance = masking._find_initiator_masking_group(
|
||||||
|
conn, controllerConfigService, self.data.initiatorNames)
|
||||||
|
# path 1: The masking view creation process created a now stale
|
||||||
|
# initiator group before it failed.
|
||||||
|
with mock.patch.object(masking,
|
||||||
|
'_last_volume_delete_initiator_group'):
|
||||||
|
masking._check_ig_rollback(conn, controllerConfigService,
|
||||||
|
igGroupName, connector, extraSpecs)
|
||||||
|
(masking._last_volume_delete_initiator_group.
|
||||||
|
assert_called_once_with(conn, controllerConfigService,
|
||||||
|
igInstance, extraSpecs, host))
|
||||||
|
# path 2: No initiator group was created before the masking
|
||||||
|
# view process failed.
|
||||||
|
with mock.patch.object(masking,
|
||||||
|
'_find_initiator_masking_group',
|
||||||
|
return_value=None):
|
||||||
|
masking._last_volume_delete_initiator_group.reset_mock()
|
||||||
|
masking._check_ig_rollback(conn, controllerConfigService,
|
||||||
|
igGroupName, connector, extraSpecs)
|
||||||
|
(masking._last_volume_delete_initiator_group.
|
||||||
|
assert_not_called())
|
||||||
|
|
||||||
|
@mock.patch.object(
|
||||||
|
emc_vmax_masking.EMCVMAXMasking,
|
||||||
|
'get_associated_masking_groups_from_device',
|
||||||
|
return_value=EMCVMAXCommonData.storagegroups)
|
||||||
|
@mock.patch.object(
|
||||||
|
emc_vmax_masking.EMCVMAXMasking,
|
||||||
|
'return_volume_to_default_storage_group_v3',
|
||||||
|
return_value='Returning volume to default sg')
|
||||||
|
def test_check_if_rollback_action_required_v3(
|
||||||
|
self, mock_return, mock_group):
|
||||||
|
conn = self.fake_ecom_connection()
|
||||||
|
masking = self.driver.common.masking
|
||||||
|
controllerConfigService = (
|
||||||
|
self.driver.utils.find_controller_configuration_service(
|
||||||
|
conn, self.data.storage_system))
|
||||||
|
extraSpecs_v3 = {'volume_backend_name': 'V3_BE',
|
||||||
|
'isV3': True,
|
||||||
|
'slo': 'Bronze',
|
||||||
|
'pool': 'SRP_1',
|
||||||
|
'connector': self.data.connector}
|
||||||
|
|
||||||
|
vol = EMC_StorageVolume()
|
||||||
|
vol['name'] = self.data.test_volume['name']
|
||||||
|
vol['CreationClassName'] = 'Symm_StorageVolume'
|
||||||
|
vol['ElementName'] = self.data.test_volume['id']
|
||||||
|
vol['DeviceID'] = self.data.test_volume['device_id']
|
||||||
|
vol['Id'] = self.data.test_volume['id']
|
||||||
|
vol['SystemName'] = self.data.storage_system
|
||||||
|
vol['NumberOfBlocks'] = self.data.test_volume['NumberOfBlocks']
|
||||||
|
vol['BlockSize'] = self.data.test_volume['BlockSize']
|
||||||
|
|
||||||
|
# Added vol to vol.path
|
||||||
|
vol['SystemCreationClassName'] = 'Symm_StorageSystem'
|
||||||
|
vol.path = vol
|
||||||
|
vol.path.classname = vol['CreationClassName']
|
||||||
|
rollbackDict = {}
|
||||||
|
rollbackDict['isV3'] = True
|
||||||
|
rollbackDict['defaultStorageGroupInstanceName'] = (
|
||||||
|
self.data.default_storage_group)
|
||||||
|
rollbackDict['sgGroupName'] = self.data.storagegroupname
|
||||||
|
rollbackDict['sgName'] = self.data.storagegroupname
|
||||||
|
rollbackDict['volumeName'] = 'vol1'
|
||||||
|
rollbackDict['slo'] = 'Bronze'
|
||||||
|
rollbackDict['volumeInstance'] = vol
|
||||||
|
rollbackDict['controllerConfigService'] = controllerConfigService
|
||||||
|
rollbackDict['extraSpecs'] = extraSpecs_v3
|
||||||
|
rollbackDict['igGroupName'] = self.data.initiatorgroup_name
|
||||||
|
rollbackDict['connector'] = self.data.connector
|
||||||
|
# v3 Path 1 - The volume is in another storage group that isn't the
|
||||||
|
# default storage group
|
||||||
|
expectedmessage = (_("Rollback - Volume in another storage "
|
||||||
|
"group besides default storage group."))
|
||||||
|
message = (
|
||||||
|
masking.
|
||||||
|
_check_if_rollback_action_for_masking_required(conn,
|
||||||
|
rollbackDict))
|
||||||
|
self.assertEqual(expectedmessage, message)
|
||||||
|
# v3 Path 2 - The volume is not in any storage group
|
||||||
|
rollbackDict['sgGroupName'] = 'sq_not_exist'
|
||||||
|
(rollbackDict
|
||||||
|
['defaultStorageGroupInstanceName']) = (self.data.
|
||||||
|
default_sg_instance_name)
|
||||||
|
expectedmessage = (_("V3 rollback"))
|
||||||
|
message = (
|
||||||
|
masking.
|
||||||
|
_check_if_rollback_action_for_masking_required(conn,
|
||||||
|
rollbackDict))
|
||||||
|
self.assertEqual(expectedmessage, message)
|
||||||
|
|
||||||
|
|
||||||
class EMCVMAXFCTest(test.TestCase):
|
class EMCVMAXFCTest(test.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
@ -88,7 +88,6 @@ class EMCVMAXMasking(object):
|
|||||||
maskingViewDict['extraSpecs'] = extraSpecs
|
maskingViewDict['extraSpecs'] = extraSpecs
|
||||||
defaultStorageGroupInstanceName = None
|
defaultStorageGroupInstanceName = None
|
||||||
fastPolicyName = None
|
fastPolicyName = None
|
||||||
assocStorageGroupName = None
|
|
||||||
storageGroupInstanceName = None
|
storageGroupInstanceName = None
|
||||||
if isLiveMigration is False:
|
if isLiveMigration is False:
|
||||||
if isV3:
|
if isV3:
|
||||||
@ -153,7 +152,9 @@ class EMCVMAXMasking(object):
|
|||||||
rollbackDict['fastPolicyName'] = fastPolicyName
|
rollbackDict['fastPolicyName'] = fastPolicyName
|
||||||
rollbackDict['isV3'] = isV3
|
rollbackDict['isV3'] = isV3
|
||||||
rollbackDict['extraSpecs'] = extraSpecs
|
rollbackDict['extraSpecs'] = extraSpecs
|
||||||
rollbackDict['sgName'] = maskingViewDict['sgGroupName']
|
rollbackDict['sgGroupName'] = maskingViewDict['sgGroupName']
|
||||||
|
rollbackDict['igGroupName'] = maskingViewDict['igGroupName']
|
||||||
|
rollbackDict['connector'] = maskingViewDict['connector']
|
||||||
|
|
||||||
if errorMessage:
|
if errorMessage:
|
||||||
# Rollback code if we cannot complete any of the steps above
|
# Rollback code if we cannot complete any of the steps above
|
||||||
@ -165,11 +166,19 @@ class EMCVMAXMasking(object):
|
|||||||
self._check_if_rollback_action_for_masking_required(
|
self._check_if_rollback_action_for_masking_required(
|
||||||
conn, rollbackDict)
|
conn, rollbackDict)
|
||||||
if isV3:
|
if isV3:
|
||||||
rollbackDict['sgGroupName'] = assocStorageGroupName
|
if maskingViewDict['slo'] is not None:
|
||||||
rollbackDict['storageSystemName'] = (
|
rollbackDict['storageSystemName'] = (
|
||||||
maskingViewDict['storageSystemName'])
|
maskingViewDict['storageSystemName'])
|
||||||
self._check_if_rollback_action_for_masking_required(
|
rollbackDict['slo'] = maskingViewDict['slo']
|
||||||
conn, rollbackDict)
|
self._check_if_rollback_action_for_masking_required(
|
||||||
|
conn, rollbackDict)
|
||||||
|
|
||||||
|
else:
|
||||||
|
errorMessage = self._check_adding_volume_to_storage_group(
|
||||||
|
conn, rollbackDict,
|
||||||
|
rollbackDict['defaultStorageGroupInstanceName'])
|
||||||
|
if errorMessage:
|
||||||
|
LOG.error(errorMessage)
|
||||||
|
|
||||||
exceptionMessage = (_(
|
exceptionMessage = (_(
|
||||||
"Failed to get, create or add volume %(volumeName)s "
|
"Failed to get, create or add volume %(volumeName)s "
|
||||||
@ -1271,7 +1280,8 @@ class EMCVMAXMasking(object):
|
|||||||
We need to be able to return the volume to the default storage group
|
We need to be able to return the volume to the default storage group
|
||||||
if anything has gone wrong. The volume can also potentially belong to
|
if anything has gone wrong. The volume can also potentially belong to
|
||||||
a storage group that is not the default depending on where
|
a storage group that is not the default depending on where
|
||||||
the exception occurred.
|
the exception occurred. We also may need to clean up any unused
|
||||||
|
initiator groups.
|
||||||
|
|
||||||
:param conn: the connection to the ecom server
|
:param conn: the connection to the ecom server
|
||||||
:param rollbackDict: the rollback dictionary
|
:param rollbackDict: the rollback dictionary
|
||||||
@ -1279,23 +1289,29 @@ class EMCVMAXMasking(object):
|
|||||||
:raises: VolumeBackendAPIException
|
:raises: VolumeBackendAPIException
|
||||||
"""
|
"""
|
||||||
message = None
|
message = None
|
||||||
|
# Check if ig has been created. If so, check for other
|
||||||
|
# masking views associated with the ig. If none, remove
|
||||||
|
# initiators and delete ig.
|
||||||
|
self._check_ig_rollback(
|
||||||
|
conn, rollbackDict['controllerConfigService'],
|
||||||
|
rollbackDict['igGroupName'], rollbackDict['connector'],
|
||||||
|
rollbackDict['extraSpecs'])
|
||||||
try:
|
try:
|
||||||
if rollbackDict['isV3']:
|
foundStorageGroupInstanceName = (
|
||||||
errorMessage = self._check_adding_volume_to_storage_group(
|
self.utils.get_storage_group_from_volume(
|
||||||
conn, rollbackDict,
|
conn, rollbackDict['volumeInstance'].path,
|
||||||
rollbackDict['defaultStorageGroupInstanceName'])
|
rollbackDict['sgGroupName']))
|
||||||
if errorMessage:
|
# Volume is not associated with any storage group so add
|
||||||
LOG.error(errorMessage)
|
# it back to the default.
|
||||||
message = (_("V3 rollback"))
|
if not foundStorageGroupInstanceName:
|
||||||
|
if rollbackDict['isV3']:
|
||||||
else:
|
errorMessage = self._check_adding_volume_to_storage_group(
|
||||||
foundStorageGroupInstanceName = (
|
conn, rollbackDict,
|
||||||
self.utils.get_storage_group_from_volume(
|
rollbackDict['defaultStorageGroupInstanceName'])
|
||||||
conn, rollbackDict['volumeInstance'].path,
|
if errorMessage:
|
||||||
rollbackDict['sgName']))
|
LOG.error(errorMessage)
|
||||||
# Volume is not associated with any storage group so add
|
message = (_("V3 rollback"))
|
||||||
# it back to the default.
|
else:
|
||||||
if not foundStorageGroupInstanceName:
|
|
||||||
LOG.warning(_LW(
|
LOG.warning(_LW(
|
||||||
"No storage group found. "
|
"No storage group found. "
|
||||||
"Performing rollback on Volume: %(volumeName)s "
|
"Performing rollback on Volume: %(volumeName)s "
|
||||||
@ -1323,35 +1339,35 @@ class EMCVMAXMasking(object):
|
|||||||
'fastPolicyName': rollbackDict['fastPolicyName']})
|
'fastPolicyName': rollbackDict['fastPolicyName']})
|
||||||
message = (_("V2 rollback, volume is not in any storage "
|
message = (_("V2 rollback, volume is not in any storage "
|
||||||
"group."))
|
"group."))
|
||||||
else:
|
else:
|
||||||
LOG.info(_LI(
|
LOG.info(_LI(
|
||||||
"The storage group found is "
|
"The storage group found is "
|
||||||
"%(foundStorageGroupInstanceName)s."),
|
"%(foundStorageGroupInstanceName)s."),
|
||||||
{'foundStorageGroupInstanceName':
|
{'foundStorageGroupInstanceName':
|
||||||
foundStorageGroupInstanceName})
|
foundStorageGroupInstanceName})
|
||||||
|
|
||||||
# Check the name, see is it the default storage group
|
# Check the name, see if it is the default storage group
|
||||||
# or another.
|
# or another.
|
||||||
if (foundStorageGroupInstanceName !=
|
if (foundStorageGroupInstanceName !=
|
||||||
rollbackDict['defaultStorageGroupInstanceName']):
|
rollbackDict['defaultStorageGroupInstanceName']):
|
||||||
# Remove it from its current masking view and return it
|
# Remove it from its current masking view and return it
|
||||||
# to its default masking view if fast is enabled.
|
# to its default masking view if fast is enabled or slo
|
||||||
self.remove_and_reset_members(
|
# is defined.
|
||||||
conn,
|
self.remove_and_reset_members(
|
||||||
rollbackDict['controllerConfigService'],
|
conn,
|
||||||
rollbackDict['volumeInstance'],
|
rollbackDict['controllerConfigService'],
|
||||||
rollbackDict['volumeName'],
|
rollbackDict['volumeInstance'],
|
||||||
rollbackDict['extraSpecs'])
|
rollbackDict['volumeName'],
|
||||||
message = (_("V2 rollback - Volume in another storage "
|
rollbackDict['extraSpecs'])
|
||||||
|
message = (_("Rollback - Volume in another storage "
|
||||||
"group besides default storage group."))
|
"group besides default storage group."))
|
||||||
except Exception:
|
except Exception:
|
||||||
errorMessage = (_(
|
errorMessage = (_(
|
||||||
"Rollback for Volume: %(volumeName)s has failed. "
|
"Rollback for Volume: %(volumeName)s has failed. "
|
||||||
"Please contact your system administrator to manually return "
|
"Please contact your system administrator to manually return "
|
||||||
"your volume to the default storage group for fast policy "
|
"your volume to the default storage group for fast policy/ "
|
||||||
"%(fastPolicyName)s failed.")
|
"slo.")
|
||||||
% {'volumeName': rollbackDict['volumeName'],
|
% {'volumeName': rollbackDict['volumeName']})
|
||||||
'fastPolicyName': rollbackDict['fastPolicyName']})
|
|
||||||
LOG.exception(errorMessage)
|
LOG.exception(errorMessage)
|
||||||
raise exception.VolumeBackendAPIException(data=errorMessage)
|
raise exception.VolumeBackendAPIException(data=errorMessage)
|
||||||
return message
|
return message
|
||||||
@ -1551,6 +1567,38 @@ class EMCVMAXMasking(object):
|
|||||||
|
|
||||||
return foundInitiatorGroupInstanceName
|
return foundInitiatorGroupInstanceName
|
||||||
|
|
||||||
|
def _check_ig_rollback(
|
||||||
|
self, conn, controllerConfigService,
|
||||||
|
igGroupName, connector, extraSpecs):
|
||||||
|
"""Check if rollback action is required on an initiator group.
|
||||||
|
|
||||||
|
If anything goes wrong on a masking view creation, we need to check if
|
||||||
|
the process created a now-stale initiator group before failing, i.e.
|
||||||
|
an initiator group a) matching the name used in the mv process and
|
||||||
|
b) not associated with any other masking views.
|
||||||
|
If a stale ig exists, remove the initiators and delete the ig.
|
||||||
|
|
||||||
|
:param conn: the ecom connection
|
||||||
|
:param controllerConfigService: controller config service
|
||||||
|
:param igGroupName: the initiator group name
|
||||||
|
:param connector: the connector object
|
||||||
|
:param extraSpecs: extra specifications
|
||||||
|
"""
|
||||||
|
initiatorNames = self._find_initiator_names(conn, connector)
|
||||||
|
foundInitiatorGroupInstanceName = self._find_initiator_masking_group(
|
||||||
|
conn, controllerConfigService, initiatorNames)
|
||||||
|
if foundInitiatorGroupInstanceName:
|
||||||
|
initiatorGroupInstance = conn.GetInstance(
|
||||||
|
foundInitiatorGroupInstanceName, LocalOnly=False)
|
||||||
|
if initiatorGroupInstance['ElementName'] == igGroupName:
|
||||||
|
host = igGroupName.split("-")[1]
|
||||||
|
LOG.debug("Searching for masking views associated with "
|
||||||
|
"%(igGroupName)s",
|
||||||
|
{'igGroupName': igGroupName})
|
||||||
|
self._last_volume_delete_initiator_group(
|
||||||
|
conn, controllerConfigService,
|
||||||
|
foundInitiatorGroupInstanceName, extraSpecs, host)
|
||||||
|
|
||||||
def _get_port_group_from_masking_view(
|
def _get_port_group_from_masking_view(
|
||||||
self, conn, maskingViewName, storageSystemName):
|
self, conn, maskingViewName, storageSystemName):
|
||||||
"""Given the masking view name get the port group from it.
|
"""Given the masking view name get the port group from it.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user