Add boot information into the inventory

Adds a new BootInfo object with 2 fields:

* current_boot_mode - bios or uefi, detected from presence of /sys/firmware/efi
  as per the following answer: http://askubuntu.com/a/162896
  This field will be used for setting the boot_mode capability in ironic-inspector
* pxe_interface - PXE booting interface, if it can be detected.
  This fields is already used by ironic-inspector, added here for consistency.

Change-Id: Ib36b592ffaba3bfa055d65c9526607867d302584
Partial-Bug: #1571580
This commit is contained in:
Dmitry Tantsur 2016-04-20 13:58:14 +02:00
parent 962ee1afb5
commit 53b187a4c3
6 changed files with 61 additions and 3 deletions

View File

@ -117,6 +117,11 @@ fields:
system vendor information from SMBIOS as reported by ``dmidecode``: system vendor information from SMBIOS as reported by ``dmidecode``:
``product_name``, ``serial_number`` and ``manufacturer``. ``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 Image Builders
-------------- --------------
Unlike most other python software, you must build an IPA ramdisk image before Unlike most other python software, you must build an IPA ramdisk image before

View File

@ -220,6 +220,14 @@ class SystemVendorInfo(encoding.SerializableComparable):
self.manufacturer = manufacturer 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) @six.add_metaclass(abc.ABCMeta)
class HardwareManager(object): class HardwareManager(object):
@abc.abstractmethod @abc.abstractmethod
@ -244,6 +252,9 @@ class HardwareManager(object):
def get_bmc_address(self): def get_bmc_address(self):
raise errors.IncompatibleHardwareMethodError() raise errors.IncompatibleHardwareMethodError()
def get_boot_info(self):
raise errors.IncompatibleHardwareMethodError()
def erase_block_device(self, node, block_device): def erase_block_device(self, node, block_device):
"""Attempt to erase a block device. """Attempt to erase a block device.
@ -305,6 +316,7 @@ class HardwareManager(object):
hardware_info['memory'] = self.get_memory() hardware_info['memory'] = self.get_memory()
hardware_info['bmc_address'] = self.get_bmc_address() hardware_info['bmc_address'] = self.get_bmc_address()
hardware_info['system_vendor'] = self.get_system_vendor_info() hardware_info['system_vendor'] = self.get_system_vendor_info()
hardware_info['boot'] = self.get_boot_info()
return hardware_info return hardware_info
def get_clean_steps(self, node, ports): def get_clean_steps(self, node, ports):
@ -597,6 +609,13 @@ class GenericHardwareManager(HardwareManager):
serial_number=serial_number, serial_number=serial_number,
manufacturer=manufacturer) 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): def erase_block_device(self, node, block_device):
# Check if the block device is virtual media and skip the device. # Check if the block device is virtual media and skip the device.

View File

@ -306,7 +306,7 @@ def collect_default(data, failures):
LOG.debug('default root device is %s', root_disk.name) LOG.debug('default root device is %s', root_disk.name)
# Both boot interface and IPMI address might not be present, # Both boot interface and IPMI address might not be present,
# we don't count it as failure # 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']) LOG.debug('boot devices was %s', data['boot_interface'])
data['ipmi_address'] = inventory.get('bmc_address') data['ipmi_address'] = inventory.get('bmc_address')
LOG.debug('BMC IP address: %s', data['ipmi_address']) LOG.debug('BMC IP address: %s', data['ipmi_address'])

View File

@ -500,6 +500,10 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
hardware.BlockDevice('/dev/hdaa', 'small', 65535, False), 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() hardware_info = self.hardware.list_hardware_info()
self.assertEqual(self.hardware.get_memory(), hardware_info['memory']) self.assertEqual(self.hardware.get_memory(), hardware_info['memory'])
self.assertEqual(self.hardware.get_cpus(), hardware_info['cpu']) self.assertEqual(self.hardware.get_cpus(), hardware_info['cpu'])
@ -507,6 +511,8 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
hardware_info['disks']) hardware_info['disks'])
self.assertEqual(self.hardware.list_network_interfaces(), self.assertEqual(self.hardware.list_network_interfaces(),
hardware_info['interfaces']) hardware_info['interfaces'])
self.assertEqual(self.hardware.get_boot_info(),
hardware_info['boot'])
@mock.patch.object(hardware, 'list_all_block_devices') @mock.patch.object(hardware, 'list_all_block_devices')
def test_list_block_devices(self, list_mock): def test_list_block_devices(self, list_mock):
@ -1120,6 +1126,30 @@ class TestGenericHardwareManager(test_base.BaseTestCase):
mocked_root_dev.call_count) mocked_root_dev.call_count)
mocked_sleep.assert_called_with(hardware._DISK_WAIT_DELAY) 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) @mock.patch.object(utils, 'execute', autospec=True)
class TestModuleFunctions(test_base.BaseTestCase): class TestModuleFunctions(test_base.BaseTestCase):

View File

@ -258,6 +258,8 @@ class BaseDiscoverTest(unittest.TestCase):
rotational=True) rotational=True)
], ],
'bmc_address': '1.2.3.4', 'bmc_address': '1.2.3.4',
'boot': hardware.BootInfo(current_boot_mode='bios',
pxe_interface='boot:if')
} }
self.failures = utils.AccumulatedFailures() self.failures = utils.AccumulatedFailures()
self.data = {} self.data = {}
@ -327,8 +329,6 @@ class TestDiscoverSchedulingProperties(BaseDiscoverTest):
self.data) 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, 'wait_for_dhcp', autospec=True)
@mock.patch.object(inspector, 'discover_scheduling_properties', autospec=True) @mock.patch.object(inspector, 'discover_scheduling_properties', autospec=True)
@mock.patch.object(inspector, 'discover_network_properties', autospec=True) @mock.patch.object(inspector, 'discover_network_properties', autospec=True)

View File

@ -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.