diff --git a/rsd_lib/base.py b/rsd_lib/base.py new file mode 100644 index 0000000..53ea057 --- /dev/null +++ b/rsd_lib/base.py @@ -0,0 +1,52 @@ +# Copyright 2019 Intel, Inc. +# All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import copy + +from sushy.resources import base + +from rsd_lib import utils as rsd_lib_utils + + +class DynamicField(base.Field): + """Base class for fields consisting of several dynamic attributes.""" + + def __init__(self, *args, **kwargs): + super(DynamicField, self).__init__(*args, **kwargs) + self._subfields = None + + def _load(self, body, resource, nested_in=None): + """Load the all attributes. + + :param body: parent JSON body. + :param resource: parent resource. + :param nested_in: parent resource name (for error reporting only). + :returns: a new object with sub-fields attached to it. + """ + nested_in = (nested_in or []) + self._path + value = super(DynamicField, self)._load(body, resource) + if value is None: + return None + + # We need a new instance, as this method is called a singleton instance + # that is attached to a class (not instance) of a resource or another + # CompositeField. We don't want to end up modifying this instance. + instance = copy.copy(self) + for name, attr in value.items(): + setattr( + instance, rsd_lib_utils.camelcase_to_underscore_joined(name), + attr) + + return instance diff --git a/rsd_lib/resources/v2_2/system/processor.py b/rsd_lib/resources/v2_2/system/processor.py index f9ba4c4..9d77f13 100644 --- a/rsd_lib/resources/v2_2/system/processor.py +++ b/rsd_lib/resources/v2_2/system/processor.py @@ -13,17 +13,80 @@ # License for the specific language governing permissions and limitations # under the License. -from sushy.resources.system import processor +from sushy.resources import base from sushy import utils -from rsd_lib import common as rsd_lib_common +from rsd_lib import base as rsd_lib_base +from rsd_lib.resources.v2_1.system import processor from rsd_lib.resources.v2_2.system import processor_metrics +from rsd_lib import utils as rsd_lib_utils + + +class OnPackageMemoryField(base.ListField): + + memory_type = base.Field('Type') + """Type of memory""" + + capacity_mb = base.Field('CapacityMB', adapter=rsd_lib_utils.num_or_none) + """Memory capacity""" + + speed_mhz = base.Field('SpeedMHz', adapter=rsd_lib_utils.num_or_none) + """Memory speed""" + + +class FpgaField(base.CompositeField): + + fpga_type = base.Field('Type') + """Type of FPGA""" + + bit_stream_version = base.Field('BitStreamVersion') + """Version of BitStream loaded on FPGA""" + + hssi_configuration = base.Field('HSSIConfiguration') + """High Speed Serial Interface configuration""" + + hssi_sideband = base.Field('HSSISideband') + """High Speed Serial Interface sideband interface type""" + + reconfiguration_slots = base.Field( + 'ReconfigurationSlots', adapter=rsd_lib_utils.num_or_none) + """Number of supported reconfiguration slots""" + + +class IntelRackScaleField(processor.IntelRackScaleField): + + on_package_memory = OnPackageMemoryField('OnPackageMemory') + """An array of references to the endpoints that connect to this processor + """ + + thermal_design_power_watt = base.Field( + 'ThermalDesignPowerWatt', adapter=rsd_lib_utils.num_or_none) + """Thermal Design Power (TDP) of this processor""" + + metrics = base.Field( + 'Metrics', adapter=rsd_lib_utils.get_resource_identity) + """A reference to the Metrics associated with this Processor""" + + extended_identification_registers = rsd_lib_base.DynamicField( + 'ExtendedIdentificationRegisters') + """Extended contents of the Identification Registers (CPUID) for this + processor + """ + + fpga = FpgaField('FPGA') + """FPGA specific properties for FPGA ProcessorType""" + + +class OemField(base.CompositeField): + + intel_rackscale = IntelRackScaleField('Intel_RackScale') + """Intel Rack Scale Design extensions ('Intel_RackScale' object)""" class Processor(processor.Processor): - status = rsd_lib_common.StatusField('Status') - """The processor status""" + oem = OemField('Oem') + """Oem extension object""" def _get_metrics_path(self): """Helper function to find the System process metrics path""" diff --git a/rsd_lib/tests/unit/json_samples/v2_2/processor.json b/rsd_lib/tests/unit/json_samples/v2_2/processor.json index d4ecfa9..d32aa6d 100644 --- a/rsd_lib/tests/unit/json_samples/v2_2/processor.json +++ b/rsd_lib/tests/unit/json_samples/v2_2/processor.json @@ -51,6 +51,13 @@ "Metrics": { "@odata.id": "/redfish/v1/Systems/System1/Processors/CPU1/Metrics" }, + "FPGA": { + "Type": "Integrated", + "BitStreamVersion": "Blue1", + "HSSIConfiguration": "4x10G", + "HSSISideband": "I2C", + "ReconfigurationSlots": 1 + }, "ExtendedIdentificationRegisters": { "EAX_00h": "0x0429943FFFFFFFFF", "EAX_01h": "0x0429943FFFFFFFFF", diff --git a/rsd_lib/tests/unit/resources/v2_2/system/test_processor.py b/rsd_lib/tests/unit/resources/v2_2/system/test_processor.py index 36d2147..91c4f9c 100644 --- a/rsd_lib/tests/unit/resources/v2_2/system/test_processor.py +++ b/rsd_lib/tests/unit/resources/v2_2/system/test_processor.py @@ -40,21 +40,146 @@ class ProcessorTestCase(testtools.TestCase): self.processor_inst._parse_attributes() self.assertEqual('1.1.0', self.processor_inst.redfish_version) self.assertEqual('CPU1', self.processor_inst.identity) + self.assertEqual('Processor', self.processor_inst.name) + self.assertEqual(None, self.processor_inst.description) self.assertEqual('CPU 1', self.processor_inst.socket) self.assertEqual('CPU', self.processor_inst.processor_type) - self.assertEqual('x86 or x86-64', - self.processor_inst.processor_architecture) + self.assertEqual('x86', self.processor_inst.processor_architecture) self.assertEqual('x86-64', self.processor_inst.instruction_set) self.assertEqual('Intel(R) Corporation', self.processor_inst.manufacturer) self.assertEqual('Multi-Core Intel(R) Xeon(R) processor 7xxx Series', self.processor_inst.model) self.assertEqual(3700, self.processor_inst.max_speed_mhz) - self.assertEqual(8, self.processor_inst.total_cores) - self.assertEqual(16, self.processor_inst.total_threads) - self.assertEqual('Enabled', self.processor_inst.status.state) + self.assertEqual( + '0x42', self.processor_inst.processor_id.effective_family) + self.assertEqual( + '0x61', self.processor_inst.processor_id.effective_model) + self.assertEqual( + '0x34AC34DC8901274A', + self.processor_inst.processor_id.identification_registers) + self.assertEqual( + '0x429943', self.processor_inst.processor_id.microcode_info) + self.assertEqual('0x1', self.processor_inst.processor_id.step) + self.assertEqual( + 'GenuineIntel', self.processor_inst.processor_id.vendor_id) self.assertEqual('OK', self.processor_inst.status.health) self.assertEqual('OK', self.processor_inst.status.health_rollup) + self.assertEqual('Enabled', self.processor_inst.status.state) + self.assertEqual(8, self.processor_inst.total_cores) + self.assertEqual(16, self.processor_inst.total_threads) + self.assertEqual('E5', self.processor_inst.oem.intel_rackscale.brand) + self.assertEqual( + ['sse', 'sse2', 'sse3'], + self.processor_inst.oem.intel_rackscale.capabilities) + self.assertEqual( + 'L2Cache', + self.processor_inst.oem.intel_rackscale.on_package_memory[0]. + memory_type) + self.assertEqual( + 2, + self.processor_inst.oem.intel_rackscale.on_package_memory[0]. + capacity_mb) + self.assertEqual( + None, + self.processor_inst.oem.intel_rackscale.on_package_memory[0]. + speed_mhz) + self.assertEqual( + 'L3Cache', + self.processor_inst.oem.intel_rackscale.on_package_memory[1]. + memory_type) + self.assertEqual( + 20, + self.processor_inst.oem.intel_rackscale.on_package_memory[1]. + capacity_mb) + self.assertEqual( + None, + self.processor_inst.oem.intel_rackscale.on_package_memory[1]. + speed_mhz) + self.assertEqual( + 160, + self.processor_inst.oem.intel_rackscale.thermal_design_power_watt) + self.assertEqual( + '/redfish/v1/Systems/System1/Processors/CPU1/Metrics', + self.processor_inst.oem.intel_rackscale.metrics) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_00h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_01h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_02h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_03h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_04h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_05h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_07h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_80000000h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_80000001h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_80000002h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_80000003h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_80000004h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_80000005h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_80000006h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_80000007h) + self.assertEqual( + "0x0429943FFFFFFFFF", + self.processor_inst.oem.intel_rackscale. + extended_identification_registers.eax_80000008h) + self.assertEqual( + 'Integrated', + self.processor_inst.oem.intel_rackscale.fpga.fpga_type) + self.assertEqual( + 'Blue1', + self.processor_inst.oem.intel_rackscale.fpga.bit_stream_version) + self.assertEqual( + '4x10G', + self.processor_inst.oem.intel_rackscale.fpga.hssi_configuration) + self.assertEqual( + 'I2C', self.processor_inst.oem.intel_rackscale.fpga.hssi_sideband) + self.assertEqual( + 1, + self.processor_inst.oem.intel_rackscale.fpga.reconfiguration_slots) def test__get_metrics_path(self): self.assertEqual('/redfish/v1/Systems/System1/Processors/CPU1/Metrics', diff --git a/rsd_lib/tests/unit/test_utils.py b/rsd_lib/tests/unit/test_utils.py index a888891..f023212 100644 --- a/rsd_lib/tests/unit/test_utils.py +++ b/rsd_lib/tests/unit/test_utils.py @@ -76,3 +76,24 @@ class UtilsTestCase(testtools.TestCase): mock_resource, 'Links' ) + + def test_camelcase_to_underscore_joined(self): + input_vs_expected = [ + ('GarbageCollection', 'garbage_collection'), + ('DD', 'dd'), + ('rr', 'rr'), + ('AABbbC', 'aa_bbb_c'), + ('AABbbCCCDd', 'aa_bbb_ccc_dd'), + ('Manager', 'manager'), + ('EthernetInterfaceCollection', 'ethernet_interface_collection'), + (' ', ' '), + ] + for inp, exp in input_vs_expected: + self.assertEqual( + exp, rsd_lib_utils.camelcase_to_underscore_joined(inp)) + + def test_camelcase_to_underscore_joined_fails_with_empty_string(self): + self.assertRaisesRegex( + ValueError, + '"camelcase_str" cannot be empty', + rsd_lib_utils.camelcase_to_underscore_joined, '') diff --git a/rsd_lib/utils.py b/rsd_lib/utils.py index c2bde4f..cdd3ef9 100644 --- a/rsd_lib/utils.py +++ b/rsd_lib/utils.py @@ -65,3 +65,29 @@ def get_sub_resource_path_list_by(resource, subresource_name): attribute='/'.join(subresource_name), resource=resource.path) return [item.get('@data.id') for item in body] + + +# TODO(linyang): Use the same function in sushy utils after sushy 1.8.1 +# is released +def camelcase_to_underscore_joined(camelcase_str): + """Convert camelCase string to underscore_joined string + + :param camelcase_str: The camelCase string + :returns: the equivalent underscore_joined string + """ + if not camelcase_str: + raise ValueError('"camelcase_str" cannot be empty') + + r = camelcase_str[0].lower() + for i, letter in enumerate(camelcase_str[1:], 1): + if letter.isupper(): + try: + if (camelcase_str[i - 1].islower() + or camelcase_str[i + 1].islower()): + r += '_' + except IndexError: + pass + + r += letter.lower() + + return r