Replace sg_scan with lsscsi to get '[H:C:T:L]'

The current get_device_info uses sg_scan to get device info but it only
returns HLU number lower than 255 due to bug#1793259. sg_scan was
designed for old days when 255 LUNs were enough. However we now have
requirement to support HLU number greater than 255. Since lsscsi doesn't
have the limit of 255, we should use lsscsi to get device info.

The 'device' of get_device_info can be of 2 types:
o /dev/disk/by-path/xxx, which is a symlink to /dev/sdX
o /dev/sdX

sg_scan can take any device name but lsscsi only show /dev/sdx names.
So if the device is a symlink, we use the device name it links to,
otherwise we use it directly.
Then get the device info '[H:C:T:L]' by comparing the device name with the
last column of lsscsi output
Also lsscsi doesn't require privilege.

Depends-on: https://review.opendev.org/743548
Change-Id: I867c972d9f712c0df4260ebc8211b786006ed7a2
Closes-bug: #1793259
This commit is contained in:
Sam Wan 2020-07-23 22:35:27 -04:00 committed by Sean McGinnis
parent 26299257ce
commit fc6ca22bdb
3 changed files with 23 additions and 16 deletions

View File

@ -17,6 +17,7 @@ libopenssl-devel [platform:suse !platform:rpm]
iscsi-initiator-utils [platform:rpm !platform:suse]
open-iscsi [platform:suse platform:dpkg]
lsscsi
# Binary dependencies for PDF doc generation
fonts-liberation [doc platform:dpkg]

View File

@ -101,22 +101,30 @@ class LinuxSCSI(executor.Executor):
raise exception.VolumePathNotRemoved(volume_path=exist)
def get_device_info(self, device):
(out, _err) = self._execute('sg_scan', device, run_as_root=True,
root_helper=self._root_helper)
dev_info = {'device': device, 'host': None,
'channel': None, 'id': None, 'lun': None}
# The input argument 'device' can be of 2 types:
# (a) /dev/disk/by-path/XXX which is a symlink to /dev/sdX device
# (b) /dev/sdX
# If it's a symlink, get the /dev/sdX name first
if os.path.islink(device):
device = '/dev/' + os.readlink(device).split('/')[-1]
# Else it's already a /dev/sdX device.
# Then get it from lsscsi output
(out, _err) = self._execute('lsscsi')
if out:
line = out.strip()
line = line.replace(device + ": ", "")
info = line.split(" ")
for item in info:
if '=' in item:
pair = item.split('=')
dev_info[pair[0]] = pair[1]
elif 'scsi' in item:
dev_info['host'] = item.replace('scsi', '')
for line in out.strip().split('\n'):
# The last column of lsscsi is device name
if line.split()[-1] == device:
# The first column of lsscsi is [H:C:T:L]
hctl_info = line.split()[0].strip('[]').split(':')
dev_info['host'] = hctl_info[0]
dev_info['channel'] = hctl_info[1]
dev_info['id'] = hctl_info[2]
dev_info['lun'] = hctl_info[3]
break
LOG.debug('dev_info=%s', str(dev_info))
return dev_info
def get_sysfs_wwn(self, device_names):

View File

@ -848,14 +848,12 @@ loop0 0"""
'multipathd', 'show', 'status', run_as_root=True, root_helper=None)
def test_get_device_info(self):
ret = "/dev/sg0 scsi1 channel=1 id=0 lun=0 [em]\n"
ret = "[1:1:0:0] disk Vendor Array 0100 /dev/adevice\n"
with mock.patch.object(self.linuxscsi, '_execute') as exec_mock:
exec_mock.return_value = (ret, "")
info = self.linuxscsi.get_device_info('/dev/adevice')
exec_mock.assert_called_once_with('sg_scan', '/dev/adevice',
root_helper=None,
run_as_root=True)
exec_mock.assert_called_once_with('lsscsi')
self.assertEqual(info, {'channel': '1',
'device': '/dev/adevice',
'host': '1',