Report system firmware information in the inventory
Change-Id: I5b6ceb9cdcf4baa97a6f0482d1030d14f3f2ecff
This commit is contained in:
parent
2ddb693491
commit
0304c73c0e
@ -198,7 +198,9 @@ fields:
|
|||||||
|
|
||||||
``system_vendor``
|
``system_vendor``
|
||||||
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``, as well as
|
||||||
|
a ``firmware`` structure with fields ``vendor``, ``version`` and
|
||||||
|
``build_date``.
|
||||||
|
|
||||||
``boot``
|
``boot``
|
||||||
boot information with fields: ``current_boot_mode`` (boot mode used for
|
boot information with fields: ``current_boot_mode`` (boot mode used for
|
||||||
|
@ -339,22 +339,16 @@ def get_component_devices(raid_device):
|
|||||||
|
|
||||||
def _calc_memory(sys_dict):
|
def _calc_memory(sys_dict):
|
||||||
physical = 0
|
physical = 0
|
||||||
for sys_child in sys_dict['children']:
|
core_dict = next(utils.find_in_lshw(sys_dict, 'core'), {})
|
||||||
if sys_child['id'] != 'core':
|
for core_child in utils.find_in_lshw(core_dict, _MEMORY_ID_RE):
|
||||||
continue
|
if core_child.get('size'):
|
||||||
for core_child in sys_child['children']:
|
value = ("%(size)s %(units)s" % core_child)
|
||||||
if not _MEMORY_ID_RE.match(core_child['id']):
|
physical += int(UNIT_CONVERTER(value).to('MB').magnitude)
|
||||||
continue
|
else:
|
||||||
if core_child.get('size'):
|
for bank in core_child.get('children', ()):
|
||||||
value = ("%(size)s %(units)s" % core_child)
|
if bank.get('size'):
|
||||||
physical += int(UNIT_CONVERTER(value).to
|
value = ("%(size)s %(units)s" % bank)
|
||||||
('MB').magnitude)
|
physical += int(UNIT_CONVERTER(value).to('MB').magnitude)
|
||||||
else:
|
|
||||||
for bank in core_child.get('children', ()):
|
|
||||||
if bank.get('size'):
|
|
||||||
value = ("%(size)s %(units)s" % bank)
|
|
||||||
physical += int(UNIT_CONVERTER(value).to
|
|
||||||
('MB').magnitude)
|
|
||||||
return physical
|
return physical
|
||||||
|
|
||||||
|
|
||||||
@ -835,13 +829,24 @@ class Memory(encoding.SerializableComparable):
|
|||||||
self.physical_mb = physical_mb
|
self.physical_mb = physical_mb
|
||||||
|
|
||||||
|
|
||||||
class SystemVendorInfo(encoding.SerializableComparable):
|
class SystemFirmware(encoding.SerializableComparable):
|
||||||
serializable_fields = ('product_name', 'serial_number', 'manufacturer')
|
serializable_fields = ('vendor', 'version', 'build_date')
|
||||||
|
|
||||||
def __init__(self, product_name, serial_number, manufacturer):
|
def __init__(self, vendor, version, build_date):
|
||||||
|
self.version = version
|
||||||
|
self.build_date = build_date
|
||||||
|
self.vendor = vendor
|
||||||
|
|
||||||
|
|
||||||
|
class SystemVendorInfo(encoding.SerializableComparable):
|
||||||
|
serializable_fields = ('product_name', 'serial_number', 'manufacturer',
|
||||||
|
'firmware')
|
||||||
|
|
||||||
|
def __init__(self, product_name, serial_number, manufacturer, firmware):
|
||||||
self.product_name = product_name
|
self.product_name = product_name
|
||||||
self.serial_number = serial_number
|
self.serial_number = serial_number
|
||||||
self.manufacturer = manufacturer
|
self.manufacturer = manufacturer
|
||||||
|
self.firmware = firmware
|
||||||
|
|
||||||
|
|
||||||
class BootInfo(encoding.SerializableComparable):
|
class BootInfo(encoding.SerializableComparable):
|
||||||
@ -1512,9 +1517,17 @@ class GenericHardwareManager(HardwareManager):
|
|||||||
except (processutils.ProcessExecutionError, OSError, ValueError) as e:
|
except (processutils.ProcessExecutionError, OSError, ValueError) as e:
|
||||||
LOG.warning('Could not retrieve vendor info from lshw: %s', e)
|
LOG.warning('Could not retrieve vendor info from lshw: %s', e)
|
||||||
sys_dict = {}
|
sys_dict = {}
|
||||||
|
|
||||||
|
core_dict = next(utils.find_in_lshw(sys_dict, 'core'), {})
|
||||||
|
fw_dict = next(utils.find_in_lshw(core_dict, 'firmware'), {})
|
||||||
|
|
||||||
|
firmware = SystemFirmware(vendor=fw_dict.get('vendor', ''),
|
||||||
|
version=fw_dict.get('version', ''),
|
||||||
|
build_date=fw_dict.get('date', ''))
|
||||||
return SystemVendorInfo(product_name=sys_dict.get('product', ''),
|
return SystemVendorInfo(product_name=sys_dict.get('product', ''),
|
||||||
serial_number=sys_dict.get('serial', ''),
|
serial_number=sys_dict.get('serial', ''),
|
||||||
manufacturer=sys_dict.get('vendor', ''))
|
manufacturer=sys_dict.get('vendor', ''),
|
||||||
|
firmware=firmware)
|
||||||
|
|
||||||
def get_boot_info(self):
|
def get_boot_info(self):
|
||||||
boot_mode = 'uefi' if os.path.isdir('/sys/firmware/efi') else 'bios'
|
boot_mode = 'uefi' if os.path.isdir('/sys/firmware/efi') else 'bios'
|
||||||
|
@ -490,6 +490,34 @@ LSHW_JSON_OUTPUT_V2 = ("""
|
|||||||
"serial" : "1234",
|
"serial" : "1234",
|
||||||
"slot" : "NULL",
|
"slot" : "NULL",
|
||||||
"children" : [
|
"children" : [
|
||||||
|
{
|
||||||
|
"id": "firmware",
|
||||||
|
"class": "memory",
|
||||||
|
"claimed": true,
|
||||||
|
"description": "BIOS",
|
||||||
|
"vendor": "BIOSVNDR",
|
||||||
|
"physid": "0",
|
||||||
|
"version": "1.2.3",
|
||||||
|
"date": "03/30/2023",
|
||||||
|
"units": "bytes",
|
||||||
|
"size": 65536,
|
||||||
|
"capacity": 16777216,
|
||||||
|
"capabilities": {
|
||||||
|
"isa": "ISA bus",
|
||||||
|
"pci": "PCI bus",
|
||||||
|
"pnp": "Plug-and-Play",
|
||||||
|
"upgrade": "BIOS EEPROM can be upgraded",
|
||||||
|
"shadowing": "BIOS shadowing",
|
||||||
|
"cdboot": "Booting from CD-ROM/DVD",
|
||||||
|
"bootselect": "Selectable boot path",
|
||||||
|
"edd": "Enhanced Disk Drive extensions",
|
||||||
|
"acpi": "ACPI",
|
||||||
|
"usb": "USB legacy emulation",
|
||||||
|
"biosbootspecification": "BIOS boot specification",
|
||||||
|
"netboot": "Function-key initiated network service boot",
|
||||||
|
"uefi": "UEFI specification is supported"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"id" : "memory:0",
|
"id" : "memory:0",
|
||||||
"class" : "memory",
|
"class" : "memory",
|
||||||
|
@ -5247,6 +5247,10 @@ class TestGenericHardwareManager(base.IronicAgentTest):
|
|||||||
self.assertEqual('ABC123 (GENERIC_SERVER)', vendor_info.product_name)
|
self.assertEqual('ABC123 (GENERIC_SERVER)', vendor_info.product_name)
|
||||||
self.assertEqual('1234567', vendor_info.serial_number)
|
self.assertEqual('1234567', vendor_info.serial_number)
|
||||||
self.assertEqual('GENERIC', vendor_info.manufacturer)
|
self.assertEqual('GENERIC', vendor_info.manufacturer)
|
||||||
|
# This sample does not have firmware information
|
||||||
|
self.assertEqual('', vendor_info.firmware.vendor)
|
||||||
|
self.assertEqual('', vendor_info.firmware.build_date)
|
||||||
|
self.assertEqual('', vendor_info.firmware.version)
|
||||||
|
|
||||||
@mock.patch.object(il_utils, 'execute', autospec=True)
|
@mock.patch.object(il_utils, 'execute', autospec=True)
|
||||||
def test_get_system_vendor_info_lshw_list(self, mocked_execute):
|
def test_get_system_vendor_info_lshw_list(self, mocked_execute):
|
||||||
@ -5255,6 +5259,9 @@ class TestGenericHardwareManager(base.IronicAgentTest):
|
|||||||
self.assertEqual('ABCD', vendor_info.product_name)
|
self.assertEqual('ABCD', vendor_info.product_name)
|
||||||
self.assertEqual('1234', vendor_info.serial_number)
|
self.assertEqual('1234', vendor_info.serial_number)
|
||||||
self.assertEqual('ABCD', vendor_info.manufacturer)
|
self.assertEqual('ABCD', vendor_info.manufacturer)
|
||||||
|
self.assertEqual('BIOSVNDR', vendor_info.firmware.vendor)
|
||||||
|
self.assertEqual('03/30/2023', vendor_info.firmware.build_date)
|
||||||
|
self.assertEqual('1.2.3', vendor_info.firmware.version)
|
||||||
|
|
||||||
@mock.patch.object(il_utils, 'execute', autospec=True)
|
@mock.patch.object(il_utils, 'execute', autospec=True)
|
||||||
def test_get_system_vendor_info_failure(self, mocked_execute):
|
def test_get_system_vendor_info_failure(self, mocked_execute):
|
||||||
@ -5263,6 +5270,9 @@ class TestGenericHardwareManager(base.IronicAgentTest):
|
|||||||
self.assertEqual('', vendor_info.product_name)
|
self.assertEqual('', vendor_info.product_name)
|
||||||
self.assertEqual('', vendor_info.serial_number)
|
self.assertEqual('', vendor_info.serial_number)
|
||||||
self.assertEqual('', vendor_info.manufacturer)
|
self.assertEqual('', vendor_info.manufacturer)
|
||||||
|
self.assertEqual('', vendor_info.firmware.vendor)
|
||||||
|
self.assertEqual('', vendor_info.firmware.build_date)
|
||||||
|
self.assertEqual('', vendor_info.firmware.version)
|
||||||
|
|
||||||
@mock.patch.object(utils, 'get_agent_params',
|
@mock.patch.object(utils, 'get_agent_params',
|
||||||
lambda: {'BOOTIF': 'boot:if'})
|
lambda: {'BOOTIF': 'boot:if'})
|
||||||
|
@ -917,3 +917,15 @@ def rescan_device(device):
|
|||||||
except processutils.ProcessExecutionError as e:
|
except processutils.ProcessExecutionError as e:
|
||||||
LOG.warning('Something went wrong when waiting for udev '
|
LOG.warning('Something went wrong when waiting for udev '
|
||||||
'to settle. Error: %s', e)
|
'to settle. Error: %s', e)
|
||||||
|
|
||||||
|
|
||||||
|
def find_in_lshw(lshw, by_id):
|
||||||
|
"""Yield all suitable records from lshw."""
|
||||||
|
for child in lshw.get('children', ()):
|
||||||
|
lshw_id = child.get('id', '')
|
||||||
|
if isinstance(by_id, re.Pattern):
|
||||||
|
if by_id.match(lshw_id) is not None:
|
||||||
|
yield child
|
||||||
|
else:
|
||||||
|
if by_id == lshw_id:
|
||||||
|
yield child
|
||||||
|
5
releasenotes/notes/bmo-extra-147559c8d1776e8c.yaml
Normal file
5
releasenotes/notes/bmo-extra-147559c8d1776e8c.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
The hardware inventory now contains information about the system firmware:
|
||||||
|
vendor, version and the build date.
|
Loading…
x
Reference in New Issue
Block a user