From: Lee Yarwood Date: Thu, 2 Apr 2015 14:46:38 +0000 (+0100) Subject: Fix multipath device discovery when UFN is enabled. X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=2fe4de571e925e26c77bffbda908f16e375b8a83;p=openstack-build%2Fcinder-build.git Fix multipath device discovery when UFN is enabled. This currently returns an invalid path of `/dev/mapper/${WWID}` when UFN is enabled leading to failures later on when we attempt to use the device. The output of `multipath -l ${path}` or `multipath -l ${wwid}` should always list the correct device identifier to use with this path as the first word on the first line. Closes-Bug: 1401799 Change-Id: Ib371b699fadcbbbb666e08eb0124c442e94a55e8 --- diff --git a/cinder/brick/initiator/linuxscsi.py b/cinder/brick/initiator/linuxscsi.py index aa1272c08..f702df7d8 100644 --- a/cinder/brick/initiator/linuxscsi.py +++ b/cinder/brick/initiator/linuxscsi.py @@ -30,6 +30,7 @@ from cinder.openstack.common import loopingcall LOG = logging.getLogger(__name__) MULTIPATH_ERROR_REGEX = re.compile("\w{3} \d+ \d\d:\d\d:\d\d \|.*$") +MULTIPATH_WWID_REGEX = re.compile("\((?P.+)\)") class LinuxSCSI(executor.Executor): @@ -182,21 +183,26 @@ class LinuxSCSI(executor.Executor): lines = [line for line in lines if not re.match(MULTIPATH_ERROR_REGEX, line)] if lines: - line = lines[0] - info = line.split(" ") - # device line output is different depending - # on /etc/multipath.conf settings. - if info[1][:2] == "dm": - mdev = "/dev/%s" % info[1] - mdev_id = info[0] - elif info[2][:2] == "dm": - mdev = "/dev/%s" % info[2] - mdev_id = info[1].replace('(', '') - mdev_id = mdev_id.replace(')', '') - - if mdev is None: - LOG.warn(_LW("Couldn't find multipath device %(line)s") - % {'line': line}) + + # Use the device name, be it the WWID, mpathN or custom alias + # of a device to build the device path. This should be the + # first item on the first line of output from `multipath -l + # ${path}` or `multipath -l ${wwid}`.. + mdev_name = lines[0].split(" ")[0] + mdev = '/dev/mapper/%s' % mdev_name + + # Find the WWID for the LUN if we are using mpathN or aliases. + wwid_search = MULTIPATH_WWID_REGEX.search(lines[0]) + if wwid_search is not None: + mdev_id = wwid_search.group('wwid') + else: + mdev_id = mdev_name + + # Confirm that the device is present. + try: + os.stat(mdev) + except OSError: + LOG.warn(_LW("Couldn't find multipath device %s"), mdev) return None LOG.debug("Found multipath device = %(mdev)s" @@ -220,6 +226,7 @@ class LinuxSCSI(executor.Executor): if mdev is not None: info = {"device": mdev, "id": mdev_id, + "name": mdev_name, "devices": devices} return info return None diff --git a/cinder/tests/brick/test_brick_linuxscsi.py b/cinder/tests/brick/test_brick_linuxscsi.py index cac64b2e8..0b3e35bac 100644 --- a/cinder/tests/brick/test_brick_linuxscsi.py +++ b/cinder/tests/brick/test_brick_linuxscsi.py @@ -12,6 +12,7 @@ # License for the specific language governing permissions and limitations # under the License. +import os import os.path import string @@ -30,11 +31,15 @@ class LinuxSCSITestCase(test.TestCase): self.cmds = [] self.stubs.Set(os.path, 'realpath', lambda x: '/dev/sdc') self.linuxscsi = linuxscsi.LinuxSCSI(None, execute=self.fake_execute) + self.fake_stat_result = os.stat(__file__) def fake_execute(self, *cmd, **kwargs): self.cmds.append(string.join(cmd)) return "", None + def fake_stat(self, path): + return self.fake_stat_result + def test_echo_scsi_command(self): self.linuxscsi.echo_scsi_command("/some/path", "1") expected_commands = ['tee -a /some/path'] @@ -110,7 +115,7 @@ class LinuxSCSITestCase(test.TestCase): ('multipath -f 350002ac20398383d'), ] self.assertEqual(expected_commands, self.cmds) - def test_find_multipath_device_3par(self): + def test_find_multipath_device_3par_ufn(self): def fake_execute(*cmd, **kwargs): out = ("mpath6 (350002ac20398383d) dm-3 3PARdata,VV\n" "size=2.0G features='0' hwhandler='0' wp=rw\n" @@ -121,10 +126,15 @@ class LinuxSCSITestCase(test.TestCase): return out, None self.stubs.Set(self.linuxscsi, '_execute', fake_execute) + self.stubs.SmartSet(os, 'stat', self.fake_stat) info = self.linuxscsi.find_multipath_device('/dev/sde') LOG.error("info = %s" % info) - self.assertEqual("/dev/dm-3", info["device"]) + + self.assertEqual("350002ac20398383d", info['id']) + self.assertEqual("mpath6", info['name']) + self.assertEqual("/dev/mapper/mpath6", info['device']) + self.assertEqual("/dev/sde", info['devices'][0]['device']) self.assertEqual("0", info['devices'][0]['host']) self.assertEqual("0", info['devices'][0]['id']) @@ -152,10 +162,16 @@ class LinuxSCSITestCase(test.TestCase): return out, None self.stubs.Set(self.linuxscsi, '_execute', fake_execute) + self.stubs.SmartSet(os, 'stat', self.fake_stat) info = self.linuxscsi.find_multipath_device('/dev/sde') LOG.error("info = %s" % info) - self.assertEqual("/dev/dm-2", info["device"]) + + self.assertEqual("36005076da00638089c000000000004d5", info["id"]) + self.assertEqual("36005076da00638089c000000000004d5", info["name"]) + self.assertEqual("/dev/mapper/36005076da00638089c000000000004d5", + info["device"]) + self.assertEqual("/dev/sde", info['devices'][0]['device']) self.assertEqual("6", info['devices'][0]['host']) self.assertEqual("0", info['devices'][0]['channel']) @@ -180,10 +196,16 @@ class LinuxSCSITestCase(test.TestCase): return out, None self.stubs.Set(self.linuxscsi, '_execute', fake_execute) + self.stubs.SmartSet(os, 'stat', self.fake_stat) info = self.linuxscsi.find_multipath_device('/dev/sdd') LOG.error("info = %s" % info) - self.assertEqual("/dev/dm-2", info["device"]) + + self.assertEqual("36005076303ffc48e0000000000000101", info["id"]) + self.assertEqual("36005076303ffc48e0000000000000101", info["name"]) + self.assertEqual("/dev/mapper/36005076303ffc48e0000000000000101", + info["device"]) + self.assertEqual("/dev/sdd", info['devices'][0]['device']) self.assertEqual("6", info['devices'][0]['host']) self.assertEqual("0", info['devices'][0]['channel']) @@ -209,10 +231,16 @@ class LinuxSCSITestCase(test.TestCase): return out, None self.stubs.Set(self.linuxscsi, '_execute', fake_execute) + self.stubs.SmartSet(os, 'stat', self.fake_stat) info = self.linuxscsi.find_multipath_device('/dev/sdd') LOG.error("info = %s" % info) - self.assertEqual("/dev/dm-2", info["device"]) + + self.assertEqual("36005076303ffc48e0000000000000101", info["id"]) + self.assertEqual("36005076303ffc48e0000000000000101", info["name"]) + self.assertEqual("/dev/mapper/36005076303ffc48e0000000000000101", + info["device"]) + self.assertEqual("/dev/sdd", info['devices'][0]['device']) self.assertEqual("6", info['devices'][0]['host']) self.assertEqual("0", info['devices'][0]['channel'])