diff --git a/doc/source/index.rst b/doc/source/index.rst index 4f1397c2b..2096f59bf 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -117,6 +117,11 @@ fields: system vendor information from SMBIOS as reported by ``dmidecode``: ``product_name``, ``serial_number`` and ``manufacturer``. +``boot`` + boot information with fields: ``current_boot_mode`` (boot mode used for + the current boot - BIOS or UEFI) and ``pxe_interface`` (interface used + for PXE booting, if any). + Image Builders -------------- Unlike most other python software, you must build an IPA ramdisk image before diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py index ac7e25d3f..8f437977f 100644 --- a/ironic_python_agent/hardware.py +++ b/ironic_python_agent/hardware.py @@ -220,6 +220,14 @@ class SystemVendorInfo(encoding.SerializableComparable): self.manufacturer = manufacturer +class BootInfo(encoding.SerializableComparable): + serializable_fields = ('current_boot_mode', 'pxe_interface') + + def __init__(self, current_boot_mode, pxe_interface=None): + self.current_boot_mode = current_boot_mode + self.pxe_interface = pxe_interface + + @six.add_metaclass(abc.ABCMeta) class HardwareManager(object): @abc.abstractmethod @@ -244,6 +252,9 @@ class HardwareManager(object): def get_bmc_address(self): raise errors.IncompatibleHardwareMethodError() + def get_boot_info(self): + raise errors.IncompatibleHardwareMethodError() + def erase_block_device(self, node, block_device): """Attempt to erase a block device. @@ -305,6 +316,7 @@ class HardwareManager(object): hardware_info['memory'] = self.get_memory() hardware_info['bmc_address'] = self.get_bmc_address() hardware_info['system_vendor'] = self.get_system_vendor_info() + hardware_info['boot'] = self.get_boot_info() return hardware_info def get_clean_steps(self, node, ports): @@ -597,6 +609,13 @@ class GenericHardwareManager(HardwareManager): serial_number=serial_number, manufacturer=manufacturer) + def get_boot_info(self): + boot_mode = 'uefi' if os.path.isdir('/sys/firmware/efi') else 'bios' + LOG.debug('The current boot mode is %s', boot_mode) + pxe_interface = utils.get_agent_params().get('BOOTIF') + return BootInfo(current_boot_mode=boot_mode, + pxe_interface=pxe_interface) + def erase_block_device(self, node, block_device): # Check if the block device is virtual media and skip the device. diff --git a/ironic_python_agent/inspector.py b/ironic_python_agent/inspector.py index d4930f842..715baf189 100644 --- a/ironic_python_agent/inspector.py +++ b/ironic_python_agent/inspector.py @@ -306,7 +306,7 @@ def collect_default(data, failures): LOG.debug('default root device is %s', root_disk.name) # Both boot interface and IPMI address might not be present, # we don't count it as failure - data['boot_interface'] = utils.get_agent_params().get('BOOTIF') + data['boot_interface'] = inventory['boot'].pxe_interface LOG.debug('boot devices was %s', data['boot_interface']) data['ipmi_address'] = inventory.get('bmc_address') LOG.debug('BMC IP address: %s', data['ipmi_address']) diff --git a/ironic_python_agent/tests/unit/test_hardware.py b/ironic_python_agent/tests/unit/test_hardware.py index 1aa04c624..945055259 100644 --- a/ironic_python_agent/tests/unit/test_hardware.py +++ b/ironic_python_agent/tests/unit/test_hardware.py @@ -500,6 +500,10 @@ class TestGenericHardwareManager(test_base.BaseTestCase): hardware.BlockDevice('/dev/hdaa', 'small', 65535, False), ] + self.hardware.get_boot_info = mock.Mock() + self.hardware.get_boot_info.return_value = hardware.BootInfo( + current_boot_mode='bios', pxe_interface='boot:if') + hardware_info = self.hardware.list_hardware_info() self.assertEqual(self.hardware.get_memory(), hardware_info['memory']) self.assertEqual(self.hardware.get_cpus(), hardware_info['cpu']) @@ -507,6 +511,8 @@ class TestGenericHardwareManager(test_base.BaseTestCase): hardware_info['disks']) self.assertEqual(self.hardware.list_network_interfaces(), hardware_info['interfaces']) + self.assertEqual(self.hardware.get_boot_info(), + hardware_info['boot']) @mock.patch.object(hardware, 'list_all_block_devices') def test_list_block_devices(self, list_mock): @@ -1120,6 +1126,30 @@ class TestGenericHardwareManager(test_base.BaseTestCase): mocked_root_dev.call_count) mocked_sleep.assert_called_with(hardware._DISK_WAIT_DELAY) + @mock.patch.object(utils, 'get_agent_params', + lambda: {'BOOTIF': 'boot:if'}) + @mock.patch.object(os.path, 'isdir', autospec=True) + def test_get_boot_info_pxe_interface(self, mocked_isdir): + mocked_isdir.return_value = False + result = self.hardware.get_boot_info() + self.assertEqual(hardware.BootInfo(current_boot_mode='bios', + pxe_interface='boot:if'), + result) + + @mock.patch.object(os.path, 'isdir', autospec=True) + def test_get_boot_info_bios(self, mocked_isdir): + mocked_isdir.return_value = False + result = self.hardware.get_boot_info() + self.assertEqual(hardware.BootInfo(current_boot_mode='bios'), result) + mocked_isdir.assert_called_once_with('/sys/firmware/efi') + + @mock.patch.object(os.path, 'isdir', autospec=True) + def test_get_boot_info_uefi(self, mocked_isdir): + mocked_isdir.return_value = True + result = self.hardware.get_boot_info() + self.assertEqual(hardware.BootInfo(current_boot_mode='uefi'), result) + mocked_isdir.assert_called_once_with('/sys/firmware/efi') + @mock.patch.object(utils, 'execute', autospec=True) class TestModuleFunctions(test_base.BaseTestCase): diff --git a/ironic_python_agent/tests/unit/test_inspector.py b/ironic_python_agent/tests/unit/test_inspector.py index a56c77adc..28d02919a 100644 --- a/ironic_python_agent/tests/unit/test_inspector.py +++ b/ironic_python_agent/tests/unit/test_inspector.py @@ -258,6 +258,8 @@ class BaseDiscoverTest(unittest.TestCase): rotational=True) ], 'bmc_address': '1.2.3.4', + 'boot': hardware.BootInfo(current_boot_mode='bios', + pxe_interface='boot:if') } self.failures = utils.AccumulatedFailures() self.data = {} @@ -327,8 +329,6 @@ class TestDiscoverSchedulingProperties(BaseDiscoverTest): self.data) -@mock.patch.object(utils, 'get_agent_params', - lambda: {'BOOTIF': 'boot:if'}) @mock.patch.object(inspector, 'wait_for_dhcp', autospec=True) @mock.patch.object(inspector, 'discover_scheduling_properties', autospec=True) @mock.patch.object(inspector, 'discover_network_properties', autospec=True) diff --git a/releasenotes/notes/boot-info-f18336ada089f6dd.yaml b/releasenotes/notes/boot-info-f18336ada089f6dd.yaml new file mode 100644 index 000000000..b31d19e68 --- /dev/null +++ b/releasenotes/notes/boot-info-f18336ada089f6dd.yaml @@ -0,0 +1,4 @@ +--- +features: + - Introduced "boot" field into the inventory, with "current_boot_mode" and + "pxe_interface" fields. Both will be used for inspection.