diff --git a/rsd_lib/resources/v2_1/system/processor.py b/rsd_lib/resources/v2_1/system/processor.py new file mode 100644 index 0000000..85e07a3 --- /dev/null +++ b/rsd_lib/resources/v2_1/system/processor.py @@ -0,0 +1,150 @@ +# 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. + +from sushy.resources import base + +from rsd_lib import utils as rsd_lib_utils + + +class StatusField(base.CompositeField): + """This Field describes the status of a resource and its children. + + This field shall contain any state or health properties of a resource. + """ + health = base.Field('Health') + """Represents health of resource w/o considering its dependent resources""" + + health_rollup = base.Field('HealthRollup') + """Represents health state of resource and its dependent resources""" + + state = base.Field('State') + """Indicates the known state of the resource, such as if it is enabled.""" + + +class ProcessorIdField(base.CompositeField): + + effective_family = base.Field('EffectiveFamily') + """The processor effective family""" + + effective_model = base.Field('EffectiveModel') + """The processor effective model""" + + identification_registers = base.Field('IdentificationRegisters') + """The processor identification registers""" + + microcode_info = base.Field('MicrocodeInfo') + """The processor microcode info""" + + step = base.Field('Step') + """The processor stepping""" + + vendor_id = base.Field('VendorId') + """The processor vendor id""" + + +class IntelRackScaleField(base.CompositeField): + + brand = base.Field("Brand") + """This property shall represent the brand of processor""" + + capabilities = base.Field("Capabilities") + """This property shall represent array of processor capabilities + (like reported in /proc/cpuinfo flags) + """ + + +class OemField(base.CompositeField): + + intel_rackscale = IntelRackScaleField("Intel_RackScale") + """Intel Rack Scale Design extensions ("Intel_RackScale" object)""" + + +class Processor(base.ResourceBase): + + identity = base.Field('Id', required=True) + """The processor identity string""" + + name = base.Field('Name') + """The processor name string""" + + description = base.Field('Description') + """The processor description string""" + + socket = base.Field('Socket') + """The socket or location of the processor""" + + processor_type = base.Field('ProcessorType') + """The type of processor""" + + processor_architecture = base.Field('ProcessorArchitecture') + """The architecture of the processor""" + + instruction_set = base.Field('InstructionSet') + """The instruction set of the processor""" + + manufacturer = base.Field('Manufacturer') + """The processor manufacturer""" + + model = base.Field('Model') + """The product model number of this device""" + + max_speed_mhz = base.Field( + 'MaxSpeedMHz', adapter=rsd_lib_utils.num_or_none) + """The maximum clock speed of the processor in MHz.""" + + processor_id = ProcessorIdField('ProcessorId') + """The processor id""" + + status = StatusField('Status') + """The processor status""" + + total_cores = base.Field( + 'TotalCores', adapter=rsd_lib_utils.num_or_none) + """The total number of cores contained in this processor""" + + total_threads = base.Field( + 'TotalThreads', adapter=rsd_lib_utils.num_or_none) + """The total number of execution threads supported by this processor""" + + oem = OemField("Oem") + """Oem extension object""" + + def __init__(self, connector, identity, redfish_version=None): + """A class representing a Processor + + :param connector: A Connector instance + :param identity: The identity of the processor + :param redfish_version: The version of RedFish. Used to construct + the object according to schema of the given version. + """ + super(Processor, self).__init__(connector, identity, redfish_version) + + +class ProcessorCollection(base.ResourceCollectionBase): + + @property + def _resource_type(self): + return Processor + + def __init__(self, connector, path, redfish_version=None): + """A class representing a ProcessorCollection + + :param connector: A Connector instance + :param path: The canonical path to the Processor collection resource + :param redfish_version: The version of RedFish. Used to construct + the object according to schema of the given version. + """ + super(ProcessorCollection, self).__init__(connector, path, + redfish_version) diff --git a/rsd_lib/resources/v2_1/system/system.py b/rsd_lib/resources/v2_1/system/system.py index 2139ed9..11fca48 100644 --- a/rsd_lib/resources/v2_1/system/system.py +++ b/rsd_lib/resources/v2_1/system/system.py @@ -21,12 +21,30 @@ from rsd_lib.resources.v2_1.system import memory from rsd_lib.resources.v2_1.system import network_interface from rsd_lib.resources.v2_1.system import pcie_device from rsd_lib.resources.v2_1.system import pcie_function +from rsd_lib.resources.v2_1.system import processor from rsd_lib.resources.v2_1.system import storage_subsystem from rsd_lib import utils as rsd_lib_utils class System(system.System): + def _get_processor_collection_path(self): + """Helper function to find the ProcessorCollection path""" + return utils.get_sub_resource_path_by(self, 'Processors') + + @property + @utils.cache_it + def processors(self): + """Property to reference `ProcessorCollection` instance + + It is set once when the first time it is queried. On refresh, + this property is marked as stale (greedy-refresh not done). + Here the actual refresh of the sub-resource happens, if stale. + """ + return processor.ProcessorCollection( + self._conn, self._get_processor_collection_path(), + redfish_version=self.redfish_version) + def _get_memory_collection_path(self): """Helper function to find the memory path""" return utils.get_sub_resource_path_by(self, 'Memory') diff --git a/rsd_lib/tests/unit/json_samples/v2_1/processor.json b/rsd_lib/tests/unit/json_samples/v2_1/processor.json index 19ce642..9003fde 100644 --- a/rsd_lib/tests/unit/json_samples/v2_1/processor.json +++ b/rsd_lib/tests/unit/json_samples/v2_1/processor.json @@ -1,5 +1,8 @@ { - "@odata.type": "#Processor.v1_0_2.Processor", + "@odata.context": "/redfish/v1/$metadata#Systems/Members/1/Processors/Members/$entity", + "@odata.id": "/redfish/v1/Systems/System1/Processors/CPU1", + "@odata.type": "#Processor.v1_0_0.Processor", + "Name": "Processor", "Id": "CPU1", "Socket": "CPU 1", "ProcessorType": "CPU", @@ -7,8 +10,8 @@ "InstructionSet": "x86-64", "Manufacturer": "Intel(R) Corporation", "Model": "Multi-Core Intel(R) Xeon(R) processor 7xxx Series", - "ProcessorID": { - "VendorID": "GenuineIntel", + "ProcessorId": { + "VendorId": "GenuineIntel", "IdentificationRegisters": "0x34AC34DC8901274A", "EffectiveFamily": "0x42", "EffectiveModel": "0x61", @@ -20,9 +23,18 @@ "TotalThreads": 16, "Status": { "State": "Enabled", - "Health": "OK" + "Health": "OK", + "HealthRollup": null }, - "@odata.context": "/redfish/v1/$metadata#Systems/Members/437XR1138R2/Processors/Members/$entity", - "@odata.id": "/redfish/v1/Systems/437XR1138R2/Processors/CPU1", - "@Redfish.Copyright": "Copyright 2014-2016 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." -} + "Oem": { + "Intel_RackScale": { + "@odata.type": "#Intel.Oem.Processor", + "Brand": "E5", + "Capabilities": [ + "sse", + "sse2", + "sse3" + ] + } + } +} \ No newline at end of file diff --git a/rsd_lib/tests/unit/json_samples/v2_1/processor2.json b/rsd_lib/tests/unit/json_samples/v2_1/processor2.json deleted file mode 100644 index a050851..0000000 --- a/rsd_lib/tests/unit/json_samples/v2_1/processor2.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "@odata.type": "#Processor.v1_0_2.Processor", - "Id": "CPU2", - "Socket": "CPU 2", - "ProcessorType": "CPU", - "Status": { - "State": "Absent" - }, - "@odata.context": "/redfish/v1/$metadata#Systems/Members/437XR1138R2/Processors/Members/$entity", - "@odata.id": "/redfish/v1/Systems/437XR1138R2/Processors/CPU2", - "@Redfish.Copyright": "Copyright 2014-2016 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." -} diff --git a/rsd_lib/tests/unit/json_samples/v2_1/processor_collection.json b/rsd_lib/tests/unit/json_samples/v2_1/processor_collection.json index 8373180..d4966d8 100644 --- a/rsd_lib/tests/unit/json_samples/v2_1/processor_collection.json +++ b/rsd_lib/tests/unit/json_samples/v2_1/processor_collection.json @@ -4,13 +4,13 @@ "Members@odata.count": 2, "Members": [ { - "@odata.id": "/redfish/v1/Systems/437XR1138R2/Processors/CPU1" + "@odata.id": "/redfish/v1/Systems/System1/Processors/CPU1" }, { - "@odata.id": "/redfish/v1/Systems/437XR1138R2/Processors/CPU2" + "@odata.id": "/redfish/v1/Systems/System1/Processors/CPU2" } ], - "@odata.context": "/redfish/v1/$metadata#Systems/Links/Members/437XR1138R2/Processors/#entity", - "@odata.id": "/redfish/v1/Systems/437XR1138R2/Processors", + "@odata.context": "/redfish/v1/$metadata#Systems/Links/Members/System1/Processors/#entity", + "@odata.id": "/redfish/v1/Systems/System1/Processors", "@Redfish.Copyright": "Copyright 2014-2016 Distributed Management Task Force, Inc. (DMTF). For the full DMTF copyright policy, see http://www.dmtf.org/about/policies/copyright." } diff --git a/rsd_lib/tests/unit/resources/v2_1/system/test_processor.py b/rsd_lib/tests/unit/resources/v2_1/system/test_processor.py new file mode 100644 index 0000000..4190235 --- /dev/null +++ b/rsd_lib/tests/unit/resources/v2_1/system/test_processor.py @@ -0,0 +1,114 @@ +# 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 json +import mock +import testtools + +from rsd_lib.resources.v2_1.system import processor + + +class ProcessorTestCase(testtools.TestCase): + + def setUp(self): + super(ProcessorTestCase, self).setUp() + self.conn = mock.Mock() + with open( + 'rsd_lib/tests/unit/json_samples/v2_1/processor.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.processor_inst = processor.Processor( + self.conn, '/redfish/v1/Systems/System1/Processors/CPU1', + redfish_version='1.0.2') + + def test__parse_attributes(self): + self.processor_inst._parse_attributes() + 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', 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( + '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(None, 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) + + +class ProcessorCollectionTestCase(testtools.TestCase): + + def setUp(self): + super(ProcessorCollectionTestCase, self).setUp() + self.conn = mock.Mock() + with open('rsd_lib/tests/unit/json_samples/v2_1/' + 'processor_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + self.processor_col = processor.ProcessorCollection( + self.conn, '/redfish/v1/Systems/System1/Processors', + redfish_version='1.1.0') + + def test__parse_attributes(self): + self.processor_col._parse_attributes() + self.assertEqual('1.1.0', self.processor_col.redfish_version) + self.assertEqual(('/redfish/v1/Systems/System1/Processors/CPU1', + '/redfish/v1/Systems/System1/Processors/CPU2'), + self.processor_col.members_identities) + + @mock.patch.object(processor, 'Processor', autospec=True) + def test_get_member(self, mock_processor): + self.processor_col.get_member( + '/redfish/v1/Systems/System1/Processors/CPU1') + mock_processor.assert_called_once_with( + self.processor_col._conn, + '/redfish/v1/Systems/System1/Processors/CPU1', + redfish_version=self.processor_col.redfish_version) + + @mock.patch.object(processor, 'Processor', autospec=True) + def test_get_members(self, mock_processor): + members = self.processor_col.get_members() + calls = [ + mock.call(self.processor_col._conn, + '/redfish/v1/Systems/System1/Processors/CPU1', + redfish_version=self.processor_col.redfish_version), + mock.call(self.processor_col._conn, + '/redfish/v1/Systems/System1/Processors/CPU2', + redfish_version=self.processor_col.redfish_version) + ] + mock_processor.assert_has_calls(calls) + self.assertIsInstance(members, list) + self.assertEqual(2, len(members)) diff --git a/rsd_lib/tests/unit/resources/v2_1/system/test_system.py b/rsd_lib/tests/unit/resources/v2_1/system/test_system.py index 3e3c166..50f129e 100644 --- a/rsd_lib/tests/unit/resources/v2_1/system/test_system.py +++ b/rsd_lib/tests/unit/resources/v2_1/system/test_system.py @@ -25,6 +25,7 @@ from rsd_lib.resources.v2_1.system import memory from rsd_lib.resources.v2_1.system import network_interface from rsd_lib.resources.v2_1.system import pcie_device from rsd_lib.resources.v2_1.system import pcie_function +from rsd_lib.resources.v2_1.system import processor from rsd_lib.resources.v2_1.system import storage_subsystem from rsd_lib.resources.v2_1.system import system @@ -46,6 +47,62 @@ class SystemTestCase(testtools.TestCase): self.assertIsInstance(self.system_inst, system.System) self.assertIsInstance(self.system_inst, sushy_system.System) + def test__get_processor_collection_path(self): + self.assertEqual('/redfish/v1/Systems/437XR1138R2/Processors', + self.system_inst._get_processor_collection_path()) + + def test__get_processor_collection_path_missing_attr(self): + self.system_inst._json.pop('Processors') + with self.assertRaisesRegex( + exceptions.MissingAttributeError, 'attribute Processors'): + self.system_inst._get_processor_collection_path() + + def test_processors(self): + # | GIVEN | + self.conn.get.return_value.json.reset_mock() + with open('rsd_lib/tests/unit/json_samples/v2_1/' + 'processor_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN | + actual_processor_col = self.system_inst.processors + # | THEN | + self.assertIsInstance(actual_processor_col, + processor.ProcessorCollection) + self.conn.get.return_value.json.assert_called_once_with() + + # reset mock + self.conn.get.return_value.json.reset_mock() + # | WHEN & THEN | + # tests for same object on invoking subsequently + self.assertIs(actual_processor_col, + self.system_inst.processors) + self.conn.get.return_value.json.assert_not_called() + + def test_processor_on_refresh(self): + # | GIVEN | + with open('rsd_lib/tests/unit/json_samples/v2_1/' + 'processor_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.system_inst.processors, + processor.ProcessorCollection) + + # On refreshing the system instance... + with open('rsd_lib/tests/unit/json_samples/v2_1/system.json', + 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.system_inst.invalidate() + self.system_inst.refresh(force=False) + + # | GIVEN | + with open('rsd_lib/tests/unit/json_samples/v2_1/' + 'processor_collection.json', 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.system_inst.processors, + processor.ProcessorCollection) + def test__get_memory_collection_path(self): self.assertEqual('/redfish/v1/Systems/437XR1138R2/Memory', self.system_inst._get_memory_collection_path()) diff --git a/rsd_lib/tests/unit/resources/v2_2/ethernet_switch/test_metrics.py b/rsd_lib/tests/unit/resources/v2_2/ethernet_switch/test_metrics.py index b1d1ba6..40f2e22 100644 --- a/rsd_lib/tests/unit/resources/v2_2/ethernet_switch/test_metrics.py +++ b/rsd_lib/tests/unit/resources/v2_2/ethernet_switch/test_metrics.py @@ -15,9 +15,10 @@ import json import mock -from rsd_lib.resources.v2_2.ethernet_switch import metrics import testtools +from rsd_lib.resources.v2_2.ethernet_switch import metrics + class MetricsTestCase(testtools.TestCase):