diff --git a/os_brick/initiator/connectors/iscsi.py b/os_brick/initiator/connectors/iscsi.py index 4701fade0..13a0d62d0 100644 --- a/os_brick/initiator/connectors/iscsi.py +++ b/os_brick/initiator/connectors/iscsi.py @@ -737,7 +737,7 @@ class ISCSIConnector(base.BaseLinuxConnector, base_iscsi.BaseISCSIConnector): data['failed_logins'])): # We have devices but we don't know the wwn yet if not wwn and found: - wwn = self._linuxscsi.get_sysfs_wwn(found) + wwn = self._linuxscsi.get_sysfs_wwn(found, mpath) if not mpath and found: mpath = self._linuxscsi.find_sysfs_multipath_dm(found) # We have the wwn but not a multipath @@ -778,6 +778,8 @@ class ISCSIConnector(base.BaseLinuxConnector, base_iscsi.BaseISCSIConnector): if not mpath: LOG.warning('No dm was created, connection to volume is probably ' 'bad and will perform poorly.') + elif not wwn: + wwn = self._linuxscsi.get_sysfs_wwn(found, mpath) return self._get_connect_result(connection_properties, wwn, found, mpath) diff --git a/os_brick/initiator/linuxscsi.py b/os_brick/initiator/linuxscsi.py index daa8259d6..f2cb45ab6 100644 --- a/os_brick/initiator/linuxscsi.py +++ b/os_brick/initiator/linuxscsi.py @@ -127,8 +127,20 @@ class LinuxSCSI(executor.Executor): LOG.debug('dev_info=%s', str(dev_info)) return dev_info - def get_sysfs_wwn(self, device_names): + def get_sysfs_wwn(self, device_names, mpath=None): """Return the wwid from sysfs in any of devices in udev format.""" + # If we have a multipath DM we know that it has found the WWN + if mpath: + # We have the WWN in /uuid even with friendly names, unline /name + try: + with open('/sys/block/%s/dm/uuid' % mpath) as f: + # Contents are matph-WWN, so get the part we want + wwid = f.read().strip()[6:] + if wwid: # Check should not be needed, but just in case + return wwid + except Exception as exc: + LOG.warning('Failed to read the DM uuid: %s', exc) + wwid = self.get_sysfs_wwid(device_names) glob_str = '/dev/disk/by-id/scsi-' wwn_paths = glob.glob(glob_str + '*') diff --git a/os_brick/tests/initiator/connectors/test_iscsi.py b/os_brick/tests/initiator/connectors/test_iscsi.py index 9b3dad366..248b532d7 100644 --- a/os_brick/tests/initiator/connectors/test_iscsi.py +++ b/os_brick/tests/initiator/connectors/test_iscsi.py @@ -1206,6 +1206,8 @@ Setting up iSCSI targets: unused result = list(get_wwn_mock.call_args[0][0]) result.sort() self.assertEqual(['sda', 'sdb', 'sdc', 'sdd'], result) + # Check we pass the mpath + self.assertIsNone(get_wwn_mock.call_args[0][1]) add_wwid_mock.assert_called_once_with('wwn') self.assertNotEqual(0, add_path_mock.call_count) self.assertGreaterEqual(find_dm_mock.call_count, 2) @@ -1238,10 +1240,13 @@ Setting up iSCSI targets: unused 'path': '/dev/dm-0'} self.assertEqual(expected, res) - self.assertEqual(2, get_wwn_mock.call_count) + self.assertEqual(3, get_wwn_mock.call_count) result = list(get_wwn_mock.call_args[0][0]) result.sort() self.assertEqual(['sda', 'sdb', 'sdc', 'sdd'], result) + # Initially mpath we pass is None, but on last call is the mpath + mpath_values = [c[1][1] for c in get_wwn_mock._mock_mock_calls] + self.assertEqual([None, None, 'dm-0'], mpath_values) add_wwid_mock.assert_not_called() add_path_mock.assert_not_called() self.assertGreaterEqual(find_dm_mock.call_count, 2) diff --git a/os_brick/tests/initiator/test_linuxscsi.py b/os_brick/tests/initiator/test_linuxscsi.py index 5b8946451..096f1da0a 100644 --- a/os_brick/tests/initiator/test_linuxscsi.py +++ b/os_brick/tests/initiator/test_linuxscsi.py @@ -860,6 +860,16 @@ loop0 0""" 'id': '0', 'lun': '0'}) + @mock.patch('six.moves.builtins.open') + def test_get_sysfs_wwn_mpath(self, open_mock): + wwn = '3600d0230000000000e13955cc3757800' + cm_open = open_mock.return_value.__enter__.return_value + cm_open.read.return_value = 'mpath-' + wwn + + res = self.linuxscsi.get_sysfs_wwn(mock.sentinel.device_names, 'dm-1') + open_mock.assert_called_once_with('/sys/block/dm-1/dm/uuid') + self.assertEqual(wwn, res) + @mock.patch('glob.glob') @mock.patch.object(linuxscsi.LinuxSCSI, 'get_sysfs_wwid') def test_get_sysfs_wwn_single_designator(self, get_wwid_mock, glob_mock): @@ -871,6 +881,20 @@ loop0 0""" glob_mock.assert_called_once_with('/dev/disk/by-id/scsi-*') get_wwid_mock.assert_called_once_with(mock.sentinel.device_names) + @mock.patch('six.moves.builtins.open', side_effect=Exception) + @mock.patch('glob.glob') + @mock.patch.object(linuxscsi.LinuxSCSI, 'get_sysfs_wwid') + def test_get_sysfs_wwn_mpath_exc(self, get_wwid_mock, glob_mock, + open_mock): + glob_mock.return_value = ['/dev/disk/by-id/scsi-wwid1', + '/dev/disk/by-id/scsi-wwid2'] + get_wwid_mock.return_value = 'wwid1' + res = self.linuxscsi.get_sysfs_wwn(mock.sentinel.device_names, 'dm-1') + open_mock.assert_called_once_with('/sys/block/dm-1/dm/uuid') + self.assertEqual('wwid1', res) + glob_mock.assert_called_once_with('/dev/disk/by-id/scsi-*') + get_wwid_mock.assert_called_once_with(mock.sentinel.device_names) + @mock.patch('os.listdir', return_value=['sda', 'sdd']) @mock.patch('os.path.realpath', side_effect=('/other/path', '/dev/dm-5',