From 609f0bcf83ca0931636018eaa843faacbf0f64f3 Mon Sep 17 00:00:00 2001 From: Gorka Eguileor Date: Mon, 1 Jun 2020 19:07:47 +0200 Subject: [PATCH] Leverage the iSCSI mpath to get the WWN Now that we search the multipath device even if we haven't been able to find the WWN in the sysfs we can leverage the multipath daemon information on sysfs to get the WWN. Pass the mpath to "get_sysfs_wwn" method where we check the sysfs to get the WWN. Conflicts: os_brick/tests/initiator/connectors/test_iscsi.py os_brick/tests/initiator/test_linuxscsi.py The tests required a few fixes to account for the differences between the branches and the changes introduced by the backports this one builds upon. Change-Id: Id1905bc174b8f2f3a345664d8a0a05284ca69927 (cherry picked from commit 0cdd9bbbe29201942c9a296d6db2175452044689) (cherry picked from commit 31c01b543cfe0219140e5bf9eaf703daa819de04) --- os_brick/initiator/connectors/iscsi.py | 4 +++- os_brick/initiator/linuxscsi.py | 14 ++++++++++- .../tests/initiator/connectors/test_iscsi.py | 7 +++++- os_brick/tests/initiator/test_linuxscsi.py | 24 +++++++++++++++++++ 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/os_brick/initiator/connectors/iscsi.py b/os_brick/initiator/connectors/iscsi.py index f497e5da4..8eb5a5bb1 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 3c42f0aa7..3edf452be 100644 --- a/os_brick/initiator/linuxscsi.py +++ b/os_brick/initiator/linuxscsi.py @@ -119,8 +119,20 @@ class LinuxSCSI(executor.Executor): 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 ee8ef351b..1b70e9af3 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.assertGreaterEqual(get_wwn_mock.call_count, 2) + self.assertGreaterEqual(get_wwn_mock.call_count, 3) 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[0:3]) 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 bd0dfb4dd..b4c02da24 100644 --- a/os_brick/tests/initiator/test_linuxscsi.py +++ b/os_brick/tests/initiator/test_linuxscsi.py @@ -848,6 +848,16 @@ loop0 0""" mock_exec.assert_called_once_with( 'multipathd', 'show', 'status', run_as_root=True, root_helper=None) + @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): @@ -859,6 +869,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',