add mixed matching of root device hints

This commit introduces the following changes:
  - New optional `all_serial_and_wwn` argument for the block device
    listing logic. The new argument makes it possible to
    collect wwn and serial number information from both
    lsblk and udevadm at the same time
  - Both the short and the long serials are collected
    from udeavadm without prioritization when the new argument
    has teh value True
  - The new feature is automatically enabled during block device listing
    as part of the root disk selecetion
  - New options are added to the lsblk command when used in the block
    device discovery process, previously lsblk was not looking
    for wwn numbers and now it does

Closes-Bug: #2061437
Change-Id: I438a686d948cd929311e2f418bb02fb771805148
Signed-off-by: Adam Rozman <adam.rozman@est.tech>
This commit is contained in:
Adam Rozman 2023-11-01 11:20:27 +02:00
parent cdd0a83448
commit 84a1195d5a
6 changed files with 323 additions and 107 deletions

View File

@ -461,7 +461,8 @@ def list_all_block_devices(block_type='disk',
ignore_raid=False,
ignore_floppy=True,
ignore_empty=True,
ignore_multipath=False):
ignore_multipath=False,
all_serial_and_wwn=False):
"""List all physical block devices
The switches we use for lsblk: P for KEY="value" output, b for size output
@ -481,6 +482,13 @@ def list_all_block_devices(block_type='disk',
:param ignore_multipath: Whether to ignore devices backing multipath
devices. Default is to consider multipath
devices, if possible.
:param all_serial_and_wwn: Don't collect serial and wwn numbers based
on a priority order, instead collect wwn
numbers from both udevadm and lsblk. When
enabled this option will allso collect both
the short and the long serial from udevadm if
possible.
:return: A list of BlockDevices
"""
@ -627,8 +635,15 @@ def list_all_block_devices(block_type='disk',
extra = {}
lsblk_serial = device_raw.get('serial')
if lsblk_serial:
extra['serial'] = lsblk_serial
lsblk_wwn = device_raw.get('wwn')
if all_serial_and_wwn:
extra['serial'] = [lsblk_serial]
extra['wwn'] = [lsblk_wwn]
else:
if lsblk_serial:
extra['serial'] = lsblk_serial
if lsblk_wwn:
extra['wwn'] = lsblk_wwn
try:
udev = pyudev.Devices.from_device_file(context, name)
except pyudev.DeviceNotFoundByFileError as e:
@ -648,18 +663,23 @@ def list_all_block_devices(block_type='disk',
]
# Only check device serial information from udev
# when lsblk returned None
if not lsblk_serial:
if all_serial_and_wwn or not lsblk_serial:
udev_property_mappings += [
('serial', 'SERIAL_SHORT'),
('serial', 'SERIAL')
]
for key, udev_key in udev_property_mappings:
if key in extra:
continue
value = (udev.get(f'ID_{udev_key}')
or udev.get(f'DM_{udev_key}')) # devicemapper
if value:
extra[key] = value
if all_serial_and_wwn and (key == 'wwn' or key == 'serial'):
value = (udev.get(f'ID_{udev_key}')
or udev.get(f'DM_{udev_key}')) # devicemapper
extra[key].append(value)
else:
if key in extra:
continue
value = (udev.get(f'ID_{udev_key}')
or udev.get(f'DM_{udev_key}')) # devicemapper
if value:
extra[key] = value
# NOTE(lucasagomes): Newer versions of the lsblk tool supports
# HCTL as a parameter but let's get it from sysfs to avoid breaking
@ -1527,8 +1547,10 @@ class GenericHardwareManager(HardwareManager):
return Memory(total=total, physical_mb=physical)
def list_block_devices(self, include_partitions=False):
block_devices = list_all_block_devices()
def list_block_devices(self, include_partitions=False,
all_serial_and_wwn=False):
block_devices = \
list_all_block_devices(all_serial_and_wwn=all_serial_and_wwn)
if include_partitions:
block_devices.extend(
list_all_block_devices(block_type='part',
@ -1562,9 +1584,11 @@ class GenericHardwareManager(HardwareManager):
return skip_list
def list_block_devices_check_skip_list(self, node,
include_partitions=False):
include_partitions=False,
all_serial_and_wwn=False):
block_devices = self.list_block_devices(
include_partitions=include_partitions)
include_partitions=include_partitions,
all_serial_and_wwn=all_serial_and_wwn)
skip_list = self.get_skip_list_from_node(
node, block_devices)
if skip_list is not None:
@ -1587,13 +1611,29 @@ class GenericHardwareManager(HardwareManager):
LOG.debug('Looking for a device matching root hints %s',
root_device_hints)
block_devices = self.list_block_devices_check_skip_list(
cached_node)
cached_node, all_serial_and_wwn=True)
else:
block_devices = self.list_block_devices()
block_devices = self.list_block_devices(all_serial_and_wwn=True)
if not root_device_hints:
dev_name = utils.guess_root_disk(block_devices).name
else:
serialized_devs = [dev.serialize() for dev in block_devices]
orig_size = len(serialized_devs)
for dev_idx in range(orig_size):
ser_dev = serialized_devs.pop(0)
serials = ser_dev.get('serial')
wwns = ser_dev.get('wwn')
# (rozzi) static serial and static wwn are used to avoid
# reundancy in the number of wwns and serials, if the code
# would just loop over both serials and wwns it could be that
# there would be an uncesarry duplication of the first wwn
# number
for serial in serials:
for wwn in wwns:
tmp_ser_dev = ser_dev.copy()
tmp_ser_dev['wwn'] = wwn
tmp_ser_dev['serial'] = serial
serialized_devs.append(tmp_ser_dev)
try:
device = il_utils.match_root_device_hints(serialized_devs,
root_device_hints)

View File

@ -833,7 +833,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
mock.call('udevadm', 'settle'),
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,'
+ 'TYPE,UUID,PARTUUID,SERIAL',
+ 'TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
@ -956,7 +956,7 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
mock.call('udevadm', 'settle'),
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,'
+ 'TYPE,UUID,PARTUUID,SERIAL',
+ 'TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),

View File

@ -102,35 +102,42 @@ BLK_DEVICE_TEMPLATE = """
"blockdevices": [
{"kname":"sda", "model":"TinyUSB Drive", "size":3116853504,
"rota":false, "type":"disk", "serial":"sda123", "uuid":"F531-BDC3",
"partuuid":null},
"partuuid":null, "wwn":"wwn0"},
{"kname":"sdb", "model":"Fastable SD131 7", "size":10737418240,
"rota":false, "type":"disk", "serial":"sdb123",
"rota":false, "type":"disk", "serial":"sdb123", "wwn":"wwn1",
"uuid":"9a5e5cca-e03d-4cbd-9054-9e6ca9048222", "partuuid":null},
{"kname":"sdc", "model":"NWD-BLP4-1600", "size":1765517033472,
"rota":false, "type":"disk", "serial":"sdc123", "uuid":null,
"partuuid":null},
"partuuid":null, "wwn":"wwn2"},
{"kname":"sdd", "model":"NWD-BLP4-1600", "size":1765517033472,
"rota":false, "type":"disk", "serial":"sdd123", "uuid":null,
"partuuid":null},
"partuuid":null, "wwn":"wwn3"},
{"kname":"loop0", "model":null, "size":109109248, "rota":true,
"type":"loop", "serial":null, "uuid":null, "partuuid": null},
"type":"loop", "serial":null, "uuid":null, "partuuid": null,
"wwn":"wwn03"},
{"kname":"zram0", "model":null, "size":0, "rota":false, "type":"disk",
"serial":null, "uuid":null, "partuuid":null},
"serial":null, "uuid":null, "partuuid":null, "wwn":"wwn04"},
{"kname":"ram0", "model":null, "size":8388608, "rota":false,
"type":"disk", "serial":null, "uuid":null, "partuuid":null},
"type":"disk", "serial":null, "uuid":null, "partuuid":null, "wwn":null
},
{"kname":"ram1", "model":null, "size":8388608, "rota":false,
"type":"disk", "serial":null, "uuid":null, "partuuid":null},
"type":"disk", "serial":null, "uuid":null, "partuuid":null, "wwn":null
},
{"kname":"ram2", "model":null, "size":8388608, "rota":false,
"type":"disk", "serial":null, "uuid":null, "partuuid":null},
"type":"disk", "serial":null, "uuid":null, "partuuid":null,"wwn":null
},
{"kname":"ram3", "model":null, "size":8388608, "rota":false,
"type":"disk", "serial":null, "uuid":null, "partuuid":null},
"type":"disk", "serial":null, "uuid":null, "partuuid":null, "wwn":null
},
{"kname":"fd1", "model":"magic", "size":4096, "rota":true,
"type":"disk", "serial":null, "uuid":null, "partuuid":null},
"type":"disk", "serial":null, "uuid":null, "partuuid":null, "wwn":null
},
{"kname":"sdf", "model":"virtual floppy", "size":0, "rota":true,
"type":"disk", "serial":null, "uuid":null, "partuuid":null},
"type":"disk", "serial":null, "uuid":null, "partuuid":null, "wwn":null
},
{"kname":"dm-0", "model":"NWD-BLP4-1600", "size":"1765517033472",
"rota":false, "type":"mpath", "serial":null, "uuid":null,
"partuuid":null}
"partuuid":null, "wwn":null }
]
}
"""
@ -140,9 +147,11 @@ BLK_DEVICE_TEMPLATE_SMALL = """
{
"blockdevices": [
{"kname":"sda", "model":"TinyUSB Drive", "size":3116853504, "rota":false,
"type":"disk", "serial":"123", "uuid":"F531-BDC", "partuuid":null},
"type":"disk", "serial":"123", "uuid":"F531-BDC", "partuuid":null,
"wwn":"wwn0" },
{"kname":"sdb", "model":"AlmostBigEnough Drive", "size":"4294967295",
"rota":false, "type":"disk", "serial":"456", "uuid":null, "partuuid":null}
"rota":false, "type":"disk", "serial":"456", "uuid":null, "partuuid":null,
"wwn":"wwn1" }
]
}
"""
@ -153,9 +162,10 @@ BLK_INCOMPLETE_DEVICE_TEMPLATE_SMALL = """
{
"blockdevices": [
{"kname":"sda", "model":"TinyUSB Drive", "size":3116853504, "rota":false,
"type":"disk", "serial":"", "uuid":"F531-BDC", "partuuid":null},
"type":"disk", "serial":"", "uuid":"F531-BDC", "partuuid":null, "wwn":""},
{"kname":"sdb", "model":"AlmostBigEnough Drive", "size":"4294967295",
"rota":false, "type":"disk", "serial":"", "uuid":null, "partuuid":null}
"rota":false, "type":"disk", "serial":"", "uuid":null, "partuuid":null,
"wwn":null }
]
}
"""
@ -171,23 +181,27 @@ RAID_BLK_DEVICE_TEMPLATE = ("""
{
"blockdevices": [
{"kname":"sda", "model":"DRIVE 0", "size":1765517033472, "rota":true,
"type":"disk", "serial":"sda123", "uuid":null, "partuuid":null},
"type":"disk", "serial":"sda123", "uuid":null, "partuuid":null,
"wwn":"wwn1234" },
{"kname":"sda1", "model":"DRIVE 0", "size":107373133824, "rota":true,
"type":"part", "serial":"sda1123", "uuid":null, "partuuid":null},
"type":"part", "serial":"sda1123", "uuid":null, "partuuid":null,
"wwn":"wwn2222"},
{"kname":"sdb", "model":"DRIVE 1", "size":1765517033472, "rota":true,
"type":"disk", "serial":"sdb123", "uuid":null, "partuuid":null},
"type":"disk", "serial":"sdb123", "uuid":null, "partuuid":null,
"wwn":"wwn333"},
{"kname":"sdb", "model":"DRIVE 1", "size":1765517033472, "rota":true,
"type":"disk", "uuid":null, "partuuid":null},
"type":"disk", "uuid":null, "partuuid":null, "wwn":"444"},
{"kname":"sdb1", "model":"DRIVE 1", "size":107373133824, "rota":true,
"type":"part", "serial":"sdb1123", "uuid":null, "partuuid":null},
"type":"part", "serial":"sdb1123", "uuid":null, "partuuid":null,
"wwn":"wwn5"},
{"kname":"md0p1", "model":"RAID", "size":107236818944, "rota":false,
"type":"md", "serial":null, "uuid":null, "partuuid":null},
"type":"md", "serial":null, "uuid":null, "partuuid":null, "wwn":"wwn6"},
{"kname":"md0", "model":"RAID", "size":1765517033470, "rota":false,
"type":"raid1", "serial":null, "uuid":null, "partuuid":null},
"type":"raid1", "serial":null, "uuid":null, "partuuid":null, "wwn":"12"},
{"kname":"md0", "model":"RAID", "size":1765517033470, "rota":false,
"type":"raid1", "serial":null, "uuid":null, "partuuid":null},
"type":"raid1", "serial":null, "uuid":null, "partuuid":null, "wwn":"33"},
{"kname":"md1", "model":"RAID", "size":0, "rota":false, "type":"raid1",
"serial":null, "uuid":null, "partuuid":null}
"serial":null, "uuid":null, "partuuid":null, "wwn":null}
]
}
""")
@ -197,51 +211,59 @@ MULTIPATH_BLK_DEVICE_TEMPLATE = ("""
"blockdevices": [
{"kname":"sda", "model":"INTEL_SSDSC2CT060A3", "size":"60022480896",
"rota":false, "type":"disk", "serial":"sda123", "uuid":null,
"partuuid":null},
"partuuid":null, "wwn":null },
{"kname":"sda2", "model":null, "size":"59162722304", "rota":false,
"type":"part", "uuid":"f8b55d59-96c3-3982-b129-1b6b2ee8da86",
"partuuid":"c97c8aac-7796-4433-b1fc-9b5fac43edf3", "serial":"sda2123"},
"partuuid":"c97c8aac-7796-4433-b1fc-9b5fac43edf3", "serial":"sda2123",
"wwn":"" },
{"kname":"sda3", "model":null, "size":"650002432", "rota":false,
"type":"part", "uuid":"b3b03565-5f13-3c93-b2a6-6d90e25be926",
"partuuid":"6c85beff-b2bd-4a1c-91b7-8abb5256459d", "serial":"sda3123"},
"partuuid":"6c85beff-b2bd-4a1c-91b7-8abb5256459d", "serial":"sda3123",
"wwn":"wwn1" },
{"kname":"sda1", "model":null, "size":"209715200", "rota":false,
"type":"part", "uuid":"0a83355d-7500-3f5f-9abd-66f6fd03714c",
"partuuid":"eba28b26-b76a-402c-94dd-0b66a523a485", "serial":"sda1123"},
"partuuid":"eba28b26-b76a-402c-94dd-0b66a523a485", "serial":"sda1123",
"wwn":"123" },
{"kname":"dm-0", "model":null, "size":"60022480896", "rota":false,
"type":"mpath", "serial":null, "uuid":null, "partuuid":null},
"type":"mpath", "serial":null, "uuid":null, "partuuid":null,
"wwn":"123aa" },
{"kname":"dm-4", "model":null, "size":"650002432", "rota":false,
"type":"part", "uuid":"b3b03565-5f13-3c93-b2a6-6d90e25be926",
"partuuid":"6c85beff-b2bd-4a1c-91b7-8abb5256459d", "serial":null},
"partuuid":"6c85beff-b2bd-4a1c-91b7-8abb5256459d", "serial":null,
"wwn":"123bb" },
{"kname":"dm-2", "model":null, "size":"209715200", "rota":false,
"type":"part", "uuid":"0a83355d-7500-3f5f-9abd-66f6fd03714c",
"partuuid":"eba28b26-b76a-402c-94dd-0b66a523a485", "serial":null},
"partuuid":"eba28b26-b76a-402c-94dd-0b66a523a485", "serial":null,
"wwn":"123cc" },
{"kname":"dm-3", "model":null, "size":"59162722304", "rota":false,
"type":"part", "uuid":"f8b55d59-96c3-3982-b129-1b6b2ee8da86",
"partuuid":"c97c8aac-7796-4433-b1fc-9b5fac43edf3", "serial":null},
"partuuid":"c97c8aac-7796-4433-b1fc-9b5fac43edf3", "serial":null,
"wwn":"123dd" },
{"kname":"sdb", "model":"INTEL_SSDSC2CT060A3", "size":"60022480896",
"rota":false, "type":"disk", "serial":"sdb123", "uuid":null,
"partuuid":null},
"partuuid":null, "wwn":"123ee" },
{"kname":"sdb2", "model":null, "size":"59162722304",
"rota":false, "type":"part", "serial":"sdb2123",
"uuid":"f8b55d59-96c3-3982-b129-1b6b2ee8da86",
"uuid":"f8b55d59-96c3-3982-b129-1b6b2ee8da86", "wwn":"123gg",
"partuuid":"c97c8aac-7796-4433-b1fc-9b5fac43edf3"},
{"kname":"sdb3", "model":null, "size":"650002432",
"rota":false, "type":"part", "serial":"sdv3123",
"uuid":"b3b03565-5f13-3c93-b2a6-6d90e25be926",
"uuid":"b3b03565-5f13-3c93-b2a6-6d90e25be926", "wwn":"123zz",
"partuuid":"6c85beff-b2bd-4a1c-91b7-8abb5256459d"},
{"kname":"sdb1", "model":null, "size":"209715200",
"rota":false, "type":"part", "serial":"sdb1123",
"uuid":"0a83355d-7500-3f5f-9abd-66f6fd03714c",
"uuid":"0a83355d-7500-3f5f-9abd-66f6fd03714c", "wwn":"123ll",
"partuuid":"eba28b26-b76a-402c-94dd-0b66a523a485"},
{"kname":"sdc", "model":"ST1000DM003-1CH162", "size":"1000204886016",
"rota":true, "type":"disk", "serial":"sdc123", "uuid":null,
"rota":true, "type":"disk", "serial":"sdc123", "uuid":null, "wwn":"123g",
"partuuid":null},
{"kname":"sdc1", "model":null, "size":"899999072256",
"rota":true, "type":"part", "serial":"sdc1123",
"uuid":"457f7d3c-9376-4997-89bd-d1a7c8b04060",
"uuid":"457f7d3c-9376-4997-89bd-d1a7c8b04060", "wwn":"123kc",
"partuuid":"c9433d2e-3bbc-47b4-92bf-43c1d80f06e0"},
{"kname":"dm-1", "model":null, "size":"1000204886016", "rota":false,
"type":"mpath", "serial":null, "uuid":null, "partuuid":null}
"type":"mpath", "serial":null, "uuid":null, "partuuid":null,
"wwn":"sp0ng3b0b" }
]
}
""")
@ -250,9 +272,10 @@ PARTUUID_DEVICE_TEMPLATE = ("""
{
"blockdevices": [
{"kname":"sda", "model":"DRIVE 0", "size":1765517033472, "rota":true,
"type":"disk", "serial":"sda123", "uuid":null, "partuuid":null},
"type":"disk", "serial":"sda123", "uuid":null, "partuuid":null,
"wwn":"4d4m" },
{"kname":"sda1", "model":"DRIVE 0", "size":107373133824, "rota":true,
"type":"part", "serial":"sda1123", "uuid":"987654-3210",
"type":"part", "serial":"sda1123", "uuid":"987654-3210", "wwn":"k4k1",
"partuuid":"1234-5678"}
]
}

View File

@ -47,37 +47,37 @@ BLK_DEVICE_TEMPLATE_SMALL_DEVICES = [
hardware.BlockDevice(name='/dev/sda', model='TinyUSB Drive',
size=3116853504, rotational=False,
vendor="FooTastic", uuid="F531-BDC3",
serial="123"),
serial="123", wwn="wwn0"),
hardware.BlockDevice(name='/dev/sdb', model='AlmostBigEnough Drive',
size=4294967295, rotational=False,
vendor="FooTastic", uuid="",
serial="456"),
serial="456", wwn="wwn1"),
]
RAID_BLK_DEVICE_TEMPLATE_DEVICES = [
hardware.BlockDevice(name='/dev/sda', model='DRIVE 0',
size=1765517033472, rotational=True,
vendor="FooTastic", uuid="",
serial="sda123"),
serial="sda123", wwn="wwn1234"),
hardware.BlockDevice(name='/dev/sdb', model='DRIVE 1',
size=1765517033472, rotational=True,
vendor="FooTastic", uuid="",
serial="sdb123"),
serial="sdb123", wwn="wwn333"),
hardware.BlockDevice(name='/dev/md0', model='RAID',
size=1765517033470, rotational=False,
vendor="FooTastic", uuid="",
serial=None),
serial=None, wwn="12"),
hardware.BlockDevice(name='/dev/md1', model='RAID',
size=0, rotational=False,
vendor="FooTastic", uuid="",
serial=None),
serial=None, wwn=None),
]
BLK_DEVICE_TEMPLATE_PARTUUID_DEVICE = [
hardware.BlockDevice(name='/dev/sda1', model='DRIVE 0',
size=107373133824, rotational=True,
vendor="FooTastic", uuid="987654-3210",
partuuid="1234-5678", serial="sda1123"),
partuuid="1234-5678", serial="sda1123", wwn="k4k1"),
]
@ -396,7 +396,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
]
expected = [
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
mock.call('multipath', '-c', '/dev/sda'),
mock.call('multipath', '-ll', '/dev/sda'),
@ -476,7 +476,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
]
expected = [
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
mock.call('multipath', '-c', '/dev/sda'),
mock.call('multipath', '-ll', '/dev/sda'),
@ -531,7 +531,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual('/dev/md0', self.hardware.get_os_install_device())
expected = [
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
]
@ -557,14 +557,14 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.hardware.get_os_install_device)
expected = [
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
]
mocked_execute.assert_has_calls(expected)
mocked_execute.assert_called_once_with(
'lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0])
self.assertIn(str(4 * units.Gi), ex.details)
mock_cached_node.assert_called_once_with()
@ -584,26 +584,26 @@ class TestGenericHardwareManager(base.IronicAgentTest):
size=3116853504,
rotational=False,
vendor='Super Vendor',
wwn='wwn0',
wwn=['strangewwn', 'wwn0'],
wwn_with_extension='wwn0ven0',
wwn_vendor_extension='ven0',
serial='serial0'),
serial=['wongserial', 'wrng0', 'serial0']),
hardware.BlockDevice(name='/dev/sdb',
model=model,
size=10737418240,
rotational=True,
vendor='fake-vendor',
wwn='fake-wwn',
wwn=['fake-wwn'],
wwn_with_extension='fake-wwnven0',
wwn_vendor_extension='ven0',
serial='fake-serial',
serial=['fake-serial', 'serial1'],
by_path='/dev/disk/by-path/1:0:0:0'),
]
self.assertEqual(expected_device,
self.hardware.get_os_install_device())
mock_cached_node.assert_called_once_with()
mock_dev.assert_called_once_with()
mock_dev.assert_called_once_with(all_serial_and_wwn=True)
def test_get_os_install_device_root_device_hints_model(self):
self._get_os_install_device_root_device_hints(
@ -681,7 +681,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertRaises(errors.DeviceNotFound,
self.hardware.get_os_install_device)
mock_cached_node.assert_called_once_with()
mock_dev.assert_called_once_with()
mock_dev.assert_called_once_with(all_serial_and_wwn=True)
@mock.patch.object(hardware, 'list_all_block_devices', autospec=True)
@mock.patch.object(hardware, 'get_cached_node', autospec=True)
@ -717,7 +717,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual('/dev/sdb', self.hardware.get_os_install_device())
mock_cached_node.assert_called_once_with()
mock_dev.assert_called_once_with()
mock_dev.assert_called_once_with(all_serial_and_wwn=True)
@mock.patch.object(hardware, 'update_cached_node', autospec=True)
@mock.patch.object(hardware, 'list_all_block_devices', autospec=True)
@ -757,7 +757,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.hardware.get_os_install_device(
permit_refresh=True))
self.assertEqual(1, mock_cached_node.call_count)
mock_dev.assert_called_once_with()
mock_dev.assert_called_once_with(all_serial_and_wwn=True)
@mock.patch.object(hardware, 'list_all_block_devices', autospec=True)
@mock.patch.object(hardware, 'get_cached_node', autospec=True)
@ -791,7 +791,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual('/dev/sdb',
self.hardware.get_os_install_device())
mock_cached_node.assert_called_once_with()
mock_dev.assert_called_once_with()
mock_dev.assert_called_once_with(all_serial_and_wwn=True)
@mock.patch.object(hardware, 'list_all_block_devices', autospec=True)
@mock.patch.object(hardware, 'get_cached_node', autospec=True)
@ -822,7 +822,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertRaises(errors.DeviceNotFound,
self.hardware.get_os_install_device)
mock_cached_node.assert_called_once_with()
mock_dev.assert_called_once_with()
mock_dev.assert_called_once_with(all_serial_and_wwn=True)
@mock.patch.object(hardware, 'list_all_block_devices', autospec=True)
@mock.patch.object(hardware, 'get_cached_node', autospec=True)
@ -854,7 +854,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertRaises(errors.DeviceNotFound,
self.hardware.get_os_install_device)
mock_cached_node.assert_called_once_with()
mock_dev.assert_called_once_with()
mock_dev.assert_called_once_with(all_serial_and_wwn=True)
def test__get_device_info(self):
fileobj = mock.mock_open(read_data='fake-vendor')
@ -1060,7 +1060,7 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual([device], devices)
list_mock.assert_called_once_with()
list_mock.assert_called_once_with(all_serial_and_wwn=False)
@mock.patch.object(hardware, 'list_all_block_devices', autospec=True)
def test_list_block_devices_including_partitions(self, list_mock):
@ -1071,8 +1071,8 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual([device, partition], devices)
self.assertEqual([mock.call(), mock.call(block_type='part',
ignore_raid=True)],
self.assertEqual([mock.call(all_serial_and_wwn=False),
mock.call(block_type='part', ignore_raid=True)],
list_mock.call_args_list)
def test_get_skip_list_from_node_block_devices_with_skip_list(self):
@ -1146,7 +1146,8 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual([device], returned_devices)
mock_list_devs.assert_called_once_with(self.hardware,
include_partitions=False)
include_partitions=False,
all_serial_and_wwn=False)
@mock.patch.object(hardware.GenericHardwareManager,
'list_block_devices', autospec=True)
@ -1165,7 +1166,8 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual(devices, returned_devices)
mock_list_devs.assert_called_once_with(self.hardware,
include_partitions=False)
include_partitions=False,
all_serial_and_wwn=False)
@mock.patch.object(hardware.GenericHardwareManager,
'list_block_devices', autospec=True)
@ -1192,7 +1194,8 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual(devices, returned_devices)
mock_list_devs.assert_called_once_with(self.hardware,
include_partitions=False)
include_partitions=False,
all_serial_and_wwn=False)
@mock.patch.object(hardware.GenericHardwareManager,
'list_block_devices', autospec=True)
@ -1219,7 +1222,8 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual([], returned_devices)
mock_list_devs.assert_called_once_with(self.hardware,
include_partitions=False)
include_partitions=False,
all_serial_and_wwn=False)
@mock.patch.object(hardware, 'get_multipath_status', lambda *_: True)
@mock.patch.object(os, 'readlink', autospec=True)
@ -1282,7 +1286,8 @@ class TestGenericHardwareManager(base.IronicAgentTest):
vendor='Super Vendor',
hctl='1:0:0:0',
by_path='/dev/disk/by-path/1:0:0:0',
serial='sda123'),
serial='sda123',
wwn='wwn0'),
hardware.BlockDevice(name='/dev/sdb',
model='Fastable SD131 7',
size=10737418240,
@ -1290,7 +1295,8 @@ class TestGenericHardwareManager(base.IronicAgentTest):
vendor='Super Vendor',
hctl='1:0:0:0',
by_path='/dev/disk/by-path/1:0:0:1',
serial='sdb123'),
serial='sdb123',
wwn='wwn1'),
hardware.BlockDevice(name='/dev/sdc',
model='NWD-BLP4-1600',
size=1765517033472,
@ -1298,13 +1304,15 @@ class TestGenericHardwareManager(base.IronicAgentTest):
vendor='Super Vendor',
hctl='1:0:0:0',
by_path='/dev/disk/by-path/1:0:0:2',
serial='sdc123'),
serial='sdc123',
wwn='wwn2'),
hardware.BlockDevice(name='/dev/dm-0',
model='NWD-BLP4-1600',
size=1765517033472,
rotational=False,
vendor='Super Vendor',
hctl='1:0:0:0'),
hctl='1:0:0:0',
wwn=None),
]
self.assertEqual(4, len(devices))
@ -1323,7 +1331,139 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock_readlink.assert_has_calls(expected_calls)
expected_calls = [
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
mock.call('multipath', '-c', '/dev/sda'),
mock.call('multipath', '-c', '/dev/sdb'),
mock.call('multipath', '-c', '/dev/sdc'),
mock.call('multipath', '-c', '/dev/sdd'),
mock.call('multipath', '-ll', '/dev/sdd'),
mock.call('multipath', '-c', '/dev/loop0'),
mock.call('multipath', '-c', '/dev/zram0'),
mock.call('multipath', '-c', '/dev/ram0'),
mock.call('multipath', '-c', '/dev/ram1'),
mock.call('multipath', '-c', '/dev/ram2'),
mock.call('multipath', '-c', '/dev/ram3'),
mock.call('multipath', '-c', '/dev/sdf'),
mock.call('multipath', '-c', '/dev/dm-0')
]
mocked_execute.assert_has_calls(expected_calls)
@mock.patch.object(hardware, 'get_multipath_status', lambda *_: True)
@mock.patch.object(os, 'readlink', autospec=True)
@mock.patch.object(os, 'listdir', autospec=True)
@mock.patch.object(hardware, '_get_device_info', autospec=True)
@mock.patch.object(pyudev.Devices, 'from_device_file', autospec=False)
@mock.patch.object(il_utils, 'execute', autospec=True)
def test_list_all_block_device_all_serial(self, mocked_execute,
mocked_udev, mocked_dev_vendor,
mock_listdir, mock_readlink):
by_path_map = {
'/dev/disk/by-path/1:0:0:0': '../../dev/sda',
'/dev/disk/by-path/1:0:0:1': '../../dev/sdb',
'/dev/disk/by-path/1:0:0:2': '../../dev/sdc',
# pretend that the by-path link to ../../dev/sdd is missing
}
mock_readlink.side_effect = lambda x, m=by_path_map: m[x]
mock_listdir.return_value = [os.path.basename(x)
for x in sorted(by_path_map)]
mocked_execute.side_effect = [
(hws.BLK_DEVICE_TEMPLATE, ''),
processutils.ProcessExecutionError(
stderr=hws.MULTIPATH_INVALID_PATH % '/dev/sda'),
processutils.ProcessExecutionError(
stderr=hws.MULTIPATH_INVALID_PATH % '/dev/sdb'),
processutils.ProcessExecutionError(
stderr=hws.MULTIPATH_INVALID_PATH % '/dev/sdc'),
# Pretend sdd is a multipath device... because why not.
(hws.MULTIPATH_VALID_PATH % '/dev/sdd', ''),
(hws.MULTIPATH_LINKS_DM % 'dm-0', ''),
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # loop0
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # zram0
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # ram0
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # ram1
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # ram2
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # ram3
processutils.ProcessExecutionError(
stderr=hws.MULTIPATH_INVALID_PATH % '/dev/sdf'),
processutils.ProcessExecutionError(
stderr='the -c option requires a path to check'), # dm-0
]
mocked_udev.side_effect = [
{'ID_WWN': 'badwwn%d' % i, 'ID_SERIAL_SHORT': 'badserial%d' % i,
'ID_SERIAL': 'longserial%d' % i,
'ID_WWN_WITH_EXTENSION': 'wwn-ext%d' % i,
'ID_WWN_VENDOR_EXTENSION': 'wwn-vendor-ext%d' % i}
for i in range(3)
] + [
{'DM_WWN': 'wwn3', 'DM_SERIAL': 'serial3'}
]
mocked_dev_vendor.return_value = 'Super Vendor'
devices = hardware.list_all_block_devices(all_serial_and_wwn=True)
expected_devices = [
hardware.BlockDevice(name='/dev/sda',
model='TinyUSB Drive',
size=3116853504,
rotational=False,
vendor='Super Vendor',
hctl='1:0:0:0',
by_path='/dev/disk/by-path/1:0:0:0',
serial=['sda123', 'badserial0',
'longserial0'],
wwn=['wwn0', 'badwwn0']),
hardware.BlockDevice(name='/dev/sdb',
model='Fastable SD131 7',
size=10737418240,
rotational=False,
vendor='Super Vendor',
hctl='1:0:0:0',
by_path='/dev/disk/by-path/1:0:0:1',
serial=['sdb123', 'badserial1',
'longserial1'],
wwn=['wwn1', 'badwwn1']),
hardware.BlockDevice(name='/dev/sdc',
model='NWD-BLP4-1600',
size=1765517033472,
rotational=False,
vendor='Super Vendor',
hctl='1:0:0:0',
by_path='/dev/disk/by-path/1:0:0:2',
serial=['sdc123', 'badserial2',
'longserial2'],
wwn=['wwn2', 'badwwn2']),
hardware.BlockDevice(name='/dev/dm-0',
model='NWD-BLP4-1600',
size=1765517033472,
rotational=False,
vendor='Super Vendor',
hctl='1:0:0:0',
wwn=[None, 'wwn3'],
serial=[None, None, 'serial3']),
]
self.assertEqual(4, len(devices))
for expected, device in zip(expected_devices, devices):
# Compare all attrs of the objects
for attr in ['name', 'model', 'size', 'rotational',
'wwn', 'vendor', 'serial', 'hctl']:
self.assertEqual(getattr(expected, attr),
getattr(device, attr))
expected_calls = [mock.call('/sys/block/%s/device/scsi_device' % dev)
for dev in ('sda', 'sdb', 'sdc', 'dm-0')]
mock_listdir.assert_has_calls(expected_calls)
expected_calls = [mock.call('/dev/disk/by-path/1:0:0:%d' % dev)
for dev in range(3)]
mock_readlink.assert_has_calls(expected_calls)
expected_calls = [
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
mock.call('multipath', '-c', '/dev/sda'),
mock.call('multipath', '-c', '/dev/sdb'),
@ -2530,7 +2670,8 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('/dev/md0', self.node['uuid'])],
mock_metadata.call_args_list)
mock_list_devs.assert_called_with(self.hardware,
include_partitions=True)
include_partitions=True,
all_serial_and_wwn=False)
self.assertEqual([mock.call(self.hardware, block_devices[0]),
mock.call(self.hardware, block_devices[1]),
mock.call(self.hardware, block_devices[4]),
@ -2589,7 +2730,8 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual([mock.call('/dev/sda1', self.node['uuid'])],
mock_metadata.call_args_list)
mock_list_devs.assert_called_with(self.hardware,
include_partitions=True)
include_partitions=True,
all_serial_and_wwn=False)
mock_safety_check.assert_has_calls([
mock.call(self.node, '/dev/sda1'),
mock.call(self.node, '/dev/sda'),
@ -2638,7 +2780,8 @@ class TestGenericHardwareManager(base.IronicAgentTest):
mock.call('/dev/sda', self.node['uuid'])],
mock_metadata.call_args_list)
mock_list_devs.assert_called_with(self.hardware,
include_partitions=True)
include_partitions=True,
all_serial_and_wwn=False)
self.assertEqual([mock.call(self.hardware, block_devices[1]),
mock.call(self.hardware, block_devices[0])],
mock__is_vmedia.call_args_list)
@ -5185,7 +5328,7 @@ class TestModuleFunctions(base.IronicAgentTest):
result = hardware.list_all_block_devices()
expected_calls = [
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
mock.call('multipath', '-c', '/dev/sda'),
mock.call('multipath', '-c', '/dev/sdb')
@ -5232,7 +5375,7 @@ class TestModuleFunctions(base.IronicAgentTest):
]
expected_calls = [
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
mock.call('multipath', '-c', '/dev/sda'),
mock.call('multipath', '-c', '/dev/sda1'),
@ -5271,7 +5414,7 @@ class TestModuleFunctions(base.IronicAgentTest):
result = hardware.list_all_block_devices(block_type='part')
expected_calls = [
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
mock.call('multipath', '-c', '/dev/sda'),
mock.call('multipath', '-c', '/dev/sda1'),
@ -5294,7 +5437,7 @@ class TestModuleFunctions(base.IronicAgentTest):
result = hardware.list_all_block_devices()
mocked_execute.assert_called_once_with(
'lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0])
self.assertEqual([], result)
mocked_udev.assert_called_once_with()
@ -5308,15 +5451,15 @@ class TestModuleFunctions(base.IronicAgentTest):
mocked_mpath.return_value = False
expected_calls = [
mock.call('lsblk', '-bia', '--json',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL',
'-oKNAME,MODEL,SIZE,ROTA,TYPE,UUID,PARTUUID,SERIAL,WWN',
check_exit_code=[0]),
]
mocked_execute.return_value = (
'{"blockdevices": [{"type":"disk", "model":"model"}]}', '')
self.assertRaisesRegex(
errors.BlockDeviceError,
r'^Block device caused unknown error: kname, partuuid, rota, '
r'serial, size, uuid must be returned by lsblk.$',
r'Block device caused unknown error: kname, partuuid, rota, '
r'serial, size, uuid, wwn must be returned by lsblk.',
hardware.list_all_block_devices)
mocked_udev.assert_called_once_with()
mocked_execute.assert_has_calls(expected_calls)

View File

@ -59,7 +59,7 @@ AGENT_PARAMS_CACHED = dict()
LSBLK_COLUMNS = ['KNAME', 'MODEL', 'SIZE', 'ROTA',
'TYPE', 'UUID', 'PARTUUID', 'SERIAL']
'TYPE', 'UUID', 'PARTUUID', 'SERIAL', 'WWN']
DEVICE_EXTRACTOR = re.compile(r'^(?:(.*\d)p|(.*\D))(?:\d+)$')

View File

@ -0,0 +1,10 @@
---
features:
- |
For a long time block device information originating form lsblk and udev
was handled in a mutually exclusive way during root disk selection.
The new ``mix and match`` approach allows IPA to collect and match
``disk serial`` and ``wwn`` root device hints against values coming
from both ``lsblk`` and ``udev`` at the same time. The ``mix and match``
approach is necesarry to handle edge cases where the serial and/or wwn
information is different in ``lsblk`` compared to ``udev``.