]> review.fuel-infra Code Review - openstack-build/cinder-build.git/commitdiff
Fix VMAX live migration problem
authorXing Yang <xing.yang@emc.com>
Wed, 23 Sep 2015 15:22:43 +0000 (11:22 -0400)
committerXing Yang <xing.yang@emc.com>
Fri, 2 Oct 2015 16:55:32 +0000 (12:55 -0400)
Live migration fails for VMAX because the host is not taken into
account when determining whether a volume is masked or not. For
live migration it is necessary to know which host a volume is masked
to. This patch fixes the problem by checking which host the volume
is masked to.

Change-Id: I693e29f7b26145bd1fd357b7d98377e26bfdfed8
Closes-Bug: #1498964
(cherry picked from commit 794e27c78255c94084b54821d44a36fd8f2096d7)

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

index 41be1e0a42927e8fd1bfc9ed27ad3a29eb3d5da1..ceee88ec5681a09df0f972597218428fd3a66a1a 100644 (file)
@@ -134,9 +134,9 @@ class Fake_CIMProperty(object):
         cimproperty.value = '10000000000'
         return cimproperty
 
-    def fake_getElementNameCIMProperty(self):
+    def fake_getElementNameCIMProperty(self, name):
         cimproperty = Fake_CIMProperty()
-        cimproperty.value = 'OS-myhost-MV'
+        cimproperty.value = name
         return cimproperty
 
     def fake_getSupportedReplicationTypes(self):
@@ -815,7 +815,7 @@ class FakeEcomConnection(object):
 
         classcimproperty = Fake_CIMProperty()
         elementName = (
-            classcimproperty.fake_getElementNameCIMProperty())
+            classcimproperty.fake_getElementNameCIMProperty('OS-myhost-MV'))
         properties = {u'ElementName': elementName}
         antecedent.properties = properties
 
@@ -824,6 +824,19 @@ class FakeEcomConnection(object):
         unitname['CreationClassName'] = self.data.unit_creationclass
         unitnames.append(unitname)
 
+        # Second masking
+        unitname2 = unitname.copy()
+        elementName2 = (
+            classcimproperty.fake_getElementNameCIMProperty('OS-fakehost-MV'))
+        properties2 = {u'ElementName': elementName2}
+
+        antecedent2 = SYMM_LunMasking()
+        antecedent2['CreationClassName'] = self.data.lunmask_creationclass2
+        antecedent2['SystemName'] = self.data.storage_system
+
+        antecedent2.properties = properties2
+        unitname2['Antecedent'] = antecedent2
+        unitnames.append(unitname2)
         return unitnames
 
     def _default_ref(self, objectpath):
@@ -1790,6 +1803,19 @@ class EMCVMAXISCSIDriverNoFastTestCase(test.TestCase):
     def fake_is_v3(self, conn, serialNumber):
         return False
 
+    def test_find_device_number(self):
+        host = 'myhost'
+        data = (
+            self.driver.common.find_device_number(self.data.test_volume_v2,
+                                                  host))
+        self.assertEqual('OS-myhost-MV', data['maskingview'])
+        host = 'bogushost'
+        data = (
+            self.driver.common.find_device_number(self.data.test_volume_v2,
+                                                  host))
+        # Empty dict
+        self.assertFalse(data)
+
     def test_unbind_and_get_volume_from_storage_pool(self):
         conn = self.fake_ecom_connection()
         common = self.driver.common
index 35ff88afcf67634cc4568921ddc0dd4c91ffd816..070e58bdca202a321c20e2ed7594d1efc2d4a1a3 100644 (file)
@@ -320,7 +320,7 @@ class EMCVMAXCommon(object):
         LOG.info(_LI("Unmap volume: %(volume)s."),
                  {'volume': volumename})
 
-        device_info = self.find_device_number(volume)
+        device_info = self.find_device_number(volume, connector['host'])
         device_number = device_info['hostlunid']
         if device_number is None:
             LOG.info(_LI("Volume %s is not mapped. No volume to unmap."),
@@ -374,7 +374,7 @@ class EMCVMAXCommon(object):
         LOG.info(_LI("Initialize connection: %(volume)s."),
                  {'volume': volumeName})
         self.conn = self._get_ecom_connection()
-        deviceInfoDict = self.find_device_number(volume)
+        deviceInfoDict = self.find_device_number(volume, connector['host'])
 
         if ('hostlunid' in deviceInfoDict and
                 deviceInfoDict['hostlunid'] is not None):
@@ -423,7 +423,7 @@ class EMCVMAXCommon(object):
             self.conn, maskingViewDict, extraSpecs)
 
         # Find host lun id again after the volume is exported to the host.
-        deviceInfoDict = self.find_device_number(volume)
+        deviceInfoDict = self.find_device_number(volume, connector['host'])
         if 'hostlunid' not in deviceInfoDict:
             # Did not successfully attach to host,
             # so a rollback for FAST is required.
@@ -1412,15 +1412,18 @@ class EMCVMAXCommon(object):
                    'initiator': foundinitiatornames})
         return foundinitiatornames
 
-    def find_device_number(self, volume):
+    def find_device_number(self, volume, host):
         """Given the volume dict find a device number.
 
         Find a device number that a host can see
         for a volume.
 
         :param volume: the volume dict
+        :param host: host from connector
         :returns: dict -- the data dict
         """
+        maskedvols = []
+        data = {}
         foundNumDeviceNumber = None
         foundMaskingViewName = None
         volumeName = volume['name']
@@ -1448,23 +1451,33 @@ class EMCVMAXCommon(object):
                     if properties[0] == 'ElementName':
                         cimProperties = properties[1]
                         foundMaskingViewName = cimProperties.value
-                        break
 
-                break
+                devicedict = {'hostlunid': foundNumDeviceNumber,
+                              'storagesystem': storageSystemName,
+                              'maskingview': foundMaskingViewName}
+                maskedvols.append(devicedict)
 
-        if foundNumDeviceNumber is None:
+        if not maskedvols:
             LOG.debug(
                 "Device number not found for volume "
                 "%(volumeName)s %(volumeInstance)s.",
                 {'volumeName': volumeName,
                  'volumeInstance': volumeInstance.path})
+        else:
+            hoststr = ("-%(host)s-"
+                       % {'host': host})
 
-        data = {'hostlunid': foundNumDeviceNumber,
-                'storagesystem': storageSystemName,
-                'maskingview': foundMaskingViewName}
+            for maskedvol in maskedvols:
+                if hoststr.lower() in maskedvol['maskingview'].lower():
+                    data = maskedvol
+                    break
+            if not data:
+                LOG.warning(_LW(
+                    "Volume is masked but not to host %(host)s as "
+                    "expected. Returning empty dictionary."),
+                    {'host': hoststr})
 
         LOG.debug("Device info: %(data)s.", {'data': data})
-
         return data
 
     def get_target_wwns(self, storageSystem, connector):
index 179cada101e5e18aaaf49d2d435a1873a35d03d9..8e97262ec07d5b835f500e629ebeafb36e24fac4 100644 (file)
@@ -212,7 +212,8 @@ class EMCVMAXISCSIDriver(driver.ISCSIDriver):
         LOG.debug("ISCSI Discovery: Found %s", location)
         properties['target_discovered'] = True
 
-        device_info = self.common.find_device_number(volume)
+        device_info = self.common.find_device_number(
+            volume, connector['host'])
 
         if device_info is None or device_info['hostlunid'] is None:
             exception_message = (_("Cannot find device number for volume "
index 7107382335e2bcd5fc3063ecc546c035166cf64b..e0e61008438987a035f87c84af7a2468130a1f95 100644 (file)
@@ -60,7 +60,7 @@ class EMCVMAXMasking(object):
             return self.get_or_create_masking_view_and_map_lun(conn,
                                                                maskingViewDict,
                                                                extraSpecs)
-        do_get_or_create_masking_view_and_map_lun()
+        return do_get_or_create_masking_view_and_map_lun()
 
     def get_or_create_masking_view_and_map_lun(self, conn, maskingViewDict,
                                                extraSpecs):