VMAX driver - Cleanup of Initiator group fails

When the last volume has been deleted from the Storage
Group and the Masking View has been deleted and the
initiator group is no longer in use, the deletion of
the Initiator group fails. The initiator group is actually
deleted off the array, but the code is throwing up an
error when it uses the now deleted initiator group
name to search for remaining masking views. The
solution is to check if the initiator group has been
deleted by common.terminate_connection before searching
for remaining masking views.

Change-Id: I79559789459f73c4adebf010d84cbeedd25f7f74
Closes-Bug: #1605193
This commit is contained in:
Helen Walsh 2016-07-27 14:38:26 +01:00
parent 781475f114
commit 8e62557f6e
4 changed files with 136 additions and 16 deletions

View File

@ -4841,9 +4841,9 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
self.data.connector)
@mock.patch.object(
emc_vmax_masking.EMCVMAXMasking,
'get_initiator_group_from_masking_view',
return_value='myInitGroup')
emc_vmax_common.EMCVMAXCommon,
'check_ig_instance_name',
return_value=None)
@mock.patch.object(
emc_vmax_masking.EMCVMAXMasking,
'_find_initiator_masking_group',
@ -4857,7 +4857,8 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
'get_volume_type_extra_specs',
return_value={'volume_backend_name': 'FCNoFAST'})
def test_detach_no_fast_last_volume_success(
self, mock_volume_type, mock_mv, mock_ig, mock_igc):
self, mock_volume_type, mock_mv, mock_ig, mock_check_ig):
# last volume so initiatorGroup will be deleted by terminate connection
self.driver.terminate_connection(self.data.test_source_volume,
self.data.connector)
@ -5431,6 +5432,10 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
self.data.test_volume,
self.data.connector)
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'check_ig_instance_name',
return_value='myInitGroup')
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'get_masking_views_by_port_group',
@ -5453,7 +5458,7 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
return_value={'volume_backend_name': 'FCFAST',
'FASTPOLICY': 'FC_GOLD1'})
def test_detach_fast_success(self, mock_volume_type, mock_maskingview,
mock_ig, mock_igc, mock_mv):
mock_ig, mock_igc, mock_mv, mock_check_ig):
common = self.driver.common
common.get_target_wwns = mock.Mock(
return_value=EMCVMAXCommonData.target_wwns)
@ -6470,6 +6475,10 @@ class EMCV3DriverTestCase(test.TestCase):
self.data.test_volume,
self.data.connector)
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'check_ig_instance_name',
return_value='myInitGroup')
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'get_masking_views_by_port_group',
@ -6491,7 +6500,7 @@ class EMCV3DriverTestCase(test.TestCase):
'get_volume_type_extra_specs',
return_value={'volume_backend_name': 'V3_BE'})
def test_detach_v3_success(self, mock_volume_type, mock_maskingview,
mock_ig, mock_igc, mock_mv):
mock_ig, mock_igc, mock_mv, mock_check_ig):
common = self.driver.common
common.get_target_wwns = mock.Mock(
return_value=EMCVMAXCommonData.target_wwns)
@ -7847,7 +7856,7 @@ class EMCVMAXFCTest(test.TestCase):
driver.db = FakeDB()
self.driver = driver
def test_terminate_connection(self):
def test_terminate_connection_ig_present(self):
common = self.driver.common
common.conn = FakeEcomConnection()
common._unmap_lun = mock.Mock()
@ -7857,6 +7866,44 @@ class EMCVMAXFCTest(test.TestCase):
return_value=[])
common.get_target_wwns = mock.Mock(
return_value=EMCVMAXCommonData.target_wwns)
initiatorGroupInstanceName = (
self.driver.common.masking._get_initiator_group_from_masking_view(
common.conn, self.data.lunmaskctrl_name,
self.data.storage_system))
with mock.patch.object(self.driver.common,
'check_ig_instance_name',
return_value=initiatorGroupInstanceName):
data = self.driver.terminate_connection(self.data.test_volume_v3,
self.data.connector)
common.get_target_wwns.assert_called_once_with(
EMCVMAXCommonData.storage_system, EMCVMAXCommonData.connector)
numTargetWwns = len(EMCVMAXCommonData.target_wwns)
self.assertEqual(numTargetWwns, len(data['data']))
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'check_ig_instance_name',
return_value=None)
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'get_target_wwns',
return_value=EMCVMAXCommonData.target_wwns)
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'get_masking_views_by_port_group',
return_value=[])
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'get_masking_view_by_volume',
return_value='testMV')
@mock.patch.object(
emc_vmax_common.EMCVMAXCommon,
'_unmap_lun')
def test_terminate_connection_no_ig(self, mock_unmap,
mock_mv_vol, mock_mv_pg,
mock_wwns, mock_check_ig):
common = self.driver.common
common.conn = FakeEcomConnection()
data = self.driver.terminate_connection(self.data.test_volume_v3,
self.data.connector)
common.get_target_wwns.assert_called_once_with(
@ -8217,6 +8264,30 @@ class EMCVMAXUtilsTest(test.TestCase):
foundIqn = self.driver.utils.get_iqn(conn, ipprotocolendpoints[1])
self.assertEqual(iqn, foundIqn)
# bug #1605193 - Cleanup of Initiator Group fails
def test_check_ig_instance_name_present(self):
conn = FakeEcomConnection()
initiatorgroup = SE_InitiatorMaskingGroup()
initiatorgroup['CreationClassName'] = (
self.data.initiatorgroup_creationclass)
initiatorgroup['DeviceID'] = self.data.initiatorgroup_id
initiatorgroup['SystemName'] = self.data.storage_system
initiatorgroup['ElementName'] = self.data.initiatorgroup_name
foundIg = self.driver.utils.check_ig_instance_name(
conn, initiatorgroup)
self.assertEqual(initiatorgroup, foundIg)
# bug #1605193 - Cleanup of Initiator Group fails
def test_check_ig_instance_name_not_present(self):
conn = FakeEcomConnection()
initiatorgroup = None
with mock.patch.object(self.driver.utils,
'get_existing_instance',
return_value=None):
foundIg = self.driver.utils.check_ig_instance_name(
conn, initiatorgroup)
self.assertIsNone(foundIg)
class EMCVMAXCommonTest(test.TestCase):
def setUp(self):

View File

@ -549,6 +549,15 @@ class EMCVMAXCommon(object):
data=exception_message)
return portGroupName
def check_ig_instance_name(self, initiatorGroupInstanceName):
"""Check if an initiator group instance is on the array.
:param initiatorGroupInstanceName: initiator group instance name
:returns: initiator group name, or None if deleted
"""
return self.utils.check_ig_instance_name(
self.conn, initiatorGroupInstanceName)
def terminate_connection(self, volume, connector):
"""Disallow connection from connector.

View File

@ -245,18 +245,38 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
LOG.debug("Looking for masking views still associated with "
"Port Group %s.", portGroupInstanceName)
mvInstances = self._get_common_masking_views(
portGroupInstanceName, initiatorGroupInstanceName)
if len(mvInstances) > 0:
LOG.debug("Found %(numViews)lu MaskingViews.",
{'numViews': len(mvInstances)})
else: # No views found.
LOG.debug("No MaskingViews were found. Deleting zone.")
# check if the initiator group has been deleted
checkIgInstanceName = (
self.common.check_ig_instance_name(initiatorGroupInstanceName))
# if it has not been deleted, check for remaining masking views
if checkIgInstanceName is not None:
mvInstances = self._get_common_masking_views(
portGroupInstanceName, initiatorGroupInstanceName)
if len(mvInstances) > 0:
LOG.debug("Found %(numViews)lu MaskingViews.",
{'numViews': len(mvInstances)})
data = {'driver_volume_type': 'fibre_channel',
'data': {}}
else: # no masking views found
LOG.debug("No MaskingViews were found. Deleting zone.")
data = {'driver_volume_type': 'fibre_channel',
'data': {'target_wwn': target_wwns,
'initiator_target_map': init_targ_map}}
LOG.debug("Return FC data for zone removal: %(data)s.",
{'data': data})
else: # The initiator group has been deleted
LOG.debug("Initiator Group has been deleted. Deleting zone.")
data = {'driver_volume_type': 'fibre_channel',
'data': {'target_wwn': target_wwns,
'initiator_target_map': init_targ_map}}
LOG.debug("Return FC data for zone removal: %(data)s.",
{'data': data})
LOG.debug("Return FC data for zone removal: %(data)s.",
{'data': data})
else:
LOG.warning(_LW("Volume %(volume)s is not in any masking view."),
{'volume': volume['name']})

View File

@ -2812,3 +2812,23 @@ class EMCVMAXUtils(object):
cimProperties = properties[1]
foundIqn = cimProperties.value
return foundIqn
def check_ig_instance_name(
self, conn, initiatorGroupInstanceName):
"""Check if a given Initiator Group Instance Name has been deleted.
:param conn: the ecom connection
:param initiatorGroupInstanceName: the given IG instance name
:return: foundinitiatorGroupInstanceName or None if deleted
"""
foundinitiatorGroupInstanceName = self.get_existing_instance(
conn, initiatorGroupInstanceName)
if foundinitiatorGroupInstanceName is not None:
LOG.debug("Found initiator group name: "
"%(igName)s.",
{'igName': foundinitiatorGroupInstanceName})
else:
LOG.debug("Could not find initiator group name: "
"%(igName)s.",
{'igName': foundinitiatorGroupInstanceName})
return foundinitiatorGroupInstanceName