Add HCTL to BlockDevices

This patch is adding a "hctl" attribute to the BlockDevices. HCTL stands
for: Host, Channel, Target and Lun, which is basically the SCSI address.

The idea behind this patch is to allow root device hints to find the
disk for deployment based on the SCSI address.

Partial-Bug: #1648036
Change-Id: If8897c68609e0df0378ee919b803ca5e497def02
This commit is contained in:
Lucas Alvares Gomes 2016-12-07 10:50:45 +00:00
parent 92ced38bfe
commit b272426562
4 changed files with 65 additions and 15 deletions

View File

@ -114,7 +114,7 @@ fields:
``disks`` ``disks``
list of disk block devices with fields: ``name``, ``model``, list of disk block devices with fields: ``name``, ``model``,
``size`` (in bytes), ``rotational`` (boolean), ``wwn``, ``serial``, ``size`` (in bytes), ``rotational`` (boolean), ``wwn``, ``serial``,
``vendor``, ``wwn_with_extension``, ``wwn_vendor_extension``. ``vendor``, ``wwn_with_extension``, ``wwn_vendor_extension``, ``hctl``.
``interfaces`` ``interfaces``
list of network interfaces with fields: ``name``, ``mac_address``, list of network interfaces with fields: ``name``, ``mac_address``,

View File

@ -154,6 +154,16 @@ def list_all_block_devices(block_type='disk'):
('wwn_with_extension', 'WWN_WITH_EXTENSION'), ('wwn_with_extension', 'WWN_WITH_EXTENSION'),
('wwn_vendor_extension', 'WWN_VENDOR_EXTENSION')]} ('wwn_vendor_extension', 'WWN_VENDOR_EXTENSION')]}
# NOTE(lucasagomes): Newer versions of the lsblk tool supports
# HCTL as a parameter but let's get it from sysfs to avoid breaking
# old distros.
try:
extra['hctl'] = os.listdir(
'/sys/block/%s/device/scsi_device' % device['KNAME'])[0]
except (OSError, IndexError):
LOG.warning('Could not find the SCSI address (HCTL) for '
'device %s. Skipping', name)
devices.append(BlockDevice(name=name, devices.append(BlockDevice(name=name,
model=device['MODEL'], model=device['MODEL'],
size=int(device['SIZE']), size=int(device['SIZE']),
@ -188,11 +198,11 @@ class HardwareType(object):
class BlockDevice(encoding.SerializableComparable): class BlockDevice(encoding.SerializableComparable):
serializable_fields = ('name', 'model', 'size', 'rotational', serializable_fields = ('name', 'model', 'size', 'rotational',
'wwn', 'serial', 'vendor', 'wwn_with_extension', 'wwn', 'serial', 'vendor', 'wwn_with_extension',
'wwn_vendor_extension') 'wwn_vendor_extension', 'hctl')
def __init__(self, name, model, size, rotational, wwn=None, serial=None, def __init__(self, name, model, size, rotational, wwn=None, serial=None,
vendor=None, wwn_with_extension=None, vendor=None, wwn_with_extension=None,
wwn_vendor_extension=None): wwn_vendor_extension=None, hctl=None):
self.name = name self.name = name
self.model = model self.model = model
self.size = size self.size = size
@ -202,6 +212,7 @@ class BlockDevice(encoding.SerializableComparable):
self.vendor = vendor self.vendor = vendor
self.wwn_with_extension = wwn_with_extension self.wwn_with_extension = wwn_with_extension
self.wwn_vendor_extension = wwn_vendor_extension self.wwn_vendor_extension = wwn_vendor_extension
self.hctl = hctl
class NetworkInterface(encoding.SerializableComparable): class NetworkInterface(encoding.SerializableComparable):

View File

@ -734,11 +734,13 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
list_mock.assert_called_once_with() list_mock.assert_called_once_with()
@mock.patch.object(os, 'listdir')
@mock.patch.object(hardware, '_get_device_info') @mock.patch.object(hardware, '_get_device_info')
@mock.patch.object(pyudev.Device, 'from_device_file') @mock.patch.object(pyudev.Device, 'from_device_file')
@mock.patch.object(utils, 'execute') @mock.patch.object(utils, 'execute')
def test_list_all_block_device(self, mocked_execute, mocked_udev, def test_list_all_block_device(self, mocked_execute, mocked_udev,
mocked_dev_vendor): mocked_dev_vendor, mock_listdir):
mock_listdir.return_value = ['1:0:0:0']
mocked_execute.return_value = (BLK_DEVICE_TEMPLATE, '') mocked_execute.return_value = (BLK_DEVICE_TEMPLATE, '')
mocked_udev.side_effect = pyudev.DeviceNotFoundError() mocked_udev.side_effect = pyudev.DeviceNotFoundError()
mocked_dev_vendor.return_value = 'Super Vendor' mocked_dev_vendor.return_value = 'Super Vendor'
@ -748,31 +750,38 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
model='TinyUSB Drive', model='TinyUSB Drive',
size=3116853504, size=3116853504,
rotational=False, rotational=False,
vendor='Super Vendor'), vendor='Super Vendor',
hctl='1:0:0:0'),
hardware.BlockDevice(name='/dev/sdb', hardware.BlockDevice(name='/dev/sdb',
model='Fastable SD131 7', model='Fastable SD131 7',
size=10737418240, size=10737418240,
rotational=False, rotational=False,
vendor='Super Vendor'), vendor='Super Vendor',
hctl='1:0:0:0'),
hardware.BlockDevice(name='/dev/sdc', hardware.BlockDevice(name='/dev/sdc',
model='NWD-BLP4-1600', model='NWD-BLP4-1600',
size=1765517033472, size=1765517033472,
rotational=False, rotational=False,
vendor='Super Vendor'), vendor='Super Vendor',
hctl='1:0:0:0'),
hardware.BlockDevice(name='/dev/sdd', hardware.BlockDevice(name='/dev/sdd',
model='NWD-BLP4-1600', model='NWD-BLP4-1600',
size=1765517033472, size=1765517033472,
rotational=False, rotational=False,
vendor='Super Vendor') vendor='Super Vendor',
hctl='1:0:0:0'),
] ]
self.assertEqual(4, len(devices)) self.assertEqual(4, len(devices))
for expected, device in zip(expected_devices, devices): for expected, device in zip(expected_devices, devices):
# Compare all attrs of the objects # Compare all attrs of the objects
for attr in ['name', 'model', 'size', 'rotational', for attr in ['name', 'model', 'size', 'rotational',
'wwn', 'vendor', 'serial']: 'wwn', 'vendor', 'serial', 'hctl']:
self.assertEqual(getattr(expected, attr), self.assertEqual(getattr(expected, attr),
getattr(device, attr)) getattr(device, attr))
expected_calls = [mock.call('/sys/block/%s/device/scsi_device' % dev)
for dev in ('sda', 'sdb', 'sdc', 'sdd')]
mock_listdir.assert_has_calls(expected_calls)
@mock.patch.object(hardware, '_get_device_info') @mock.patch.object(hardware, '_get_device_info')
@mock.patch.object(pyudev.Device, 'from_device_file') @mock.patch.object(pyudev.Device, 'from_device_file')
@ -786,11 +795,29 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
devices = hardware.list_all_block_devices() devices = hardware.list_all_block_devices()
self.assertEqual(4, len(devices)) self.assertEqual(4, len(devices))
@mock.patch.object(os, 'listdir')
@mock.patch.object(hardware, '_get_device_info')
@mock.patch.object(pyudev.Device, 'from_device_file')
@mock.patch.object(utils, 'execute')
def test_list_all_block_device_hctl_fail(self, mocked_execute, mocked_udev,
mocked_dev_vendor,
mocked_listdir):
mocked_listdir.side_effect = (OSError, IndexError)
mocked_execute.return_value = (BLK_DEVICE_TEMPLATE_SMALL, '')
mocked_dev_vendor.return_value = 'Super Vendor'
devices = hardware.list_all_block_devices()
self.assertEqual(2, len(devices))
expected_calls = [mock.call('/sys/block/%s/device/scsi_device' % dev)
for dev in ('sda', 'sdb')]
mocked_listdir.assert_has_calls(expected_calls)
@mock.patch.object(os, 'listdir')
@mock.patch.object(hardware, '_get_device_info') @mock.patch.object(hardware, '_get_device_info')
@mock.patch.object(pyudev.Device, 'from_device_file') @mock.patch.object(pyudev.Device, 'from_device_file')
@mock.patch.object(utils, 'execute') @mock.patch.object(utils, 'execute')
def test_list_all_block_device_with_udev(self, mocked_execute, mocked_udev, def test_list_all_block_device_with_udev(self, mocked_execute, mocked_udev,
mocked_dev_vendor): mocked_dev_vendor, mock_listdir):
mock_listdir.return_value = ['1:0:0:0']
mocked_execute.return_value = (BLK_DEVICE_TEMPLATE, '') mocked_execute.return_value = (BLK_DEVICE_TEMPLATE, '')
mocked_udev.side_effect = iter([ mocked_udev.side_effect = iter([
{'ID_WWN': 'wwn%d' % i, 'ID_SERIAL_SHORT': 'serial%d' % i, {'ID_WWN': 'wwn%d' % i, 'ID_SERIAL_SHORT': 'serial%d' % i,
@ -809,7 +836,8 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
wwn='wwn0', wwn='wwn0',
wwn_with_extension='wwn-ext0', wwn_with_extension='wwn-ext0',
wwn_vendor_extension='wwn-vendor-ext0', wwn_vendor_extension='wwn-vendor-ext0',
serial='serial0'), serial='serial0',
hctl='1:0:0:0'),
hardware.BlockDevice(name='/dev/sdb', hardware.BlockDevice(name='/dev/sdb',
model='Fastable SD131 7', model='Fastable SD131 7',
size=10737418240, size=10737418240,
@ -818,7 +846,8 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
wwn='wwn1', wwn='wwn1',
wwn_with_extension='wwn-ext1', wwn_with_extension='wwn-ext1',
wwn_vendor_extension='wwn-vendor-ext1', wwn_vendor_extension='wwn-vendor-ext1',
serial='serial1'), serial='serial1',
hctl='1:0:0:0'),
hardware.BlockDevice(name='/dev/sdc', hardware.BlockDevice(name='/dev/sdc',
model='NWD-BLP4-1600', model='NWD-BLP4-1600',
size=1765517033472, size=1765517033472,
@ -827,7 +856,8 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
wwn='wwn2', wwn='wwn2',
wwn_with_extension='wwn-ext2', wwn_with_extension='wwn-ext2',
wwn_vendor_extension='wwn-vendor-ext2', wwn_vendor_extension='wwn-vendor-ext2',
serial='serial2'), serial='serial2',
hctl='1:0:0:0'),
hardware.BlockDevice(name='/dev/sdd', hardware.BlockDevice(name='/dev/sdd',
model='NWD-BLP4-1600', model='NWD-BLP4-1600',
size=1765517033472, size=1765517033472,
@ -836,7 +866,8 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
wwn='wwn3', wwn='wwn3',
wwn_with_extension='wwn-ext3', wwn_with_extension='wwn-ext3',
wwn_vendor_extension='wwn-vendor-ext3', wwn_vendor_extension='wwn-vendor-ext3',
serial='serial3') serial='serial3',
hctl='1:0:0:0')
] ]
self.assertEqual(4, len(expected_devices)) self.assertEqual(4, len(expected_devices))
@ -844,9 +875,12 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
# Compare all attrs of the objects # Compare all attrs of the objects
for attr in ['name', 'model', 'size', 'rotational', for attr in ['name', 'model', 'size', 'rotational',
'wwn', 'vendor', 'serial', 'wwn_with_extension', 'wwn', 'vendor', 'serial', 'wwn_with_extension',
'wwn_vendor_extension']: 'wwn_vendor_extension', 'hctl']:
self.assertEqual(getattr(expected, attr), self.assertEqual(getattr(expected, attr),
getattr(device, attr)) getattr(device, attr))
expected_calls = [mock.call('/sys/block/%s/device/scsi_device' % dev)
for dev in ('sda', 'sdb', 'sdc', 'sdd')]
mock_listdir.assert_has_calls(expected_calls)
@mock.patch.object(hardware, 'dispatch_to_managers') @mock.patch.object(hardware, 'dispatch_to_managers')
def test_erase_devices(self, mocked_dispatch): def test_erase_devices(self, mocked_dispatch):
@ -1500,6 +1534,7 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
mocked_isdir.assert_called_once_with('/sys/firmware/efi') mocked_isdir.assert_called_once_with('/sys/firmware/efi')
@mock.patch.object(os, 'listdir', lambda *_: [])
@mock.patch.object(utils, 'execute', autospec=True) @mock.patch.object(utils, 'execute', autospec=True)
class TestModuleFunctions(test_base.BaseTestCase): class TestModuleFunctions(test_base.BaseTestCase):

View File

@ -0,0 +1,4 @@
---
features:
- Add HCTL (Host, Channel, Target and Lun) information to the block
devices.