From 352df0bc54c56f6a1ea2e3efc7c3db4b882d9f03 Mon Sep 17 00:00:00 2001 From: Steve Baker Date: Mon, 6 Nov 2023 15:29:13 +1300 Subject: [PATCH] Parse efibootmgr type and details This change improves the regex to match an exact entry name, and to also match with the the entry type from a set of recognised types. The boot entry details start from the recognised type onwards. This can be used by a step which deletes all entries of type 'HW' and UsbClass. Related-Bug: #2041901 Change-Id: I5d879f724efc2919b541fd3fef0f931df67ff9c7 --- ironic_python_agent/efi_utils.py | 13 +-- .../tests/unit/test_efi_utils.py | 90 +++++++++++-------- 2 files changed, 62 insertions(+), 41 deletions(-) diff --git a/ironic_python_agent/efi_utils.py b/ironic_python_agent/efi_utils.py index 48708f22c..077f7f86b 100644 --- a/ironic_python_agent/efi_utils.py +++ b/ironic_python_agent/efi_utils.py @@ -267,13 +267,16 @@ def _get_efi_bootloaders(location): # NOTE(TheJulia): regex used to identify entries in the efibootmgr # output on stdout. -_ENTRY_LABEL = re.compile(r'Boot([0-9a-f-A-F]+)\*?\s(.*).*$') +_ENTRY_LABEL = re.compile( + r'Boot([0-9a-f-A-F]+)\*?\s+(.*?)\s+' + r'((BBS|HD|FvFile|FvVol|PciRoot|VenMsg|VenHw|UsbClass)\(.*)$') def get_boot_records(): """Executes efibootmgr and returns boot records. - :return: an iterator yielding pairs (boot number, boot record). + :return: An iterator yielding tuples + (boot number, boot record, root device type, device path). """ # Invokes binary=True so we get a bytestream back. efi_output = utils.execute('efibootmgr', '-v', binary=True) @@ -286,7 +289,7 @@ def get_boot_records(): for line in cmd_output.split('\n'): match = _ENTRY_LABEL.match(line) if match is not None: - yield (match[1], match[2]) + yield (match[1], match[2], match[4], match[3]) def add_boot_record(device, efi_partition, loader, label): @@ -358,11 +361,11 @@ def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition, label = label + " " + str(label_suffix) # Iterate through standard out, and look for duplicates - for boot_num, boot_rec in boot_records: + for boot_num, boot_rec, boot_type, boot_details in boot_records: # Look for the base label in the string if a line match # occurs, so we can identify if we need to eliminate the # entry. - if label in boot_rec: + if label == boot_rec: LOG.debug("Found bootnum %s matching label", boot_num) remove_boot_record(boot_num) diff --git a/ironic_python_agent/tests/unit/test_efi_utils.py b/ironic_python_agent/tests/unit/test_efi_utils.py index e84392223..e78c0fd5a 100644 --- a/ironic_python_agent/tests/unit/test_efi_utils.py +++ b/ironic_python_agent/tests/unit/test_efi_utils.py @@ -211,9 +211,9 @@ class TestManageUefi(base.IronicAgentTest): BootCurrent: 0001 Timeout: 0 seconds BootOrder: 0001,0000,001B,001C,001D,001E,001F,0020,0021,0022,0012,0011,0023,0024,0002 -Boot0000* Red Hat Enterprise Linux HD(1,GPT,34178504-2340-4fe0-8001-264372cf9b2d,0x800,0x64000)/File(\EFI\redhat\shimx64.efi) -Boot0001* Fedora HD(1,GPT,da6b4491-61f2-42b0-8ab1-7c4a87317c4e,0x800,0x64000)/File(\EFI\fedora\shimx64.efi) -Boot0002* Linux-Firmware-Updater HD(1,GPT,da6b4491-61f2-42b0-8ab1-7c4a87317c4e,0x800,0x64000)/File(\EFI\fedora\fwupdx64.efi) +Boot0000* Red Hat Enterprise Linux HD(1,GPT,34178504-2340-4fe0-8001-264372cf9b2d,0x800,0x64000)/File(\\EFI\\redhat\\shimx64.efi) +Boot0001* Fedora HD(1,GPT,da6b4491-61f2-42b0-8ab1-7c4a87317c4e,0x800,0x64000)/File(\\EFI\\fedora\\shimx64.efi) +Boot0002* Linux-Firmware-Updater HD(1,GPT,da6b4491-61f2-42b0-8ab1-7c4a87317c4e,0x800,0x64000)/File(\\EFI\\fedora\\fwupdx64.efi) Boot0003 ThinkShield secure wipe FvFile(3593a0d5-bd52-43a0-808e-cbff5ece2477) Boot0004 LENOVO CLOUD VenMsg(bc7838d2-0f82-4d60-8316-c068ee79d25b,ad38ccbbf7edf04d959cf42aa74d3650)/Uri(https://download.lenovo.com/pccbbs/cdeploy/efi/boot.efi) Boot0005 IDER BOOT CDROM PciRoot(0x0)/Pci(0x14,0x0)/USB(11,1) @@ -229,53 +229,71 @@ Boot0009* Internal CD/DVD ROM Drive (UEFI) PciRoot(0x0)/Pci(0x11,0x0)/Sata( mock_execute.return_value = (efibootmgr_resp, '') result = list(efi_utils.get_boot_records()) - def assertEntry(expected, actual): - self.assertEqual(2, len(actual)) - self.assertEqual(expected[0], actual[0]) - self.assertIn(expected[1], actual[1]) - - assertEntry( - ('0000', 'Red Hat Enterprise Linux'), + self.assertEqual( + ('0000', 'Red Hat Enterprise Linux', 'HD', + 'HD(1,GPT,34178504-2340-4fe0-8001-264372cf9b2d,0x800,0x64000)/' + 'File(\\EFI\\redhat\\shimx64.efi)'), result[0]) - assertEntry( - ('0001', 'Fedora'), + self.assertEqual( + ('0001', 'Fedora', 'HD', + 'HD(1,GPT,da6b4491-61f2-42b0-8ab1-7c4a87317c4e,0x800,0x64000)/' + 'File(\\EFI\\fedora\\shimx64.efi)'), result[1]) - assertEntry( - ('0002', 'Linux-Firmware-Updater'), + self.assertEqual( + ('0002', 'Linux-Firmware-Updater', 'HD', + 'HD(1,GPT,da6b4491-61f2-42b0-8ab1-7c4a87317c4e,0x800,0x64000)/' + 'File(\\EFI\\fedora\\fwupdx64.efi)'), result[2]) - assertEntry( - ('0003', 'ThinkShield secure wipe'), + self.assertEqual( + ('0003', 'ThinkShield secure wipe', 'FvFile', + 'FvFile(3593a0d5-bd52-43a0-808e-cbff5ece2477)'), result[3]) - assertEntry( - ('0004', 'LENOVO CLOUD'), + self.assertEqual( + ('0004', 'LENOVO CLOUD', 'VenMsg', + 'VenMsg(bc7838d2-0f82-4d60-8316-c068ee79d25b,' + 'ad38ccbbf7edf04d959cf42aa74d3650)/' + 'Uri(https://download.lenovo.com/pccbbs/cdeploy/efi/boot.efi)'), result[4]) - assertEntry( - ('0005', 'IDER BOOT CDROM'), + self.assertEqual( + ('0005', 'IDER BOOT CDROM', 'PciRoot', + 'PciRoot(0x0)/Pci(0x14,0x0)/USB(11,1)'), result[5]) - assertEntry( - ('0006', 'ATA HDD'), + self.assertEqual( + ('0006', 'ATA HDD', 'VenMsg', + 'VenMsg(bc7838d2-0f82-4d60-8316-c068ee79d25b,' + '91af625956449f41a7b91f4f892ab0f6)'), result[6]) - assertEntry( - ('0007', 'Hard drive C:'), + self.assertEqual( + ('0007', 'Hard drive C:', 'VenHw', + mock.ANY), result[7]) - assertEntry( - ('AAA8', 'IBA GE Slot 0100 v1588'), + self.assertEqual( + ('AAA8', 'IBA GE Slot 0100 v1588', 'BBS', + mock.ANY), result[8]) - assertEntry( - ('0FF9', 'Virtual CD/DVD'), + self.assertEqual( + ('0FF9', 'Virtual CD/DVD', 'PciRoot', + 'PciRoot(0x0)/Pci(0x14,0x0)/USB(13,0)/USB(3,0)/USB(1,0)'), result[9]) - assertEntry( - ('123A', 'Integrated NIC 1 Port 1 Partition 1'), + self.assertEqual( + ('123A', 'Integrated NIC 1 Port 1 Partition 1', 'VenHw', + 'VenHw(33391845-5f86-4e78-8fce-c4cff59f9bbb)'), result[10]) - assertEntry( + self.assertEqual( ('000B', - 'UEFI: PXE IPv4 Realtek PCIe 2.5GBE Family Controller'), + 'UEFI: PXE IPv4 Realtek PCIe 2.5GBE Family Controller', + 'PciRoot', + 'PciRoot(0x0)/Pci(0x1c,0x0)/Pci(0x0,0x0)/MAC([REDACTED],0)/' + 'IPv4(0.0.0.00.0.0.0,0,0)..BO'), result[11]) - assertEntry( - ('0008', 'Generic USB Boot'), + self.assertEqual( + ('0008', 'Generic USB Boot', 'UsbClass', + 'UsbClass(ffff,ffff,255,255)'), result[12]) - assertEntry( - ('0009', 'Internal CD/DVD ROM Drive (UEFI'), + self.assertEqual( + ('0009', 'Internal CD/DVD ROM Drive (UEFI)', 'PciRoot', + 'PciRoot(0x0)/Pci(0x11,0x0)/Sata(1,65535,0)/' + 'CDROM(1,0x265,0x2000)'), result[13]) @mock.patch.object(os.path, 'exists', lambda *_: False)