summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/source/admin/how_it_works.rst18
-rw-r--r--ironic_python_agent/config.py4
-rw-r--r--ironic_python_agent/efi_utils.py15
-rw-r--r--ironic_python_agent/hardware.py36
-rw-r--r--ironic_python_agent/inspector.py9
-rw-r--r--ironic_python_agent/netutils.py24
-rw-r--r--ironic_python_agent/tests/unit/extensions/test_image.py45
-rw-r--r--ironic_python_agent/tests/unit/test_efi_utils.py85
-rw-r--r--releasenotes/notes/fixes-efibootmgr-character-encoding-19e531ba694824c1.yaml9
-rw-r--r--releasenotes/notes/lldp-raw-a09174cb930bca97.yaml12
-rw-r--r--setup.cfg1
-rw-r--r--tox.ini2
12 files changed, 164 insertions, 96 deletions
diff --git a/doc/source/admin/how_it_works.rst b/doc/source/admin/how_it_works.rst
index 973588b1..20a5f477 100644
--- a/doc/source/admin/how_it_works.rst
+++ b/doc/source/admin/how_it_works.rst
@@ -145,8 +145,16 @@ collectors are:
* ``nics`` - list of objects with keys ``name`` (NIC name) and
``numa_node`` (node ID).
+``lldp``
+ Collects information about the network connectivity using LLDP_. Provides
+ one key:
+
+ * ``lldp_raw`` - mapping of interface names to lists of raw
+ type-length-value (TLV) records.
+
.. _hardware: https://pypi.org/project/hardware/
.. _NUMA: https://en.wikipedia.org/wiki/Non-uniform_memory_access
+.. _LLDP: https://en.wikipedia.org/wiki/Link_Layer_Discovery_Protocol
.. _hardware-inventory:
@@ -191,10 +199,12 @@ fields:
``interfaces``
list of network interfaces with fields: ``name``, ``mac_address``,
``ipv4_address``, ``lldp``, ``vendor``, ``product``, and optionally
- ``biosdevname`` (BIOS given NIC name). If configuration option
- ``collect_lldp`` is set to True the ``lldp`` field will be populated
- by a list of type-length-value(TLV) fields retrieved using the
- Link Layer Discovery Protocol (LLDP).
+ ``biosdevname`` (BIOS given NIC name).
+
+ .. note::
+ For backward compatibility, interfaces may contain ``lldp`` fields.
+ They are deprecated, consumers should rely on the ``lldp`` inspection
+ collector instead.
``system_vendor``
system vendor information from SMBIOS as reported by ``dmidecode``:
diff --git a/ironic_python_agent/config.py b/ironic_python_agent/config.py
index b1760179..5c5de305 100644
--- a/ironic_python_agent/config.py
+++ b/ironic_python_agent/config.py
@@ -149,7 +149,9 @@ cli_opts = [
help='Whether IPA should attempt to receive LLDP packets for '
'each network interface it discovers in the inventory. '
'Can be supplied as "ipa-collect-lldp" '
- 'kernel parameter.'),
+ 'kernel parameter.',
+ deprecated_for_removal=True,
+ deprecated_reason="Use the lldp collector instead"),
cfg.StrOpt('inspection_callback_url',
default=APARAMS.get('ipa-inspection-callback-url'),
diff --git a/ironic_python_agent/efi_utils.py b/ironic_python_agent/efi_utils.py
index da5e97b6..48708f22 100644
--- a/ironic_python_agent/efi_utils.py
+++ b/ironic_python_agent/efi_utils.py
@@ -275,8 +275,15 @@ def get_boot_records():
:return: an iterator yielding pairs (boot number, boot record).
"""
- efi_output = utils.execute('efibootmgr', '-v')
- for line in efi_output[0].split('\n'):
+ # Invokes binary=True so we get a bytestream back.
+ efi_output = utils.execute('efibootmgr', '-v', binary=True)
+ # Bytes must be decoded before regex can be run and
+ # matching to work as intended.
+ # Also ignore errors on decoding, as we can basically get
+ # garbage out of the nvram record, this way we don't fail
+ # hard on unrelated records.
+ cmd_output = efi_output[0].decode('utf-16', errors='ignore')
+ for line in cmd_output.split('\n'):
match = _ENTRY_LABEL.match(line)
if match is not None:
yield (match[1], match[2])
@@ -293,7 +300,7 @@ def add_boot_record(device, efi_partition, loader, label):
# https://linux.die.net/man/8/efibootmgr
utils.execute('efibootmgr', '-v', '-c', '-d', device,
'-p', str(efi_partition), '-w', '-L', label,
- '-l', loader)
+ '-l', loader, binary=True)
def remove_boot_record(boot_num):
@@ -301,7 +308,7 @@ def remove_boot_record(boot_num):
:param boot_num: the number of the boot record
"""
- utils.execute('efibootmgr', '-b', boot_num, '-B')
+ utils.execute('efibootmgr', '-b', boot_num, '-B', binary=True)
def _run_efibootmgr(valid_efi_bootloaders, device, efi_partition,
diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py
index 8dd3a961..badb07db 100644
--- a/ironic_python_agent/hardware.py
+++ b/ironic_python_agent/hardware.py
@@ -865,6 +865,9 @@ class HardwareManager(object, metaclass=abc.ABCMeta):
def list_network_interfaces(self):
raise errors.IncompatibleHardwareMethodError
+ def collect_lldp_data(self, interface_names=None):
+ raise errors.IncompatibleHardwareMethodError
+
def get_cpus(self):
raise errors.IncompatibleHardwareMethodError
@@ -1198,7 +1201,6 @@ class GenericHardwareManager(HardwareManager):
HARDWARE_MANAGER_VERSION = '1.1'
def __init__(self):
- self.sys_path = '/sys'
self.lldp_data = {}
def evaluate_hardware_support(self):
@@ -1213,7 +1215,7 @@ class GenericHardwareManager(HardwareManager):
self.wait_for_disks()
return HardwareSupport.GENERIC
- def collect_lldp_data(self, interface_names):
+ def collect_lldp_data(self, interface_names=None):
"""Collect and convert LLDP info from the node.
In order to process the LLDP information later, the raw data needs to
@@ -1222,7 +1224,8 @@ class GenericHardwareManager(HardwareManager):
:param interface_names: list of names of node's interfaces.
:return: a dict, containing the lldp data from every interface.
"""
-
+ if interface_names is None:
+ interface_names = netutils.list_interfaces()
interface_names = [name for name in interface_names if name != 'lo']
lldp_data = {}
try:
@@ -1292,7 +1295,7 @@ class GenericHardwareManager(HardwareManager):
"""
global WARN_BIOSDEVNAME_NOT_FOUND
- if self._is_vlan(interface_name):
+ if netutils.is_vlan(interface_name):
LOG.debug('Interface %s is a VLAN, biosdevname not called',
interface_name)
return
@@ -1313,35 +1316,14 @@ class GenericHardwareManager(HardwareManager):
else:
LOG.warning('Biosdevname returned exit code %s', e.exit_code)
- def _is_device(self, interface_name):
- device_path = '{}/class/net/{}/device'.format(self.sys_path,
- interface_name)
- return os.path.exists(device_path)
-
- def _is_vlan(self, interface_name):
- # A VLAN interface does not have /device, check naming convention
- # used when adding VLAN interface
-
- interface, sep, vlan = interface_name.partition('.')
-
- return vlan.isdigit()
-
- def _is_bond(self, interface_name):
- device_path = '{}/class/net/{}/bonding'.format(self.sys_path,
- interface_name)
- return os.path.exists(device_path)
-
def list_network_interfaces(self):
- network_interfaces_list = []
- iface_names = os.listdir('{}/class/net'.format(self.sys_path))
- iface_names = [name for name in iface_names
- if self._is_vlan(name) or self._is_device(name)
- or self._is_bond(name)]
+ iface_names = netutils.list_interfaces()
if CONF.collect_lldp:
self.lldp_data = dispatch_to_managers('collect_lldp_data',
interface_names=iface_names)
+ network_interfaces_list = []
for iface_name in iface_names:
try:
result = dispatch_to_managers(
diff --git a/ironic_python_agent/inspector.py b/ironic_python_agent/inspector.py
index 8e570c3b..c2d98d5d 100644
--- a/ironic_python_agent/inspector.py
+++ b/ironic_python_agent/inspector.py
@@ -364,3 +364,12 @@ def collect_pci_devices_info(data, failures):
'revision': pci_revision,
'bus': subdir})
data['pci_devices'] = pci_devices_info
+
+
+def collect_lldp(data, failures):
+ """Collect LLDP information for network interfaces.
+
+ :param data: mutable data that we'll send to inspector
+ :param failures: AccumulatedFailures object
+ """
+ data['lldp_raw'] = hardware.dispatch_to_managers('collect_lldp_data')
diff --git a/ironic_python_agent/netutils.py b/ironic_python_agent/netutils.py
index 0b595d96..2e289808 100644
--- a/ironic_python_agent/netutils.py
+++ b/ironic_python_agent/netutils.py
@@ -14,6 +14,7 @@
import ctypes
import fcntl
+import os
import select
import socket
import struct
@@ -247,6 +248,29 @@ def get_hostname():
return socket.gethostname()
+def is_network_device(interface_name):
+ device_path = f'/sys/class/net/{interface_name}/device'
+ return os.path.exists(device_path)
+
+
+def is_vlan(interface_name):
+ # A VLAN interface does not have /device, check naming convention
+ # used when adding VLAN interface
+ interface, sep, vlan = interface_name.partition('.')
+ return vlan.isdigit()
+
+
+def is_bond(interface_name):
+ device_path = f'/sys/class/net/{interface_name}/bonding'
+ return os.path.exists(device_path)
+
+
+def list_interfaces():
+ iface_names = os.listdir('/sys/class/net')
+ return [name for name in iface_names
+ if is_vlan(name) or is_network_device(name) or is_bond(name)]
+
+
def interface_has_carrier(interface_name):
path = '/sys/class/net/{}/carrier'.format(interface_name)
try:
diff --git a/ironic_python_agent/tests/unit/extensions/test_image.py b/ironic_python_agent/tests/unit/extensions/test_image.py
index 46afe0ef..2bfa782c 100644
--- a/ironic_python_agent/tests/unit/extensions/test_image.py
+++ b/ironic_python_agent/tests/unit/extensions/test_image.py
@@ -32,6 +32,9 @@ from ironic_python_agent.tests.unit import base
from ironic_python_agent.tests.unit.samples import hardware_samples as hws
+EFI_RESULT = ''.encode('utf-16')
+
+
@mock.patch.object(hardware, 'dispatch_to_managers', autospec=True)
@mock.patch.object(ilib_utils, 'execute', autospec=True)
@mock.patch.object(tempfile, 'mkdtemp', lambda *_: '/tmp/fake-dir')
@@ -231,7 +234,7 @@ class TestImageExtension(base.IronicAgentTest):
mock_execute.side_effect = iter([('', ''), ('', ''),
('', ''), ('', ''),
- ('', ''), ('', ''),
+ (EFI_RESULT, ''), (EFI_RESULT, ''),
('', ''), ('', '')])
expected = [mock.call('efibootmgr', '--version'),
@@ -240,11 +243,11 @@ class TestImageExtension(base.IronicAgentTest):
mock.call('udevadm', 'settle'),
mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
+ mock.call('efibootmgr', '-v', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', self.fake_dev,
'-p', '1', '-w',
'-L', 'ironic1', '-l',
- '\\EFI\\BOOT\\BOOTX64.EFI'),
+ '\\EFI\\BOOT\\BOOTX64.EFI', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
@@ -279,7 +282,7 @@ class TestImageExtension(base.IronicAgentTest):
mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI']
mock_execute.side_effect = iter([('', ''), ('', ''),
('', ''), ('', ''),
- ('', ''), ('', ''),
+ (EFI_RESULT, ''), (EFI_RESULT, ''),
('', ''), ('', '')])
expected = [mock.call('efibootmgr', '--version'),
@@ -288,11 +291,11 @@ class TestImageExtension(base.IronicAgentTest):
mock.call('udevadm', 'settle'),
mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
+ mock.call('efibootmgr', '-v', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', self.fake_dev,
'-p', '1', '-w',
'-L', 'ironic1', '-l',
- '\\EFI\\BOOT\\BOOTX64.EFI'),
+ '\\EFI\\BOOT\\BOOTX64.EFI', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
@@ -333,10 +336,11 @@ Boot0000 ironic1 HD(1,GPT,4f3c6294-bf9b-4208-9808-be45dfc34b5c)File(\EFI\Boot\BO
Boot0001 ironic2 HD(1,GPT,4f3c6294-bf9b-4208-9808-111111111112)File(\EFI\Boot\BOOTX64.EFI)
Boot0002 VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
""" # noqa This is a giant literal string for testing.
+ stdout_msg = stdout_msg.encode('utf-16')
mock_execute.side_effect = iter([('', ''), ('', ''),
('', ''), ('', ''),
- (stdout_msg, ''), ('', ''),
- ('', ''), ('', ''),
+ (stdout_msg, ''), (EFI_RESULT, ''),
+ (EFI_RESULT, ''), (EFI_RESULT, ''),
('', ''), ('', '')])
expected = [mock.call('efibootmgr', '--version'),
@@ -345,12 +349,12 @@ Boot0002 VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
mock.call('udevadm', 'settle'),
mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
- mock.call('efibootmgr', '-b', '0000', '-B'),
+ mock.call('efibootmgr', '-v', binary=True),
+ mock.call('efibootmgr', '-b', '0000', '-B', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', self.fake_dev,
'-p', '1', '-w',
'-L', 'ironic1', '-l',
- '\\EFI\\BOOT\\BOOTX64.EFI'),
+ '\\EFI\\BOOT\\BOOTX64.EFI', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
@@ -396,6 +400,7 @@ Boot0002* Hard Disk VenHw(1fad3248-0000-7950-2166-a1e506fdb83a,01000000)..GO
Boot0003* Network VenHw(1fad3248-0000-7950-2166-a1e506fdb83a,05000000)..GO..NO............U.E.F.I.:. . . .S.L.O.T.2. .(.2.F./.0./.0.). .P.X.E. .I.P.4. . .Q.L.o.g.i.c. .Q.L.4.1.2.6.2. .P.C.I.e. .2.5.G.b. .2.-.P.o.r.t. .S.F.P.2.8. .E.t.h.e.r.n.e.t. .A.d.a.p.t.e.r. .-. .P.X.E........A....................%.4..Z...............................................................Gd-.;.A..MQ..L.P.X.E. .I.P.4. .Q.L.o.g.i.c. .Q.L.4.1.2.6.2. .P.C.I.e. .2.5.G.b. .2.-.P.o.r.t. .S.F.P.2.8. .E.t.h.e.r.n.e.t. .A.d.a.p.t.e.r. .-. .P.X.E.......BO..NO............U.E.F.I.:. . . .S.L.O.T.1. .(.3.0./.0./.0.). .P.X.E. .I.P.4. . .Q.L.o.g.i.c. .Q.L.4.1.2.6.2. .P.C.I.e. .2.5.G.b. .2.-.P.o.r.t. .S.F.P.2.8. .E.t.h.e.r.n.e.t. .A.d.a.p.t.e.r. .-.
Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x64000)/File(\EFI\boot\shimx64.efi)
""" # noqa This is a giant literal string for testing.
+ stdout_msg = stdout_msg.encode('utf-16')
mock_execute.side_effect = iter([('', ''), ('', ''),
('', ''), ('', ''),
(stdout_msg, ''), ('', ''),
@@ -407,13 +412,13 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
mock.call('udevadm', 'settle'),
mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
- mock.call('efibootmgr', '-b', '0000', '-B'),
- mock.call('efibootmgr', '-b', '0004', '-B'),
+ mock.call('efibootmgr', '-v', binary=True),
+ mock.call('efibootmgr', '-b', '0000', '-B', binary=True),
+ mock.call('efibootmgr', '-b', '0004', '-B', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', self.fake_dev,
'-p', '1', '-w',
'-L', 'ironic1', '-l',
- '\\EFI\\BOOT\\BOOTX64.EFI'),
+ '\\EFI\\BOOT\\BOOTX64.EFI', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
@@ -450,8 +455,8 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
mock_execute.side_effect = iter([('', ''), ('', ''),
('', ''), ('', ''),
- ('', ''), ('', ''),
- ('', ''), ('', ''),
+ (EFI_RESULT, ''), (EFI_RESULT, ''),
+ (EFI_RESULT, ''), ('', ''),
('', '')])
expected = [mock.call('efibootmgr', '--version'),
@@ -460,15 +465,15 @@ Boot0004* ironic1 HD(1,GPT,55db8d03-c8f6-4a5b-9155-790dddc348fa,0x800,0x640
mock.call('udevadm', 'settle'),
mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
+ mock.call('efibootmgr', '-v', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', self.fake_dev,
'-p', '1', '-w',
'-L', 'ironic1', '-l',
- '\\EFI\\BOOT\\BOOTX64.EFI'),
+ '\\EFI\\BOOT\\BOOTX64.EFI', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', self.fake_dev,
'-p', '1', '-w',
'-L', 'ironic2', '-l',
- '\\WINDOWS\\system32\\winload.efi'),
+ '\\WINDOWS\\system32\\winload.efi', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
diff --git a/ironic_python_agent/tests/unit/test_efi_utils.py b/ironic_python_agent/tests/unit/test_efi_utils.py
index 64de61bc..09e89796 100644
--- a/ironic_python_agent/tests/unit/test_efi_utils.py
+++ b/ironic_python_agent/tests/unit/test_efi_utils.py
@@ -28,6 +28,9 @@ from ironic_python_agent.tests.unit.samples import hardware_samples
from ironic_python_agent import utils
+EFI_RESULT = ''.encode('utf-16')
+
+
@mock.patch.object(os, 'walk', autospec=True)
@mock.patch.object(os, 'access', autospec=False)
class TestGetEfiBootloaders(base.IronicAgentTest):
@@ -115,16 +118,16 @@ class TestRunEfiBootmgr(base.IronicAgentTest):
mock_execute.assert_has_calls(expected)
def test__run_efibootmgr(self, mock_execute):
- mock_execute.return_value = ('', '')
+ mock_execute.return_value = (''.encode('utf-16'), '')
result = efi_utils._run_efibootmgr(['EFI/BOOT/BOOTX64.EFI'],
self.fake_dev,
self.fake_efi_system_part,
self.fake_dir)
- expected = [mock.call('efibootmgr', '-v'),
+ expected = [mock.call('efibootmgr', '-v', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', self.fake_dev,
'-p', self.fake_efi_system_part, '-w',
'-L', 'ironic1', '-l',
- '\\EFI\\BOOT\\BOOTX64.EFI')]
+ '\\EFI\\BOOT\\BOOTX64.EFI', binary=True)]
self.assertIsNone(result)
mock_execute.assert_has_calls(expected)
@@ -176,19 +179,18 @@ class TestManageUefi(base.IronicAgentTest):
mock_is_md_device.return_value = False
mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI']
-
- mock_execute.side_effect = iter([('', ''), ('', ''),
- ('', ''), ('', ''),
+ mock_execute.side_effect = iter([('', ''), (EFI_RESULT, ''),
+ (EFI_RESULT, ''), ('', ''),
('', ''), ('', ''),
('', '')])
expected = [mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
+ mock.call('efibootmgr', '-v', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', self.fake_dev,
'-p', '1', '-w',
'-L', 'ironic1', '-l',
- '\\EFI\\BOOT\\BOOTX64.EFI'),
+ '\\EFI\\BOOT\\BOOTX64.EFI', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
@@ -219,30 +221,34 @@ class TestManageUefi(base.IronicAgentTest):
# at the start of the file, where as Red Hat *does*
csv_file_data = u'shimx64.efi,Vendor String,,Grub2MadeUSDoThis\n'
# This test also handles deleting a pre-existing matching vendor
- # string in advance.
+ # string in advance. This string also includes a UTF16 character
+ # *on* purpose, to force proper decoding to be tested and garbage
+ # characters which can be found in OVMF test VM NVRAM records.
dupe_entry = """
BootCurrent: 0001
Timeout: 0 seconds
BootOrder: 0000,00001
-Boot0000* Vendor String HD(1,GPT,4f3c6294-bf9b-4208-9808-be45dfc34b5c)File(\EFI\Boot\BOOTX64.EFI)
-Boot0001 Vendor String HD(2,GPT,4f3c6294-bf9b-4208-9808-be45dfc34b5c)File(\EFI\Boot\BOOTX64.EFI)
-Boot0002: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
+Boot0000 UTF16ΓΏ HD(1,GPT,4f3c6294-bf9b-4208-9808-be45dfc34b5c)File(\EFI\Boot\BOOTX64.EFI)
+Boot0001* Vendor String HD(1,GPT,4f3c6294-bf9b-4208-9808-be45dfc34b5c)File(\EFI\Boot\BOOTX64.EFI)
+Boot0002 Vendor String HD(2,GPT,4f3c6294-bf9b-4208-9808-be45dfc34b5c)File(\EFI\Boot\BOOTX64.EFI)
+Boot0003: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)N.....YM....R,Y.
""" # noqa This is a giant literal string for testing.
-
- mock_execute.side_effect = iter([('', ''), (dupe_entry, ''),
+ dupe_entry = dupe_entry.encode('utf-16')
+ mock_execute.side_effect = iter([('', ''),
+ (dupe_entry, ''),
('', ''), ('', ''),
('', ''), ('', ''),
('', '')])
expected = [mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
- mock.call('efibootmgr', '-b', '0000', '-B'),
- mock.call('efibootmgr', '-b', '0001', '-B'),
+ mock.call('efibootmgr', '-v', binary=True),
+ mock.call('efibootmgr', '-b', '0001', '-B', binary=True),
+ mock.call('efibootmgr', '-b', '0002', '-B', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', self.fake_dev,
'-p', '1', '-w',
'-L', 'Vendor String', '-l',
- '\\EFI\\vendor\\shimx64.efi'),
+ '\\EFI\\vendor\\shimx64.efi', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
@@ -266,19 +272,18 @@ Boot0002: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
mock_is_md_device.return_value = False
mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI']
-
- mock_execute.side_effect = iter([('', ''), ('', ''),
- ('', ''), ('', ''),
+ mock_execute.side_effect = iter([('', ''), (EFI_RESULT, ''),
+ (EFI_RESULT, ''), ('', ''),
('', ''), ('', ''),
('', '')])
expected = [mock.call('mount', '/dev/fakenvme0p1',
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
+ mock.call('efibootmgr', '-v', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', '/dev/fakenvme0',
'-p', '1', '-w',
'-L', 'ironic1', '-l',
- '\\EFI\\BOOT\\BOOTX64.EFI'),
+ '\\EFI\\BOOT\\BOOTX64.EFI', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
@@ -301,19 +306,18 @@ Boot0002: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
mock_is_md_device.return_value = False
mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI']
-
- mock_execute.side_effect = iter([('', ''), ('', ''),
- ('', ''), ('', ''),
+ mock_execute.side_effect = iter([('', ''), (EFI_RESULT, ''),
+ (EFI_RESULT, ''), ('', ''),
('', ''), ('', ''),
('', '')])
expected = [mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
+ mock.call('efibootmgr', '-v', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', self.fake_dev,
'-p', '1', '-w',
'-L', 'ironic1', '-l',
- '\\EFI\\BOOT\\BOOTX64.EFI'),
+ '\\EFI\\BOOT\\BOOTX64.EFI', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
@@ -347,11 +351,14 @@ Boot0002: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
mock_get_component_devices.return_value = ['/dev/sda3', '/dev/sdb3']
mock_efi_bl.return_value = ['EFI/BOOT/BOOTX64.EFI']
-
- mock_execute.side_effect = iter([('', ''), ('', ''),
- ('', ''), ('', ''),
- ('', ''), ('', ''),
- ('', ''), ('', ''),
+ mock_execute.side_effect = iter([('', ''),
+ ('', ''),
+ ('', ''),
+ (EFI_RESULT, ''),
+ (EFI_RESULT, ''),
+ (EFI_RESULT, ''),
+ (EFI_RESULT, ''),
+ ('', ''),
('', '')])
expected = [mock.call('mount', self.fake_efi_system_part,
@@ -360,14 +367,14 @@ Boot0002: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
attempts=3, delay_on_retry=True),
mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
+ mock.call('efibootmgr', '-v', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', '/dev/sda3',
'-p', '3', '-w', '-L', 'ironic1 (RAID, part0)',
- '-l', '\\EFI\\BOOT\\BOOTX64.EFI'),
- mock.call('efibootmgr', '-v'),
+ '-l', '\\EFI\\BOOT\\BOOTX64.EFI', binary=True),
+ mock.call('efibootmgr', '-v', binary=True),
mock.call('efibootmgr', '-v', '-c', '-d', '/dev/sdb3',
'-p', '3', '-w', '-L', 'ironic1 (RAID, part1)',
- '-l', '\\EFI\\BOOT\\BOOTX64.EFI'),
+ '-l', '\\EFI\\BOOT\\BOOTX64.EFI', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
@@ -425,7 +432,7 @@ Boot0002: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
expected = [mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
+ mock.call('efibootmgr', '-v', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True),
mock.call('sync')]
@@ -462,7 +469,7 @@ Boot0002: VENDMAGIC FvFile(9f3c6294-bf9b-4208-9808-be45dfc34b51)
expected = [mock.call('mount', self.fake_efi_system_part,
self.fake_dir + '/boot/efi'),
- mock.call('efibootmgr', '-v'),
+ mock.call('efibootmgr', '-v', binary=True),
mock.call('umount', self.fake_dir + '/boot/efi',
attempts=3, delay_on_retry=True)]
diff --git a/releasenotes/notes/fixes-efibootmgr-character-encoding-19e531ba694824c1.yaml b/releasenotes/notes/fixes-efibootmgr-character-encoding-19e531ba694824c1.yaml
new file mode 100644
index 00000000..b4c3270c
--- /dev/null
+++ b/releasenotes/notes/fixes-efibootmgr-character-encoding-19e531ba694824c1.yaml
@@ -0,0 +1,9 @@
+---
+fixes:
+ - |
+ Fixes UEFI NVRAM record handling with efibootmgr so we can accept and
+ handle UTF-16 encoded data which is to be expected in UEFI NVRAM as
+ the records are UTF-16 encoded.
+ - |
+ Fixes handling of UEFI NVRAM records to allow for unexpected characters
+ in the response, so it is non-fatal to Ironic.
diff --git a/releasenotes/notes/lldp-raw-a09174cb930bca97.yaml b/releasenotes/notes/lldp-raw-a09174cb930bca97.yaml
new file mode 100644
index 00000000..19d668bc
--- /dev/null
+++ b/releasenotes/notes/lldp-raw-a09174cb930bca97.yaml
@@ -0,0 +1,12 @@
+---
+features:
+ - |
+ Adds a new inspection collector ``lldp`` that collects LLDP information
+ into the ``lldp_raw`` field.
+deprecations:
+ - |
+ The LLDP information as part of the general inventory is deprecated.
+ Use the new ``lldp`` inspection collector to retrieve it.
+ - |
+ The ``ipa-collect-lldp`` kernel parameter and the corresponding option are
+ now deprecated.
diff --git a/setup.cfg b/setup.cfg
index 8e9e95f3..e2a28664 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -55,6 +55,7 @@ ironic_python_agent.inspector.collectors =
pci-devices = ironic_python_agent.inspector:collect_pci_devices_info
numa-topology = ironic_python_agent.numa_inspector:collect_numa_topology_info
dmi-decode = ironic_python_agent.dmi_inspector:collect_dmidecode_info
+ lldp = ironic_python_agent.inspector:collect_lldp
[pbr]
autodoc_index_modules = True
diff --git a/tox.ini b/tox.ini
index 139a5232..f53ce780 100644
--- a/tox.ini
+++ b/tox.ini
@@ -39,7 +39,7 @@ commands = stestr run {posargs}
[testenv:pep8]
deps =
- hacking>=4.1.0,<5.0.0 # Apache-2.0
+ hacking~=6.0.0 # Apache-2.0
bashate>=0.5.1 # Apache-2.0
flake8-import-order>=0.17.1 # LGPLv3
pycodestyle>=2.0.0,<3.0.0 # MIT