From e0541c586701ecee17ace478187995a3817a641c Mon Sep 17 00:00:00 2001 From: Helen Walsh Date: Fri, 13 Nov 2015 00:42:49 +0000 Subject: [PATCH] EMC VMAX - VMAX driver failing to remove zones The population of the target_wwns and init_targ_map should occur before the terminate_connection and not after. Closes-Bug: #1501938 Change-Id: I3a896a1fcdfd9c09a4c8fa5a1f7fb7253ff36715 --- cinder/tests/unit/test_emc_vmax.py | 114 ++++++++++++++++++ cinder/volume/drivers/emc/emc_vmax_common.py | 21 ++++ cinder/volume/drivers/emc/emc_vmax_fc.py | 28 ++++- cinder/volume/drivers/emc/emc_vmax_iscsi.py | 1 + cinder/volume/drivers/emc/emc_vmax_masking.py | 13 ++ 5 files changed, 173 insertions(+), 4 deletions(-) diff --git a/cinder/tests/unit/test_emc_vmax.py b/cinder/tests/unit/test_emc_vmax.py index 29088b3c1..1c688e65f 100644 --- a/cinder/tests/unit/test_emc_vmax.py +++ b/cinder/tests/unit/test_emc_vmax.py @@ -7238,3 +7238,117 @@ class EMCVMAXMaskingTest(test.TestCase): maskingviewdict, controllerConfigService, maskingviewdict['volumeName'])) self.assertIsNone(result) + + +class EMCVMAXFCTest(test.TestCase): + def setUp(self): + self.data = EMCVMAXCommonData() + + super(EMCVMAXFCTest, self).setUp() + + configuration = mock.Mock() + configuration.safe_get.return_value = 'FCTests' + configuration.config_group = 'FCTests' + emc_vmax_common.EMCVMAXCommon._gather_info = mock.Mock() + driver = emc_vmax_fc.EMCVMAXFCDriver(configuration=configuration) + driver.db = FakeDB() + self.driver = driver + + def test_terminate_connection(self): + common = self.driver.common + common.conn = FakeEcomConnection() + common._unmap_lun = mock.Mock() + common.get_masking_view_by_volume = mock.Mock( + return_value='testMV') + common.get_masking_views_by_port_group = mock.Mock( + return_value=[]) + common.get_target_wwns = mock.Mock( + return_value=EMCVMAXCommonData.target_wwns) + 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'])) + + def test_get_common_masking_views_two_exist(self): + common = self.driver.common + common.conn = FakeEcomConnection() + maskingviews = [{'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'MV1'}, + {'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'MV2'}] + + portGroupInstanceName = ( + self.driver.common.masking._get_port_group_from_masking_view( + common.conn, self.data.lunmaskctrl_name, + self.data.storage_system)) + + initiatorGroupInstanceName = ( + self.driver.common.masking._get_initiator_group_from_masking_view( + common.conn, self.data.lunmaskctrl_name, + self.data.storage_system)) + common.get_masking_views_by_port_group = mock.Mock( + return_value=maskingviews) + common.get_masking_views_by_initiator_group = mock.Mock( + return_value=maskingviews) + + mvInstances = self.driver._get_common_masking_views( + portGroupInstanceName, initiatorGroupInstanceName) + self.assertTrue(len(mvInstances) == 2) + + def test_get_common_masking_views_one_overlap(self): + common = self.driver.common + common.conn = FakeEcomConnection() + maskingviewsPG = [{'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'MV1'}, + {'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'MV2'}] + + maskingviewsIG = [{'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'MV1'}] + + portGroupInstanceName = ( + self.driver.common.masking._get_port_group_from_masking_view( + common.conn, self.data.lunmaskctrl_name, + self.data.storage_system)) + + initiatorGroupInstanceName = ( + self.driver.common.masking._get_initiator_group_from_masking_view( + common.conn, self.data.lunmaskctrl_name, + self.data.storage_system)) + common.get_masking_views_by_port_group = mock.Mock( + return_value=maskingviewsPG) + common.get_masking_views_by_initiator_group = mock.Mock( + return_value=maskingviewsIG) + + mvInstances = self.driver._get_common_masking_views( + portGroupInstanceName, initiatorGroupInstanceName) + self.assertTrue(len(mvInstances) == 1) + + def test_get_common_masking_views_no_overlap(self): + common = self.driver.common + common.conn = FakeEcomConnection() + maskingviewsPG = [{'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'MV2'}] + + maskingviewsIG = [{'CreationClassName': 'Symm_LunMaskingView', + 'ElementName': 'MV1'}] + + portGroupInstanceName = ( + self.driver.common.masking._get_port_group_from_masking_view( + common.conn, self.data.lunmaskctrl_name, + self.data.storage_system)) + + initiatorGroupInstanceName = ( + self.driver.common.masking._get_initiator_group_from_masking_view( + common.conn, self.data.lunmaskctrl_name, + self.data.storage_system)) + common.get_masking_views_by_port_group = mock.Mock( + return_value=maskingviewsPG) + common.get_masking_views_by_initiator_group = mock.Mock( + return_value=maskingviewsIG) + + mvInstances = self.driver._get_common_masking_views( + portGroupInstanceName, initiatorGroupInstanceName) + self.assertTrue(len(mvInstances) == 0) diff --git a/cinder/volume/drivers/emc/emc_vmax_common.py b/cinder/volume/drivers/emc/emc_vmax_common.py index e6ed9795a..d29406a78 100644 --- a/cinder/volume/drivers/emc/emc_vmax_common.py +++ b/cinder/volume/drivers/emc/emc_vmax_common.py @@ -3691,6 +3691,15 @@ class EMCVMAXCommon(object): return self.masking.get_port_group_from_masking_view( self.conn, maskingViewInstanceName) + def get_initiator_group_from_masking_view(self, maskingViewInstanceName): + """Get the initiator group in a masking view. + + :param maskingViewInstanceName: masking view instance name + :returns: initiatorGroupInstanceName + """ + return self.masking.get_initiator_group_from_masking_view( + self.conn, maskingViewInstanceName) + def get_masking_view_by_volume(self, volume, connector): """Given volume, retrieve the masking view instance name. @@ -3715,6 +3724,18 @@ class EMCVMAXCommon(object): return self.masking.get_masking_views_by_port_group( self.conn, portGroupInstanceName) + def get_masking_views_by_initiator_group( + self, initiatorGroupInstanceName): + """Given initiator group, retrieve the masking view instance name. + + :param initiatorGroupInstanceName: initiator group instance name + :returns: list -- maskingViewInstanceNames + """ + LOG.debug("Finding Masking Views for initiator group %(ig)s.", + {'ig': initiatorGroupInstanceName}) + return self.masking.get_masking_views_by_initiator_group( + self.conn, initiatorGroupInstanceName) + def _create_replica_v3( self, repServiceInstanceName, cloneVolume, sourceVolume, sourceInstance, isSnapshot, extraSpecs): diff --git a/cinder/volume/drivers/emc/emc_vmax_fc.py b/cinder/volume/drivers/emc/emc_vmax_fc.py index 2c95dc625..40843c0fb 100644 --- a/cinder/volume/drivers/emc/emc_vmax_fc.py +++ b/cinder/volume/drivers/emc/emc_vmax_fc.py @@ -52,6 +52,7 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver): - Extend Volume for VMAX3, SE8.1.0.3 https://blueprints.launchpad.net/cinder/+spec/vmax3-extend-volume - Incorrect SG selected on an attach (#1515176) + - Cleanup Zoning (bug #1501938) NOTE: FC only """ VERSION = "2.3.0" @@ -217,24 +218,28 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver): portGroupInstanceName = ( self.common.get_port_group_from_masking_view( mvInstanceName)) + initiatorGroupInstanceName = ( + self.common.get_initiator_group_from_masking_view( + mvInstanceName)) LOG.debug("Found port group: %(portGroup)s " "in masking view %(maskingView)s.", {'portGroup': portGroupInstanceName, 'maskingView': mvInstanceName}) + # Map must be populated before the terminate_connection + target_wwns, init_targ_map = self._build_initiator_target_map( + storage_system, volume, connector) self.common.terminate_connection(volume, connector) LOG.debug("Looking for masking views still associated with " "Port Group %s.", portGroupInstanceName) - mvInstances = self.common.get_masking_views_by_port_group( - 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. - target_wwns, init_targ_map = self._build_initiator_target_map( - storage_system, volume, connector) LOG.debug("No MaskingViews were found. Deleting zone.") data = {'driver_volume_type': 'fibre_channel', 'data': {'target_wwn': target_wwns, @@ -246,6 +251,21 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver): {'volume': volume['name']}) return data + def _get_common_masking_views( + self, portGroupInstanceName, initiatorGroupInstanceName): + """Check to see the existence of mv in list""" + mvInstances = [] + mvInstancesByPG = self.common.get_masking_views_by_port_group( + portGroupInstanceName) + + mvInstancesByIG = self.common.get_masking_views_by_initiator_group( + initiatorGroupInstanceName) + + for mvInstanceByPG in mvInstancesByPG: + if mvInstanceByPG in mvInstancesByIG: + mvInstances.append(mvInstanceByPG) + return mvInstances + def _build_initiator_target_map(self, storage_system, volume, connector): """Build the target_wwns and the initiator target map.""" target_wwns = [] diff --git a/cinder/volume/drivers/emc/emc_vmax_iscsi.py b/cinder/volume/drivers/emc/emc_vmax_iscsi.py index 489ae583b..3240754a5 100644 --- a/cinder/volume/drivers/emc/emc_vmax_iscsi.py +++ b/cinder/volume/drivers/emc/emc_vmax_iscsi.py @@ -58,6 +58,7 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver): - Extend Volume for VMAX3, SE8.1.0.3 https://blueprints.launchpad.net/cinder/+spec/vmax3-extend-volume - Incorrect SG selected on an attach (#1515176) + - Cleanup Zoning (bug #1501938) NOTE: FC only """ VERSION = "2.3.0" diff --git a/cinder/volume/drivers/emc/emc_vmax_masking.py b/cinder/volume/drivers/emc/emc_vmax_masking.py index b4b595bbc..d2bed73f8 100644 --- a/cinder/volume/drivers/emc/emc_vmax_masking.py +++ b/cinder/volume/drivers/emc/emc_vmax_masking.py @@ -2158,6 +2158,19 @@ class EMCVMAXMasking(object): portGroupInstanceName, ResultClass='Symm_LunMaskingView') return mvInstanceNames + def get_masking_views_by_initiator_group( + self, conn, initiatorGroupInstanceName): + """Given initiator group, retrieve the masking view instance name. + + :param conn: the ecom connection + :param initiatorGroupInstanceName: the instance name of the + initiator group + :returns: masking view instance names + """ + mvInstanceNames = conn.AssociatorNames( + initiatorGroupInstanceName, ResultClass='Symm_LunMaskingView') + return mvInstanceNames + def get_port_group_from_masking_view(self, conn, maskingViewInstanceName): """Get the port group in a masking view. -- 2.45.2