]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
EMC VMAX - get iscsi ip from port in existing MV
authorHelen Walsh <helen.walsh@emc.com>
Mon, 16 Nov 2015 20:31:53 +0000 (20:31 +0000)
committerHelen Walsh <helen.walsh@emc.com>
Sat, 27 Feb 2016 12:57:13 +0000 (12:57 +0000)
This patch fixes bug in getting the iscsi ip address from
an existing masking view.  In this scenario we must query
the port(s) in the portgroup belonging to the masking view
and not the random port group from the xml file.

Change-Id: I4f8bea5c9f8c07a793aa1053678a7a915d565d22
Closes-Bug: #1504460

cinder/tests/unit/test_emc_vmax.py
cinder/volume/drivers/emc/emc_vmax_common.py
cinder/volume/drivers/emc/emc_vmax_fc.py
cinder/volume/drivers/emc/emc_vmax_iscsi.py
cinder/volume/drivers/emc/emc_vmax_masking.py

index 678aa0552308e87bece7f13d429d8e5a8c692b6d..bd87d3474d484a9f4e100221e1a7e813b07a2193 100644 (file)
@@ -243,8 +243,9 @@ class EMCVMAXCommonData(object):
     storage_system_v3 = 'SYMMETRIX-+-000197200056'
     port_group = 'OS-portgroup-PG'
     lunmaskctrl_id = (
-        'SYMMETRIX+000195900551+OS-fakehost-gold-MV')
-    lunmaskctrl_name = 'OS-fakehost-gold-MV'
+        'SYMMETRIX+000195900551+OS-fakehost-gold-I-MV')
+    lunmaskctrl_name = (
+        'OS-fakehost-gold-I-MV')
 
     initiatorgroup_id = (
         'SYMMETRIX+000195900551+OS-fakehost-IG')
@@ -850,6 +851,7 @@ class FakeEcomConnection(object):
         antecedent = SYMM_LunMasking()
         antecedent['CreationClassName'] = self.data.lunmask_creationclass2
         antecedent['SystemName'] = self.data.storage_system
+        antecedent['ElementName'] = mvname
 
         classcimproperty = Fake_CIMProperty()
         elementName = (
@@ -1196,6 +1198,9 @@ class FakeEcomConnection(object):
     def _getinstance_ipprotocolendpoint(self, objectpath):
         return self._enum_ipprotocolendpoint()[0]
 
+    def _getinstance_lunmaskingview(self, objectpath):
+        return self._enum_maskingView()[0]
+
     def _default_getinstance(self, objectpath):
         return objectpath
 
@@ -1601,9 +1606,15 @@ class FakeEcomConnection(object):
 
     def _enum_maskingView(self):
         maskingViews = []
-        maskingView = {}
+        maskingView = SYMM_LunMasking()
         maskingView['CreationClassName'] = 'Symm_LunMaskingView'
-        maskingView['ElementName'] = 'myMaskingView'
+        maskingView['ElementName'] = self.data.lunmaskctrl_name
+
+        cimproperty = Fake_CIMProperty()
+        cimproperty.value = self.data.lunmaskctrl_name
+        properties = {u'ElementName': cimproperty}
+        maskingView.properties = properties
+
         maskingViews.append(maskingView)
         return maskingViews
 
@@ -2053,6 +2064,16 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         issynched = self.driver.common.utils._is_sync_complete(conn, syncname)
         self.assertFalse(issynched)
 
+    def test_get_correct_port_group(self):
+        self.driver.common.conn = self.fake_ecom_connection()
+        maskingViewInstanceName = {'CreationClassName': 'Symm_LunMaskingView',
+                                   'ElementName': 'OS-fakehost-gold-I-MV',
+                                   'SystemName': 'SYMMETRIX+000195900551'}
+        deviceinfodict = {'controller': maskingViewInstanceName}
+        portgroupname = self.driver.common._get_correct_port_group(
+            deviceinfodict, self.data.storage_system)
+        self.assertEqual('OS-portgroup-PG', portgroupname)
+
     def test_generate_unique_trunc_pool(self):
         pool_under_16_chars = 'pool_under_16'
         pool1 = self.driver.utils.generate_unique_trunc_pool(
@@ -2089,11 +2110,11 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
         self.assertEqual('10.10.10.10', foundIpAddresses[0])
 
     def test_find_device_number(self):
-        host = 'myhost'
+        host = 'fakehost'
         data = (
             self.driver.common.find_device_number(self.data.test_volume_v2,
                                                   host))
-        self.assertEqual('OS-myhost-MV', data['maskingview'])
+        self.assertEqual('OS-fakehost-MV', data['maskingview'])
         host = 'bogushost'
         data = (
             self.driver.common.find_device_number(self.data.test_volume_v2,
@@ -2762,7 +2783,7 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
                 self.data.storage_system, self.data.connector))
         # The controller has been found.
         self.assertEqual(
-            'OS-fakehost-gold-MV',
+            'OS-fakehost-gold-I-MV',
             self.driver.common.conn.GetInstance(
                 foundControllerInstanceName)['ElementName'])
 
@@ -3087,6 +3108,8 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
     def test_already_mapped_no_fast_success(
             self, _mock_volume_type, mock_wrap_group, mock_wrap_device,
             mock_is_same_host):
+        self.driver.common._get_correct_port_group = mock.Mock(
+            return_value=self.data.port_group)
         self.driver.initialize_connection(self.data.test_volume,
                                           self.data.connector)
 
@@ -3109,6 +3132,8 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
     def test_map_new_masking_view_no_fast_success(
             self, _mock_volume_type, mock_wrap_group,
             mock_storage_group, mock_add_volume):
+        self.driver.common._wrap_find_device_number = mock.Mock(
+            return_value={})
         self.driver.initialize_connection(self.data.test_volume,
                                           self.data.connector)
 
@@ -4055,6 +4080,8 @@ class EMCVMAXISCSIDriverFastTestCase(test.TestCase):
     def test_already_mapped_fast_success(
             self, _mock_volume_type, mock_wrap_group, mock_wrap_device,
             mock_is_same_host):
+        self.driver.common._get_correct_port_group = mock.Mock(
+            return_value=self.data.port_group)
         self.driver.initialize_connection(self.data.test_volume,
                                           self.data.connector)
 
@@ -4646,6 +4673,8 @@ class EMCVMAXFCDriverNoFastTestCase(test.TestCase):
         common = self.driver.common
         common.get_target_wwns_from_masking_view = mock.Mock(
             return_value=EMCVMAXCommonData.target_wwns)
+        common._get_correct_port_group = mock.Mock(
+            return_value=self.data.port_group)
         lookup_service = self.driver.zonemanager_lookup_service
         lookup_service.get_device_mapping_from_network = mock.Mock(
             return_value=EMCVMAXCommonData.device_map)
@@ -5238,6 +5267,8 @@ class EMCVMAXFCDriverFastTestCase(test.TestCase):
         common = self.driver.common
         common.get_target_wwns = mock.Mock(
             return_value=EMCVMAXCommonData.target_wwns)
+        self.driver.common._get_correct_port_group = mock.Mock(
+            return_value=self.data.port_group)
         data = self.driver.initialize_connection(
             self.data.test_volume, self.data.connector)
         # Test the no lookup service, pre-zoned case.
@@ -6207,6 +6238,8 @@ class EMCV3DriverTestCase(test.TestCase):
             return_value=EMCVMAXCommonData.target_wwns)
         self.driver.common._initial_setup = mock.Mock(
             return_value=self.default_extraspec())
+        self.driver.common._get_correct_port_group = mock.Mock(
+            return_value=self.data.port_group)
         data = self.driver.initialize_connection(
             self.data.test_volume_v3, self.data.connector)
         # Test the no lookup service, pre-zoned case.
index 0ba3cac19a48b08362fb4cd291757a772d1dac29..1e4fede9d78a475a519191730346349ed8587cb1 100644 (file)
@@ -323,8 +323,7 @@ class EMCVMAXCommon(object):
                  {'volume': volumename})
 
         device_info = self.find_device_number(volume, connector['host'])
-        device_number = device_info['hostlunid']
-        if device_number is None:
+        if 'hostlunid' not in device_info:
             LOG.info(_LI("Volume %s is not mapped. No volume to unmap."),
                      volumename)
             return
@@ -370,13 +369,15 @@ class EMCVMAXCommon(object):
         :returns: dict -- deviceInfoDict - device information dict
         :raises: VolumeBackendAPIException
         """
+        portGroupName = None
         extraSpecs = self._initial_setup(volume)
 
         volumeName = volume['name']
         LOG.info(_LI("Initialize connection: %(volume)s."),
                  {'volume': volumeName})
         self.conn = self._get_ecom_connection()
-        deviceInfoDict = self.find_device_number(volume, connector['host'])
+        deviceInfoDict = self._wrap_find_device_number(
+            volume, connector['host'])
         maskingViewDict = self._populate_masking_dict(
             volume, connector, extraSpecs)
 
@@ -392,17 +393,23 @@ class EMCVMAXCommon(object):
                              "The device number is  %(deviceNumber)s."),
                          {'volume': volumeName,
                           'deviceNumber': deviceNumber})
+                # Special case, we still need to get the iscsi ip address.
+                portGroupName = (
+                    self._get_correct_port_group(
+                        deviceInfoDict, maskingViewDict['storageSystemName']))
+
             else:
-                deviceInfoDict = self._attach_volume(
+                deviceInfoDict, portGroupName = self._attach_volume(
                     volume, connector, extraSpecs, maskingViewDict, True)
         else:
-            deviceInfoDict = self._attach_volume(
-                volume, connector, extraSpecs, maskingViewDict)
+            deviceInfoDict, portGroupName = (
+                self._attach_volume(
+                    volume, connector, extraSpecs, maskingViewDict))
 
         if self.protocol.lower() == 'iscsi':
             return self._find_ip_protocol_endpoints(
                 self.conn, deviceInfoDict['storagesystem'],
-                maskingViewDict['pgGroupName'])
+                portGroupName)
         else:
             return deviceInfoDict
 
@@ -419,6 +426,7 @@ class EMCVMAXCommon(object):
         :param maskingViewDict: masking view information
         :param isLiveMigration: boolean, can be None
         :returns: dict -- deviceInfoDict
+                  String -- port group name
         :raises: VolumeBackendAPIException
         """
         volumeName = volume['name']
@@ -449,7 +457,7 @@ class EMCVMAXCommon(object):
             raise exception.VolumeBackendAPIException(
                 data=exception_message)
 
-        return deviceInfoDict
+        return deviceInfoDict, rollbackDict['pgGroupName']
 
     def _is_same_host(self, connector, deviceInfoDict):
         """Check if the host is the same.
@@ -470,6 +478,48 @@ class EMCVMAXCommon(object):
                     return True
         return False
 
+    def _get_correct_port_group(self, deviceInfoDict, storageSystemName):
+        """Get the portgroup name from the existing masking view.
+
+        :params deviceInfoDict: the device info dictionary
+        :params storageSystemName: storage system name
+        :returns: String port group name
+        """
+        if ('controller' in deviceInfoDict and
+                deviceInfoDict['controller'] is not None):
+            maskingViewInstanceName = deviceInfoDict['controller']
+            try:
+                maskingViewInstance = (
+                    self.conn.GetInstance(maskingViewInstanceName))
+            except Exception:
+                exception_message = (_("Unable to get the name of "
+                                       "the masking view."))
+                raise exception.VolumeBackendAPIException(
+                    data=exception_message)
+
+            # Get the portgroup from masking view
+            portGroupInstanceName = (
+                self.masking._get_port_group_from_masking_view(
+                    self.conn,
+                    maskingViewInstance['ElementName'],
+                    storageSystemName))
+            try:
+                portGroupInstance = (
+                    self.conn.GetInstance(portGroupInstanceName))
+                portGroupName = (
+                    portGroupInstance['ElementName'])
+            except Exception:
+                exception_message = (_("Unable to get the name of "
+                                       "the portgroup."))
+                raise exception.VolumeBackendAPIException(
+                    data=exception_message)
+        else:
+            exception_message = (_("Cannot get the portgroup from "
+                                   "the masking view."))
+            raise exception.VolumeBackendAPIException(
+                data=exception_message)
+        return portGroupName
+
     def terminate_connection(self, volume, connector):
         """Disallow connection from connector.
 
@@ -1426,6 +1476,9 @@ class EMCVMAXCommon(object):
                    'initiator': foundinitiatornames})
         return foundinitiatornames
 
+    def _wrap_find_device_number(self, volume, host):
+        return self.find_device_number(volume, host)
+
     def find_device_number(self, volume, host):
         """Given the volume dict find a device number.
 
@@ -1438,6 +1491,7 @@ class EMCVMAXCommon(object):
         """
         maskedvols = []
         data = {}
+        foundController = None
         foundNumDeviceNumber = None
         foundMaskingViewName = None
         volumeName = volume['name']
@@ -1455,9 +1509,9 @@ class EMCVMAXCommon(object):
             if index > -1:
                 unitinstance = self.conn.GetInstance(unitname,
                                                      LocalOnly=False)
-                numDeviceNumber = int(unitinstance['DeviceNumber'],
-                                      16)
+                numDeviceNumber = int(unitinstance['DeviceNumber'], 16)
                 foundNumDeviceNumber = numDeviceNumber
+                foundController = controller
                 controllerInstance = self.conn.GetInstance(controller,
                                                            LocalOnly=False)
                 propertiesList = controllerInstance.properties.items()
@@ -1468,7 +1522,8 @@ class EMCVMAXCommon(object):
 
                 devicedict = {'hostlunid': foundNumDeviceNumber,
                               'storagesystem': storageSystemName,
-                              'maskingview': foundMaskingViewName}
+                              'maskingview': foundMaskingViewName,
+                              'controller': foundController}
                 maskedvols.append(devicedict)
 
         if not maskedvols:
@@ -4350,12 +4405,14 @@ class EMCVMAXCommon(object):
 
     def _find_ip_protocol_endpoints(self, conn, storageSystemName,
                                     portgroupname):
-        """Find the IP protocol endpoint for ISCSI
+        """Find the IP protocol endpoint for ISCSI.
 
         :param storageSystemName: the system name
         :param portgroupname: the portgroup name
         :returns: foundIpAddresses
         """
+        LOG.debug("The portgroup name for iscsiadm is %(pg)s",
+                  {'pg': portgroupname})
         foundipaddresses = []
         configservice = (
             self.utils.find_controller_configuration_service(
index d64af926e49b7f49b17165fafba1dfc0accac5eb..38405e5b7e674a51e6881370e8b3d914bc47752c 100644 (file)
@@ -58,6 +58,7 @@ class EMCVMAXFCDriver(driver.FibreChannelDriver):
                 for VMAX3 (bug #1520549)
               - necessary updates for CG changes (#1534616)
               - Changing PercentSynced to CopyState (bug #1517103)
+              - Getting iscsi ip from port in existing masking view
     """
 
     VERSION = "2.3.0"
index 78fd1d1e145bec4a285bd22f89120ae7d785715e..d1cde1c4fde585978c1963c6cf6da5d81f20926c 100644 (file)
@@ -64,6 +64,7 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
                 for VMAX3 (bug #1520549)
               - necessary updates for CG changes (#1534616)
               - Changing PercentSynced to CopyState (bug #1517103)
+              - Getting iscsi ip from port in existing masking view
     """
 
     VERSION = "2.3.0"
index 66127d90c4aa2b6a03c9d646931c0f587962abfc..d770e58be7cfd60cad90283a2fdd74c22def33fc 100644 (file)
@@ -89,6 +89,7 @@ class EMCVMAXMasking(object):
         defaultStorageGroupInstanceName = None
         fastPolicyName = None
         assocStorageGroupName = None
+        storageGroupInstanceName = None
         if isLiveMigration is False:
             if isV3:
                 defaultStorageGroupInstanceName = (
@@ -127,6 +128,11 @@ class EMCVMAXMasking(object):
                 {'maskingViewName': maskingViewDict['maskingViewName']})
             errorMessage = e
 
+        rollbackDict['pgGroupName'], errorMessage = (
+            self._get_port_group_name_from_mv(
+                conn, maskingViewDict['maskingViewName'],
+                maskingViewDict['storageSystemName']))
+
         if not errorMessage:
             # Only after the masking view has been validated, add the
             # volume to the storage group and recheck that it has been
@@ -2618,3 +2624,34 @@ class EMCVMAXMasking(object):
             foundHardwareIDsInstanceNames.append(hardwareIdInstanceName)
 
         return foundHardwareIDsInstanceNames
+
+    def _get_port_group_name_from_mv(self, conn, maskingViewName,
+                                     storageSystemName):
+        """Get the port group name from the masking view.
+
+        :param conn: the connection to the ecom server
+        :param maskingViewName: the masking view name
+        :param storageSystemName: the storage system name
+        :returns: String - port group name
+                  String - error message
+        """
+        errorMessage = None
+        portGroupName = None
+        portGroupInstanceName = (
+            self._get_port_group_from_masking_view(
+                conn, maskingViewName, storageSystemName))
+        if portGroupInstanceName is None:
+            LOG.error(_LE(
+                "Cannot get port group from masking view: "
+                "%(maskingViewName)s. "),
+                {'maskingViewName': maskingViewName})
+        else:
+            try:
+                portGroupInstance = (
+                    conn.GetInstance(portGroupInstanceName))
+                portGroupName = (
+                    portGroupInstance['ElementName'])
+            except Exception:
+                LOG.error(_LE(
+                    "Cannot get port group name."))
+        return portGroupName, errorMessage