Merge "EMC VMAX - not cleaning up HW Resource WWPN initiators"

This commit is contained in:
Jenkins 2016-02-26 18:21:07 +00:00 committed by Gerrit Code Review
commit eccd7d7c5b
2 changed files with 260 additions and 20 deletions

View File

@ -702,7 +702,7 @@ class FakeEcomConnection(object):
def Associators(self, objectpath, ResultClass='EMC_StorageHardwareID'):
result = None
if ResultClass == 'EMC_StorageHardwareID':
if '_StorageHardwareID' in ResultClass:
result = self._assoc_hdwid()
elif ResultClass == 'EMC_iSCSIProtocolEndpoint':
result = self._assoc_endpoint()
@ -1554,6 +1554,7 @@ class FakeEcomConnection(object):
hdwid = SE_StorageHardwareID()
hdwid['CreationClassName'] = self.data.hardwareid_creationclass
hdwid['StorageID'] = self.data.connector['wwpns'][0]
hdwid['InstanceID'] = "W-+-" + self.data.connector['wwpns'][0]
hdwid.path = hdwid
storhdwids.append(hdwid)
@ -2478,6 +2479,95 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
conn, controllerConfigService, storageGroupInstanceName,
storageGroupName, volumeInstanceName, volumeName, extraSpecs)
# Bug 1504192 - if the last volume is being unmapped and the masking view
# goes away, cleanup the initiators and associated initiator group.
def test_delete_initiators_from_initiator_group(self):
conn = self.fake_ecom_connection()
controllerConfigService = (
self.driver.utils.find_controller_configuration_service(
conn, self.data.storage_system))
initiatorGroupName = self.data.initiatorgroup_name
initiatorGroupInstanceName = (
self.driver.common.masking._get_initiator_group_from_masking_view(
conn, self.data.lunmaskctrl_name, self.data.storage_system))
conn.InvokeMethod = mock.Mock(return_value=1)
# Deletion of initiators failed.
self.driver.common.masking._delete_initiators_from_initiator_group(
conn, controllerConfigService, initiatorGroupInstanceName,
initiatorGroupName)
conn.InvokeMethod = mock.Mock(return_value=0)
# Deletion of initiators successful.
self.driver.common.masking._delete_initiators_from_initiator_group(
conn, controllerConfigService, initiatorGroupInstanceName,
initiatorGroupName)
# Bug 1504192 - if the last volume is being unmapped and the masking view
# goes away, cleanup the initiators and associated initiator group.
def test_last_volume_delete_initiator_group_exception(self):
extraSpecs = {'volume_backend_name': 'ISCSINoFAST'}
conn = self.fake_ecom_connection()
controllerConfigService = (
self.driver.utils.find_controller_configuration_service(
conn, self.data.storage_system))
initiatorGroupInstanceName = (
self.driver.common.masking._get_initiator_group_from_masking_view(
conn, self.data.lunmaskctrl_name, self.data.storage_system))
job = {
'Job': {'InstanceID': '9999', 'status': 'success', 'type': None}}
conn.InvokeMethod = mock.Mock(return_value=(4096, job))
self.driver.common.masking.get_masking_views_by_initiator_group = (
mock.Mock(return_value=[]))
self.driver.common.masking._delete_initiators_from_initiator_group = (
mock.Mock(return_value=True))
self.driver.common.masking.utils.wait_for_job_complete = (
mock.Mock(return_value=(2, 'failure')))
# Exception occurrs while deleting the initiator group.
self.assertRaises(
exception.VolumeBackendAPIException,
self.driver.common.masking._last_volume_delete_initiator_group,
conn, controllerConfigService, initiatorGroupInstanceName,
extraSpecs)
# Bug 1504192 - if the last volume is being unmapped and the masking view
# goes away, cleanup the initiators and associated initiator group.
def test_last_volume_delete_initiator_group(self):
extraSpecs = {'volume_backend_name': 'ISCSINoFAST'}
conn = self.fake_ecom_connection()
controllerConfigService = (
self.driver.utils.find_controller_configuration_service(
conn, self.data.storage_system))
initiatorGroupName = self.data.initiatorgroup_name
initiatorGroupInstanceName = (
self.driver.common.masking._get_initiator_group_from_masking_view(
conn, self.data.lunmaskctrl_name, self.data.storage_system))
self.assertEqual(initiatorGroupName,
conn.GetInstance(
initiatorGroupInstanceName)['ElementName'])
# masking view is associated with the initiator group and initiator
# group will not be deleted.
self.driver.common.masking._last_volume_delete_initiator_group(
conn, controllerConfigService, initiatorGroupInstanceName,
extraSpecs)
self.driver.common.masking.get_masking_views_by_initiator_group = (
mock.Mock(return_value=[]))
self.driver.common.masking._delete_initiators_from_initiator_group = (
mock.Mock(return_value=True))
# No Masking view and initiators associated with the Initiator group
# and initiator group will be deleted.
self.driver.common.masking._last_volume_delete_initiator_group(
conn, controllerConfigService, initiatorGroupInstanceName,
extraSpecs)
job = {
'Job': {'InstanceID': '9999', 'status': 'success', 'type': None}}
conn.InvokeMethod = mock.Mock(return_value=(4096, job))
self.driver.common.masking.utils.wait_for_job_complete = (
mock.Mock(return_value=(0, 'success')))
# Deletion of initiator group is successful after waiting for job
# to complete.
self.driver.common.masking._last_volume_delete_initiator_group(
conn, controllerConfigService, initiatorGroupInstanceName,
extraSpecs)
# Tests removal of last volume in a storage group V2
def test_remove_and_reset_members(self):
extraSpecs = {'volume_backend_name': 'GOLD_BE',
@ -5678,7 +5768,7 @@ class EMCV3DriverTestCase(test.TestCase):
conn, controllerConfigService, storageGroupName))
vol = self.default_vol()
self.driver.common.masking._delete_mv_and_sg = mock.Mock()
self.driver.common.masking._delete_mv_ig_and_sg = mock.Mock()
self.assertTrue(self.driver.common.masking._last_vol_in_SG(
conn, controllerConfigService, storageGroupInstanceName,
storageGroupName, vol, vol['name'], extraSpecs))

View File

@ -1817,9 +1817,11 @@ class EMCVMAXMasking(object):
"""Steps if the volume is the last in a storage group.
1. Check if the volume is in a masking view.
2. If it is in a masking view, delete the masking view,
remove the volume from the storage group, and delete
the storage group.
2. If it is in a masking view, delete the masking view, remove the
initiators from the initiator group and delete the initiator
group if there are no other masking views associated with the
initiator group, remove the volume from the storage group, and
delete the storage group.
3. If it is not in a masking view, remove the volume from the
storage group and delete the storage group.
@ -1853,13 +1855,13 @@ class EMCVMAXMasking(object):
@lockutils.synchronized(maskingViewName,
"emc-mv-", True)
def do_delete_mv_and_sg():
return self._delete_mv_and_sg(
def do_delete_mv_ig_and_sg():
return self._delete_mv_ig_and_sg(
conn, controllerConfigService, mvInstanceName,
maskingViewName, storageGroupInstanceName,
storageGroupName, volumeInstance, volumeName,
extraSpecs)
do_delete_mv_and_sg()
do_delete_mv_ig_and_sg()
status = True
else:
# Remove the volume from the storage group and delete the SG.
@ -1925,11 +1927,11 @@ class EMCVMAXMasking(object):
"End: number of volumes in masking storage group: %(numVol)d.",
{'numVol': len(volumeInstanceNames)})
def _delete_mv_and_sg(self, conn, controllerConfigService, mvInstanceName,
maskingViewName, storageGroupInstanceName,
storageGroupName, volumeInstance, volumeName,
extraSpecs):
"""Delete the Masking view and the Storage Group.
def _delete_mv_ig_and_sg(
self, conn, controllerConfigService, mvInstanceName,
maskingViewName, storageGroupInstanceName, storageGroupName,
volumeInstance, volumeName, extraSpecs):
"""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
@ -1950,10 +1952,15 @@ class EMCVMAXMasking(object):
storageSystemInstanceName = self.utils.find_storage_system(
conn, controllerConfigService)
initiatorGroupInstanceName = (
self.get_initiator_group_from_masking_view(conn, mvInstanceName))
self._last_volume_delete_masking_view(
conn, controllerConfigService, mvInstanceName,
maskingViewName, extraSpecs)
self._last_volume_delete_initiator_group(
conn, controllerConfigService,
initiatorGroupInstanceName, extraSpecs)
if not isV3:
isTieringPolicySupported, tierPolicyServiceInstanceName = (
self._get_tiering_info(conn, storageSystemInstanceName,
@ -2261,10 +2268,13 @@ class EMCVMAXMasking(object):
self, conn, initiatorGroupInstanceName):
"""Given initiator group, retrieve the masking view instance name.
Retrieve the list of masking view instances associated with the
initiator group instance name.
:param conn: the ecom connection
:param initiatorGroupInstanceName: the instance name of the
initiator group
:returns: masking view instance names
:returns: list of masking view instance names
"""
mvInstanceNames = conn.AssociatorNames(
initiatorGroupInstanceName, ResultClass='Symm_LunMaskingView')
@ -2299,13 +2309,13 @@ class EMCVMAXMasking(object):
initiatorGroupInstanceNames = conn.AssociatorNames(
maskingViewInstanceName, ResultClass='SE_InitiatorMaskingGroup')
if len(initiatorGroupInstanceNames) > 0:
LOG.debug("Found initiator group %(pg)s in masking view %(mv)s.",
{'pg': initiatorGroupInstanceNames[0],
LOG.debug("Found initiator group %(ig)s in masking view %(mv)s.",
{'ig': initiatorGroupInstanceNames[0],
'mv': maskingViewInstanceName})
return initiatorGroupInstanceNames[0]
else:
LOG.warning(_LW("No port group found in masking view %(mv)s."),
{'mv': maskingViewInstanceName})
LOG.warning(_LW("No Initiator group found in masking view "
"%(mv)s."), {'mv': maskingViewInstanceName})
def _get_sg_or_mv_associated_with_initiator(
self, conn, controllerConfigService, volumeInstanceName,
@ -2436,7 +2446,7 @@ class EMCVMAXMasking(object):
if rc != 0:
exceptionMessage = (_(
"Error Deleting Group: %(storageGroupName)s. "
"Return code: %(rc)lu. Error: %(error)s")
"Return code: %(rc)lu. Error: %(error)s")
% {'storageGroupName': storageGroupName,
'rc': rc,
'error': errordesc})
@ -2444,6 +2454,146 @@ class EMCVMAXMasking(object):
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
def _delete_initiator_group(self, conn, controllerConfigService,
initiatorGroupInstanceName, initiatorGroupName,
extraSpecs):
"""Delete an initiatorGroup.
:param conn - connection to the ecom server
:param controllerConfigService - controller config service
:param initiatorGroupInstanceName - the initiator group instance name
:param initiatorGroupName - initiator group name
:param extraSpecs: extra specifications
"""
rc, job = conn.InvokeMethod(
'DeleteGroup',
controllerConfigService,
MaskingGroup=initiatorGroupInstanceName,
Force=True)
if rc != 0:
rc, errordesc = self.utils.wait_for_job_complete(conn, job,
extraSpecs)
if rc != 0:
exceptionMessage = (_(
"Error Deleting Initiator Group: %(initiatorGroupName)s. "
"Return code: %(rc)lu. Error: %(error)s")
% {'initiatorGroupName': initiatorGroupName,
'rc': rc,
'error': errordesc})
LOG.error(exceptionMessage)
raise exception.VolumeBackendAPIException(
data=exceptionMessage)
else:
LOG.debug("Initiator group %(initiatorGroupName)s "
"is successfully deleted.",
{'initiatorGroupName': initiatorGroupName})
else:
LOG.debug("Initiator group %(initiatorGroupName)s "
"is successfully deleted.",
{'initiatorGroupName': initiatorGroupName})
def _delete_storage_hardware_id(self,
conn,
hardwareIdManagementService,
hardwareIdPath):
"""Delete given initiator path
Delete the initiator. Do not rise exception or failure if deletion
fails due to any reasons.
:param conn - connection to the ecom server
:param hardwareIdManagementService - hardware id management service
:param hardwareIdPath - The path of the initiator object
"""
ret = conn.InvokeMethod('DeleteStorageHardwareID',
hardwareIdManagementService,
HardwareID = hardwareIdPath)
if ret == 0:
LOG.debug("Deletion of initiator path %(hardwareIdPath)s "
"is successful.", {'hardwareIdPath': hardwareIdPath})
else:
LOG.warning(_LW("Deletion of initiator path %(hardwareIdPath)s "
"is failed."), {'hardwareIdPath': hardwareIdPath})
def _delete_initiators_from_initiator_group(self, conn,
controllerConfigService,
initiatorGroupInstanceName,
initiatorGroupName):
"""Delete initiators
Delete all initiators associated with the initiator group instance.
Cleanup whatever is possible. It will not return any failure or
rise exception if deletion fails due to any reasons.
:param conn - connection to the ecom server
:param controllerConfigService - controller config service
:param initiatorGroupInstanceName - the initiator group instance name
"""
storageHardwareIdInstanceNames = (
conn.AssociatorNames(initiatorGroupInstanceName,
ResultClass='SE_StorageHardwareID'))
if len(storageHardwareIdInstanceNames) == 0:
LOG.debug("No initiators found in Initiator group "
"%(initiatorGroupName)s.",
{'initiatorGroupName': initiatorGroupName})
return
storageSystemName = controllerConfigService['SystemName']
hardwareIdManagementService = (
self.utils.find_storage_hardwareid_service(conn,
storageSystemName))
for storageHardwareIdInstanceName in storageHardwareIdInstanceNames:
initiatorName = storageHardwareIdInstanceName['InstanceID']
hardwareIdPath = storageHardwareIdInstanceName
LOG.debug("Initiator %(initiatorName)s "
"will be deleted from the Initiator group "
"%(initiatorGroupName)s. HardwareIdPath is "
"%(hardwareIdPath)s.",
{'initiatorName': initiatorName,
'initiatorGroupName': initiatorGroupName,
'hardwareIdPath': hardwareIdPath})
self._delete_storage_hardware_id(conn,
hardwareIdManagementService,
hardwareIdPath)
def _last_volume_delete_initiator_group(
self, conn, controllerConfigService,
initiatorGroupInstanceName, extraSpecs):
"""Delete the initiator group
Delete the Initiator group if there are no masking views associated
with the initiator group.
:param conn: the ecom connection
:param controllerConfigService: controller config service
:param igInstanceNames: initiator group instance name
:param extraSpecs: extra specifications
"""
maskingViewInstanceNames = self.get_masking_views_by_initiator_group(
conn, initiatorGroupInstanceName)
initiatorGroupInstance = conn.GetInstance(initiatorGroupInstanceName)
initiatorGroupName = initiatorGroupInstance['ElementName']
if len(maskingViewInstanceNames) == 0:
LOG.debug(
"Last volume is associated with the initiator group, deleting "
"the associated initiator group %(initiatorGroupName)s.",
{'initiatorGroupName': initiatorGroupName})
self._delete_initiators_from_initiator_group(
conn, controllerConfigService, initiatorGroupInstanceName,
initiatorGroupName)
self._delete_initiator_group(conn, controllerConfigService,
initiatorGroupInstanceName,
initiatorGroupName, extraSpecs)
else:
LOG.warning(_LW("Initiator group %(initiatorGroupName)s is "
"associated with masking views and can't be "
"deleted. Number of associated masking view is: "
"%(nmv)d."),
{'initiatorGroupName': initiatorGroupName,
'nmv': len(maskingViewInstanceNames)})
def _create_hardware_ids(
self, conn, initiatorNames, storageSystemName):
"""Create hardwareIds for initiator(s).