Add to Redfish hardware inventory collection
Add to the information collected by Redfish hardware inspection from sushy, and store it in the documented hardware inventory format Change-Id: I651599b84e6b8901647960b719626489b000b65f
This commit is contained in:
parent
7f281392c2
commit
b3d7ba88d2
@ -41,6 +41,17 @@ if sushy:
|
||||
sushy.PROCESSOR_ARCH_OEM: 'oem'
|
||||
}
|
||||
|
||||
PROCESSOR_INSTRUCTION_SET_MAP = {
|
||||
sushy.InstructionSet.ARM_A32: 'arm',
|
||||
sushy.InstructionSet.ARM_A64: 'aarch64',
|
||||
sushy.InstructionSet.IA_64: 'ia64',
|
||||
sushy.InstructionSet.MIPS32: 'mips',
|
||||
sushy.InstructionSet.MIPS64: 'mips64',
|
||||
sushy.InstructionSet.OEM: None,
|
||||
sushy.InstructionSet.X86: 'i686',
|
||||
sushy.InstructionSet.X86_64: 'x86_64'
|
||||
}
|
||||
|
||||
BOOT_MODE_MAP = {
|
||||
sushy.BOOT_SOURCE_MODE_UEFI: boot_modes.UEFI,
|
||||
sushy.BOOT_SOURCE_MODE_BIOS: boot_modes.LEGACY_BIOS
|
||||
@ -102,34 +113,59 @@ class RedfishInspect(base.InspectInterface):
|
||||
# get the essential properties and update the node properties
|
||||
# with it.
|
||||
inspected_properties = task.node.properties
|
||||
inventory = {}
|
||||
|
||||
if system.memory_summary and system.memory_summary.size_gib:
|
||||
inspected_properties['memory_mb'] = str(
|
||||
system.memory_summary.size_gib * units.Ki)
|
||||
memory = system.memory_summary.size_gib * units.Ki
|
||||
inspected_properties['memory_mb'] = memory
|
||||
inventory['memory'] = {'physical_mb': memory}
|
||||
|
||||
if system.processors and system.processors.summary:
|
||||
arch = system.processors.summary[1]
|
||||
|
||||
if arch:
|
||||
try:
|
||||
inspected_properties['cpu_arch'] = CPU_ARCH_MAP[arch]
|
||||
|
||||
except KeyError:
|
||||
LOG.warning("Unknown CPU arch %(arch)s discovered "
|
||||
"for node %(node)s", {'node': task.node.uuid,
|
||||
'arch': arch})
|
||||
self._get_processor_info(task, system, inspected_properties, inventory)
|
||||
|
||||
# TODO(etingof): should we respect root device hints here?
|
||||
local_gb = self._detect_local_gb(task, system)
|
||||
|
||||
if local_gb:
|
||||
inspected_properties['local_gb'] = str(local_gb)
|
||||
|
||||
else:
|
||||
LOG.warning("Could not provide a valid storage size configured "
|
||||
"for node %(node)s. Assuming this is a disk-less node",
|
||||
{'node': task.node.uuid})
|
||||
inspected_properties['local_gb'] = '0'
|
||||
|
||||
if system.simple_storage:
|
||||
simple_storage_list = system.simple_storage.get_members()
|
||||
disks = list()
|
||||
|
||||
for simple_storage in simple_storage_list:
|
||||
for simple_storage_device in simple_storage.devices:
|
||||
disk = {}
|
||||
disk['name'] = simple_storage_device.name
|
||||
disk['size'] = simple_storage_device.capacity_bytes
|
||||
disks.append(disk)
|
||||
|
||||
inventory['disks'] = disks
|
||||
|
||||
if system.ethernet_interfaces and system.ethernet_interfaces.summary:
|
||||
inventory['interfaces'] = []
|
||||
mac_addresses = list(system.ethernet_interfaces.summary.keys())
|
||||
for mac_address in mac_addresses:
|
||||
inventory['interfaces'].append({'mac_address': mac_address})
|
||||
|
||||
system_vendor = {}
|
||||
if system.name:
|
||||
system_vendor['product_name'] = str(system.name)
|
||||
|
||||
if system.serial_number:
|
||||
system_vendor['serial_number'] = str(system.serial_number)
|
||||
|
||||
if system.manufacturer:
|
||||
system_vendor['manufacturer'] = str(system.manufacturer)
|
||||
|
||||
if system_vendor:
|
||||
inventory['system_vendor'] = system_vendor
|
||||
|
||||
if system.boot.mode:
|
||||
if not drivers_utils.get_node_capability(task.node, 'boot_mode'):
|
||||
capabilities = utils.get_updated_capabilities(
|
||||
@ -137,6 +173,8 @@ class RedfishInspect(base.InspectInterface):
|
||||
{'boot_mode': BOOT_MODE_MAP[system.boot.mode]})
|
||||
|
||||
inspected_properties['capabilities'] = capabilities
|
||||
inventory['boot'] = {'current_boot_mode':
|
||||
BOOT_MODE_MAP[system.boot.mode]}
|
||||
|
||||
valid_keys = self.ESSENTIAL_PROPERTIES
|
||||
missing_keys = valid_keys - set(inspected_properties)
|
||||
@ -182,6 +220,8 @@ class RedfishInspect(base.InspectInterface):
|
||||
LOG.warning("No port information discovered "
|
||||
"for node %(node)s", {'node': task.node.uuid})
|
||||
|
||||
inspect_utils.store_inspection_data(task.node,
|
||||
inventory, None, task.context)
|
||||
return states.MANAGEABLE
|
||||
|
||||
def _create_ports(self, task, system):
|
||||
@ -269,3 +309,34 @@ class RedfishInspect(base.InspectInterface):
|
||||
If cannot be determined, returns None.
|
||||
"""
|
||||
return None
|
||||
|
||||
def _get_processor_info(self, task, system, inspected_properties,
|
||||
inventory):
|
||||
if system.processors is None:
|
||||
return
|
||||
|
||||
cpu = {}
|
||||
if system.processors.summary:
|
||||
cpus, arch = system.processors.summary
|
||||
if cpus:
|
||||
inspected_properties['cpus'] = cpus
|
||||
cpu['count'] = cpus
|
||||
if arch:
|
||||
try:
|
||||
inspected_properties['cpu_arch'] = CPU_ARCH_MAP[arch]
|
||||
except KeyError:
|
||||
LOG.warning("Unknown CPU arch %(arch)s discovered "
|
||||
"for node %(node)s", {'node': task.node.uuid,
|
||||
'arch': arch})
|
||||
|
||||
processor = system.processors.get_members()[0]
|
||||
|
||||
if processor.model is not None:
|
||||
cpu['model_name'] = str(processor.model)
|
||||
if processor.max_speed_mhz is not None:
|
||||
cpu['frequency'] = processor.max_speed_mhz
|
||||
if processor.instruction_set is not None:
|
||||
cpu['architecture'] = PROCESSOR_INSTRUCTION_SET_MAP[
|
||||
processor.instruction_set]
|
||||
|
||||
inventory['cpu'] = cpu
|
||||
|
@ -58,7 +58,22 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
|
||||
system_mock.memory_summary.size_gib = 2
|
||||
|
||||
system_mock.processors.summary = '8', sushy.PROCESSOR_ARCH_MIPS
|
||||
mock_processor = mock.Mock()
|
||||
mock_processor.model = 'test'
|
||||
mock_processor.instruction_set = sushy.InstructionSet.X86
|
||||
mock_processor.max_speed_mhz = 1234
|
||||
system_mock.processors.get_members.return_value = [mock_processor]
|
||||
|
||||
system_mock.processors.summary = '8', sushy.PROCESSOR_ARCH_x86
|
||||
|
||||
mock_simple_storage_device = mock.Mock()
|
||||
mock_simple_storage_device.name = 'test-name'
|
||||
mock_simple_storage_device.capacity_bytes = '123'
|
||||
|
||||
mock_simple_storage = mock.Mock()
|
||||
mock_simple_storage.devices = [mock_simple_storage_device]
|
||||
system_mock.simple_storage.get_members.return_value = [
|
||||
mock_simple_storage]
|
||||
|
||||
system_mock.simple_storage.disks_sizes_bytes = (
|
||||
1 * units.Gi, units.Gi * 3, units.Gi * 5)
|
||||
@ -70,6 +85,12 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
'66:77:88:99:AA:BB': sushy.STATE_DISABLED,
|
||||
}
|
||||
|
||||
system_mock.name = 'System1'
|
||||
|
||||
system_mock.serial_number = '123456'
|
||||
|
||||
system_mock.manufacturer = 'Sushy Emulator'
|
||||
|
||||
return system_mock
|
||||
|
||||
def test_get_properties(self):
|
||||
@ -93,9 +114,9 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
mock_get_system):
|
||||
expected_properties = {
|
||||
'capabilities': 'boot_mode:uefi',
|
||||
'cpu_arch': 'mips', 'local_gb': '3', 'memory_mb': '2048'
|
||||
'cpu_arch': 'x86_64', 'cpus': '8',
|
||||
'local_gb': '3', 'memory_mb': 2048,
|
||||
}
|
||||
|
||||
self.init_system_mock(mock_get_system.return_value)
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
@ -105,6 +126,34 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
mock_get_system.assert_called_once_with(task.node)
|
||||
self.assertEqual(expected_properties, task.node.properties)
|
||||
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
|
||||
system_vendor = inventory['inventory']['system_vendor']
|
||||
expected_product_name = 'System1'
|
||||
expected_serial_number = '123456'
|
||||
expected_manufacturer = 'Sushy Emulator'
|
||||
self.assertEqual(expected_product_name,
|
||||
system_vendor['product_name'])
|
||||
self.assertEqual(expected_serial_number,
|
||||
system_vendor['serial_number'])
|
||||
self.assertEqual(expected_manufacturer,
|
||||
system_vendor['manufacturer'])
|
||||
|
||||
expected_interfaces = [{'mac_address': '00:11:22:33:44:55'},
|
||||
{'mac_address': '66:77:88:99:AA:BB'}]
|
||||
self.assertEqual(expected_interfaces,
|
||||
inventory['inventory']['interfaces'])
|
||||
|
||||
expected_cpu = {'count': '8', 'model_name': 'test',
|
||||
'frequency': 1234, 'architecture': 'i686'}
|
||||
self.assertEqual(expected_cpu,
|
||||
inventory['inventory']['cpu'])
|
||||
|
||||
expected_disks = [{'name': 'test-name', 'size': '123'}]
|
||||
self.assertEqual(expected_disks,
|
||||
inventory["inventory"]['disks'])
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
@ -120,7 +169,7 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
task, result)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_fail_missing_cpu(self, mock_get_system):
|
||||
def test_inspect_hardware_fail_missing_cpu_arch(self, mock_get_system):
|
||||
system_mock = self.init_system_mock(mock_get_system.return_value)
|
||||
system_mock.processors.summary = None, None
|
||||
|
||||
@ -131,7 +180,7 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
task.driver.inspect.inspect_hardware, task)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_ignore_missing_cpu(self, mock_get_system):
|
||||
def test_inspect_hardware_ignore_missing_cpu_count(self, mock_get_system):
|
||||
system_mock = self.init_system_mock(mock_get_system.return_value)
|
||||
system_mock.processors.summary = None, None
|
||||
|
||||
@ -139,11 +188,78 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
shared=True) as task:
|
||||
expected_properties = {
|
||||
'capabilities': 'boot_mode:uefi',
|
||||
'cpu_arch': 'x86_64', 'local_gb': '3', 'memory_mb': '2048'
|
||||
'cpu_arch': 'x86_64', 'local_gb': '3', 'memory_mb': 2048
|
||||
}
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertEqual(expected_properties, task.node.properties)
|
||||
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
self.assertNotIn('count', inventory['inventory']['cpu'])
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_ignore_missing_cpu_model(self, mock_get_system):
|
||||
system_mock = self.init_system_mock(mock_get_system.return_value)
|
||||
mock_processor = system_mock.processors.get_members.return_value[0]
|
||||
mock_processor.model = None
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
expected_properties = {
|
||||
'capabilities': 'boot_mode:uefi',
|
||||
'cpu_arch': 'x86_64', 'cpus': '8',
|
||||
'local_gb': '3', 'memory_mb': 2048
|
||||
}
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertEqual(expected_properties, task.node.properties)
|
||||
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
self.assertNotIn('model', inventory['inventory']['cpu'])
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_ignore_missing_cpu_frequency(self,
|
||||
mock_get_system):
|
||||
system_mock = self.init_system_mock(mock_get_system.return_value)
|
||||
mock_processor = system_mock.processors.get_members.return_value[0]
|
||||
mock_processor.max_speed_mhz = None
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
expected_properties = {
|
||||
'capabilities': 'boot_mode:uefi',
|
||||
'cpu_arch': 'x86_64', 'cpus': '8',
|
||||
'local_gb': '3', 'memory_mb': 2048
|
||||
}
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertEqual(expected_properties, task.node.properties)
|
||||
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
self.assertNotIn('frequency', inventory['inventory']['cpu'])
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_ignore_missing_cpu_instruction_set(
|
||||
self,
|
||||
mock_get_system):
|
||||
system_mock = self.init_system_mock(mock_get_system.return_value)
|
||||
mock_processor = system_mock.processors.get_members.return_value[0]
|
||||
mock_processor.instruction_set = None
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
expected_properties = {
|
||||
'capabilities': 'boot_mode:uefi',
|
||||
'cpu_arch': 'x86_64', 'cpus': '8',
|
||||
'local_gb': '3', 'memory_mb': 2048
|
||||
}
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertEqual(expected_properties, task.node.properties)
|
||||
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
self.assertNotIn('architecture', inventory['inventory']['cpu'])
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_ignore_missing_local_gb(self, mock_get_system):
|
||||
system_mock = self.init_system_mock(mock_get_system.return_value)
|
||||
@ -154,11 +270,32 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
shared=True) as task:
|
||||
expected_properties = {
|
||||
'capabilities': 'boot_mode:uefi',
|
||||
'cpu_arch': 'mips', 'local_gb': '0', 'memory_mb': '2048'
|
||||
'cpu_arch': 'x86_64', 'cpus': '8',
|
||||
'local_gb': '0', 'memory_mb': 2048
|
||||
}
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertEqual(expected_properties, task.node.properties)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_ignore_missing_simple_storage(self,
|
||||
mock_get_system):
|
||||
system_mock = self.init_system_mock(mock_get_system.return_value)
|
||||
system_mock.simple_storage = None
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
expected_properties = {
|
||||
'capabilities': 'boot_mode:uefi',
|
||||
'cpu_arch': 'x86_64', 'cpus': '8',
|
||||
'local_gb': '3', 'memory_mb': 2048
|
||||
}
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertEqual(expected_properties, task.node.properties)
|
||||
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
self.assertNotIn('disks', inventory['inventory'])
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_fail_missing_memory_mb(self, mock_get_system):
|
||||
system_mock = self.init_system_mock(mock_get_system.return_value)
|
||||
@ -179,11 +316,16 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
shared=True) as task:
|
||||
expected_properties = {
|
||||
'capabilities': 'boot_mode:uefi',
|
||||
'cpu_arch': 'mips', 'local_gb': '3', 'memory_mb': '4096'
|
||||
'cpu_arch': 'x86_64', 'cpus': '8',
|
||||
'local_gb': '3', 'memory_mb': '4096'
|
||||
}
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertEqual(expected_properties, task.node.properties)
|
||||
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
self.assertNotIn('memory', inventory['inventory'])
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@mock.patch.object(inspect_utils, 'create_ports_if_not_exist',
|
||||
autospec=True)
|
||||
@ -197,6 +339,10 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertFalse(mock_create_ports_if_not_exist.called)
|
||||
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
self.assertNotIn('interfaces', inventory['inventory'])
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_preserve_boot_mode(self, mock_get_system):
|
||||
self.init_system_mock(mock_get_system.return_value)
|
||||
@ -208,11 +354,19 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
}
|
||||
expected_properties = {
|
||||
'capabilities': 'boot_mode:bios',
|
||||
'cpu_arch': 'mips', 'local_gb': '3', 'memory_mb': '2048'
|
||||
'cpu_arch': 'x86_64', 'cpus': '8',
|
||||
'local_gb': '3', 'memory_mb': 2048
|
||||
}
|
||||
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertEqual(expected_properties, task.node.properties)
|
||||
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
expected_boot_mode = {'current_boot_mode': 'uefi'}
|
||||
self.assertEqual(expected_boot_mode,
|
||||
inventory['inventory']['boot'])
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_ignore_missing_boot_mode(self, mock_get_system):
|
||||
system_mock = self.init_system_mock(mock_get_system.return_value)
|
||||
@ -221,10 +375,14 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
expected_properties = {
|
||||
'cpu_arch': 'mips', 'local_gb': '3', 'memory_mb': '2048'
|
||||
'cpu_arch': 'x86_64', 'cpus': '8',
|
||||
'local_gb': '3', 'memory_mb': 2048
|
||||
}
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
self.assertEqual(expected_properties, task.node.properties)
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
self.assertNotIn('boot', inventory['inventory'])
|
||||
|
||||
@mock.patch.object(objects.Port, 'list_by_node_id') # noqa
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
@ -335,6 +493,21 @@ class RedfishInspectTestCase(db_base.DbTestCase):
|
||||
for port in ports:
|
||||
self.assertIn(port.address, expected_port_mac_list)
|
||||
|
||||
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||
def test_inspect_hardware_ignore_missing_system_vendor(self,
|
||||
mock_get_system):
|
||||
system_mock = self.init_system_mock(mock_get_system.return_value)
|
||||
system_mock.name = None
|
||||
system_mock.serial_number = None
|
||||
system_mock.manufacturer = None
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=True) as task:
|
||||
task.driver.inspect.inspect_hardware(task)
|
||||
inventory = inspect_utils.get_inspection_data(task.node,
|
||||
self.context)
|
||||
self.assertNotIn('system_vendor', inventory['inventory'])
|
||||
|
||||
def test_get_pxe_port_macs(self):
|
||||
expected_properties = None
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
|
@ -0,0 +1,8 @@
|
||||
---
|
||||
features:
|
||||
- Uses Redfish to collect the available hardware inventory information and
|
||||
stores it in the right format. Information collected includes cpu
|
||||
information including "count", "architecture", "model_name", and
|
||||
"frequency", disk "size" (in bytes), interface "mac_address",
|
||||
"system_vendor" information including "product_name", "serial_number" and
|
||||
"manufacturer", and "current_boot_mode".
|
Loading…
Reference in New Issue
Block a user