diff --git a/rsd_lib/resources/v2_2/chassis/__init__.py b/rsd_lib/resources/v2_2/chassis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsd_lib/resources/v2_2/chassis/chassis.py b/rsd_lib/resources/v2_2/chassis/chassis.py new file mode 100644 index 0000000..2ab5d17 --- /dev/null +++ b/rsd_lib/resources/v2_2/chassis/chassis.py @@ -0,0 +1,45 @@ +# 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 import utils + +from rsd_lib.resources.v2_1.chassis import chassis +from rsd_lib.resources.v2_2.chassis import power + + +class Chassis(chassis.Chassis): + """Chassis resource class + + A Chassis represents the physical components for any system. This + resource represents the sheet-metal confined spaces and logical zones + like racks, enclosures, chassis and all other containers. Subsystems + (like sensors), which operate outside of a system's data plane (meaning + the resources are not accessible to software running on the system) are + linked either directly or indirectly through this resource. + """ + + @property + @utils.cache_it + def power(self): + """Property to provide reference to `Power` instance + + It is calculated once when it is queried for the first time. On + refresh, this property is reset. + """ + return power.Power( + self._conn, + utils.get_sub_resource_path_by(self, "Power"), + redfish_version=self.redfish_version, + ) diff --git a/rsd_lib/resources/v2_2/chassis/power.py b/rsd_lib/resources/v2_2/chassis/power.py new file mode 100644 index 0000000..168c021 --- /dev/null +++ b/rsd_lib/resources/v2_2/chassis/power.py @@ -0,0 +1,61 @@ +# 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.resources.v2_1.chassis import power +from rsd_lib import utils as rsd_lib_utils + + +class IntelRackScaleField(base.CompositeField): + """Power field + + Extended Power resource. + """ + + input_ac_power_watts = base.Field( + "InputACPowerWatts", adapter=rsd_lib_utils.num_or_none + ) + """The global power level on AC side in Watts.""" + + +class OemField(base.CompositeField): + + intel_rackscale = IntelRackScaleField("Intel_RackScale") + """Intel Rack Scale Design specific properties.""" + + +class PowerSupplyCollectionField(power.PowerSupplyCollectionField): + """PowerSupply field + + Details of a power supplies associated with this system or device + """ + + indicator_led = base.Field("IndicatorLED") + """The state of the indicator LED, used to identify the power supply.""" + + +class Power(power.Power): + """Power resource class + + This is the schema definition for the Power Metrics. It represents the + properties for Power Consumption and Power Limiting. + """ + + power_supplies = PowerSupplyCollectionField("PowerSupplies") + """Details of the power supplies associated with this system or device""" + + oem = OemField("Oem") + """Oem specific properties.""" diff --git a/rsd_lib/tests/unit/json_samples/v2_2/chassis.json b/rsd_lib/tests/unit/json_samples/v2_2/chassis.json new file mode 100644 index 0000000..5a0cbc2 --- /dev/null +++ b/rsd_lib/tests/unit/json_samples/v2_2/chassis.json @@ -0,0 +1,118 @@ +{ + "@odata.context": "/redfish/v1/$metadata#Chassis/Members/$entity", + "@odata.id": "/redfish/v1/Chassis/1", + "@odata.type": "#Chassis.v1_3_0.Chassis", + "Id": "1", + "ChassisType": "RackMount", + "Name": "name-as-string", + "Description": "description-as-string", + "Manufacturer": "Intel Corporation", + "Model": "model-as-string", + "SKU": "sku-as-string", + "SerialNumber": "serial-number-as-string", + "PartNumber": "part-number-as-string", + "AssetTag": "FlexChassis1", + "IndicatorLED": "Unknown", + "PowerState": "On", + "PhysicalSecurity": { + "IntrusionSensorNumber": 1, + "IntrusionSensor": 2, + "IntrusionSensorReArm": 3 + }, + "Status": { + "State": "Enabled", + "Health": "OK" + }, + "Oem": { + "Intel_RackScale": { + "@odata.type": "#Intel.Oem.RackChassis", + "Location": { + "Id": "Rack1", + "ParentId": "Pod1" + }, + "RMMPresent": true, + "RackSupportsDisaggregatedPowerCooling": true, + "UUID": "Unique ID", + "GeoTag": "54.348103, 18.645172" + } + }, + "ThermalZones": { + "@odata.id": "/redfish/v1/Chassis/Rack1/ThermalZones" + }, + "PowerZones": { + "@odata.id": "/redfish/v1/Chassis/Rack1/PowerZones" + }, + "Thermal": { + "@odata.id": "/redfish/v1/Chassis/Rack1/Thermal" + }, + "Power": { + "@odata.id": "/redfish/v1/Chassis/Rack1/Power" + }, + "Links": { + "@odata.type": "#Chassis.v1_2_0.Links", + "Contains": [ + { + "@odata.id": "/redfish/v1/Chassis/Drawer1" + } + ], + "ContainedBy": null, + "ComputerSystems": [ + { + "@odata.id": "/redfish/v1/Systems/system1" + }, + { + "@odata.id": "/redfish/v1/Systems/system2" + }, + { + "@odata.id": "/redfish/v1/Systems/system3" + }, + { + "@odata.id": "/redfish/v1/Systems/system4" + } + ], + "Drives": [ + { + "@odata.id": "/redfish/v1/Drives/1" + } + ], + "Storage": [ + { + "@odata.id": "/redfish/v1/Storage/1" + } + ], + "CooledBy": [ + { + "@odata.id": "/redfish/v1/Cool/1" + } + ], + "PoweredBy": [ + { + "@odata.id": "/redfish/v1/Power/1" + } + ], + "ManagedBy": [ + { + "@odata.id": "/redfish/v1/Managers/RMM" + } + ], + "ManagersInChassis": [ + { + "@odata.id": "/redfish/v1/Managers/RMM" + } + ], + "Oem": { + "Intel_RackScale": { + "@odata.type": "#Intel.Oem.ChassisLinks", + "Switches": [] + } + }, + "PCIeDevices": [ + { + "@data.id": "/redfish/v1/Chassis/1/PCIeDevices/Device1" + }, + { + "@data.id": "/redfish/v1/Chassis/1/PCIeDevices/Device2" + } + ] + } +} \ No newline at end of file diff --git a/rsd_lib/tests/unit/json_samples/v2_2/power.json b/rsd_lib/tests/unit/json_samples/v2_2/power.json new file mode 100644 index 0000000..3875c78 --- /dev/null +++ b/rsd_lib/tests/unit/json_samples/v2_2/power.json @@ -0,0 +1,133 @@ +{ + "@odata.context": "/redfish/v1/$metadata#Power.Power", + "@odata.id": "/redfish/v1/Chassis/Rack1/Power", + "@odata.type": "#Power.v1_1_0.Power", + "Id": "Power", + "Name": "PowerName", + "Description": "PowerSubsystem", + "PowerControl": [ + { + "@odata.id": "/redfish/v1/Chassis/Rack1/Power#/PowerControl/0", + "MemberId": "0", + "Name": "System Power Control", + "PowerConsumedWatts": 8000, + "PowerRequestedWatts": 8500, + "PowerAvailableWatts": 8500, + "PowerCapacityWatts": 10000, + "PowerAllocatedWatts": 8500, + "PowerMetrics": { + "IntervalInMin": 30, + "MinConsumedWatts": 7500, + "MaxConsumedWatts": 8200, + "AverageConsumedWatts": 8000 + }, + "PowerLimit": { + "LimitInWatts": 9000, + "LimitException": "LogEventOnly", + "CorrectionInMs": 42 + }, + "RelatedItem": [ + {"@odata.id": "/redfish/v1/Chassis/Drawer1"}, + {"@odata.id": "/redfish/v1/Systems/System1"} + ], + "Status": { + "State": "Enabled", + "Health": "OK", + "HealthRollup": "OK" + }, + "Oem": {} + } + ], + "Voltages": [ + { + "@odata.id": "/redfish/v1/Chassis/Rack1/Power#/Voltages/0", + "MemberId": "0", + "Name": "VRM1 Voltage", + "SensorNumber": 11, + "Status": { + "State": "Enabled", + "Health": "OK" + }, + "ReadingVolts": 12, + "UpperThresholdNonCritical": 100.5, + "UpperThresholdCritical": 13, + "UpperThresholdFatal": 15, + "LowerThresholdNonCritical": 11.5, + "LowerThresholdCritical": 11, + "LowerThresholdFatal": 10, + "MinReadingRange": 0, + "MaxReadingRange": 20, + "PhysicalContext": "VoltageRegulator", + "RelatedItem": [ + {"@odata.id": "/redfish/v1/Systems/System1" } + ] + } + ], + "PowerSupplies": [ + { + "@odata.id": "/redfish/v1/Chassis/Rack1/Power#/PowerSupplies/0", + "MemberId": "0", + "Name": "Power Supply Bay 1", + "Status": { + "State": "Enabled", + "Health": "Warning" + }, + "Oem": {}, + "PowerSupplyType": "DC", + "LineInputVoltageType": "DCNeg48V", + "LineInputVoltage": -48, + "PowerCapacityWatts": 400, + "LastPowerOutputWatts": 192, + "Model": "499253-B21", + "Manufacturer": "ManufacturerName", + "FirmwareVersion": "1.00", + "SerialNumber": "1z0000001", + "PartNumber": "1z0000001A3a", + "SparePartNumber": "0000001A3a", + "IndicatorLED": "Off", + "InputRanges": [ + { + "InputType": "DC", + "MinimumVoltage": -47, + "MaximumVoltage": -49, + "OutputWattage": 400, + "MinimumFrequencyHz": 50, + "MaximumFrequencyHz": 60, + "Oem": {} + } + ], + "RelatedItem": [ + {"@odata.id": "/redfish/v1/Chassis/Rack1"} + ], + "Redundancy": [ + { + "@odata.id": "/redfish/v1/Chassis/1/Power#/Redundancy/0", + "MemberId": "0", + "Name": "PowerSupply Redundancy Group 2" + } + ] + } + ], + "Redundancy": [ + { + "@odata.id": "/redfish/v1/Chassis/Rack1/Power#/Redundancy/0", + "MemberId": "0", + "Name": "PowerSupply Redundancy Group 1", + "Mode": "Failover", + "MaxNumSupported": 2, + "MinNumNeeded": 1, + "RedundancySet": [ + {"@odata.id": "/redfish/v1/Chassis/1/Power#/PowerSupplies/0"} + ], + "Status": { + "State": "Offline", + "Health": "OK" + } + } + ], + "Oem": { + "Intel_RackScale": { + "InputACPowerWatts": 245 + } + } +} \ No newline at end of file diff --git a/rsd_lib/tests/unit/resources/v2_1/chassis/test_chassis.py b/rsd_lib/tests/unit/resources/v2_1/chassis/test_chassis.py index e833a64..08e186e 100644 --- a/rsd_lib/tests/unit/resources/v2_1/chassis/test_chassis.py +++ b/rsd_lib/tests/unit/resources/v2_1/chassis/test_chassis.py @@ -167,7 +167,7 @@ class TestChassis(base.TestCase): # On refreshing the chassis instance... with open( - "rsd_lib/tests/unit/json_samples/v2_1/" "chassis.json", "r" + "rsd_lib/tests/unit/json_samples/v2_1/chassis.json", "r" ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) @@ -190,7 +190,7 @@ class TestChassis(base.TestCase): # | GIVEN | self.conn.get.return_value.json.reset_mock() with open( - "rsd_lib/tests/unit/json_samples/v2_1/" "power.json", "r" + "rsd_lib/tests/unit/json_samples/v2_1/power.json", "r" ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN | @@ -209,7 +209,7 @@ class TestChassis(base.TestCase): def test_power_on_refresh(self): # | GIVEN | with open( - "rsd_lib/tests/unit/json_samples/v2_1/" "power.json", "r" + "rsd_lib/tests/unit/json_samples/v2_1/power.json", "r" ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN & THEN | @@ -217,7 +217,7 @@ class TestChassis(base.TestCase): # On refreshing the chassis instance... with open( - "rsd_lib/tests/unit/json_samples/v2_1/" "chassis.json", "r" + "rsd_lib/tests/unit/json_samples/v2_1/chassis.json", "r" ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) @@ -226,7 +226,7 @@ class TestChassis(base.TestCase): # | GIVEN | with open( - "rsd_lib/tests/unit/json_samples/v2_1/" "power.json", "r" + "rsd_lib/tests/unit/json_samples/v2_1/power.json", "r" ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN & THEN | @@ -271,7 +271,7 @@ class TestChassis(base.TestCase): # On refreshing the chassis instance... with open( - "rsd_lib/tests/unit/json_samples/v2_1/" "chassis.json", "r" + "rsd_lib/tests/unit/json_samples/v2_1/chassis.json", "r" ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) @@ -294,7 +294,7 @@ class TestChassis(base.TestCase): # | GIVEN | self.conn.get.return_value.json.reset_mock() with open( - "rsd_lib/tests/unit/json_samples/v2_1/" "thermal.json", "r" + "rsd_lib/tests/unit/json_samples/v2_1/thermal.json", "r" ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN | @@ -313,7 +313,7 @@ class TestChassis(base.TestCase): def test_thermal_on_refresh(self): # | GIVEN | with open( - "rsd_lib/tests/unit/json_samples/v2_1/" "thermal.json", "r" + "rsd_lib/tests/unit/json_samples/v2_1/thermal.json", "r" ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN & THEN | @@ -321,7 +321,7 @@ class TestChassis(base.TestCase): # On refreshing the chassis instance... with open( - "rsd_lib/tests/unit/json_samples/v2_1/" "chassis.json", "r" + "rsd_lib/tests/unit/json_samples/v2_1/chassis.json", "r" ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) @@ -330,7 +330,7 @@ class TestChassis(base.TestCase): # | GIVEN | with open( - "rsd_lib/tests/unit/json_samples/v2_1/" "thermal.json", "r" + "rsd_lib/tests/unit/json_samples/v2_1/thermal.json", "r" ) as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) # | WHEN & THEN | diff --git a/rsd_lib/tests/unit/resources/v2_1/chassis/test_power.py b/rsd_lib/tests/unit/resources/v2_1/chassis/test_power.py index 0b2212f..ced362b 100644 --- a/rsd_lib/tests/unit/resources/v2_1/chassis/test_power.py +++ b/rsd_lib/tests/unit/resources/v2_1/chassis/test_power.py @@ -24,9 +24,7 @@ class PowerTestCase(testtools.TestCase): def setUp(self): super(PowerTestCase, self).setUp() self.conn = mock.Mock() - with open( - "rsd_lib/tests/unit/json_samples/v2_1/" "power.json", "r" - ) as f: + with open("rsd_lib/tests/unit/json_samples/v2_1/power.json", "r") as f: self.conn.get.return_value.json.return_value = json.loads(f.read()) self.power_inst = power.Power( diff --git a/rsd_lib/tests/unit/resources/v2_2/chassis/__init__.py b/rsd_lib/tests/unit/resources/v2_2/chassis/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rsd_lib/tests/unit/resources/v2_2/chassis/test_chassis.py b/rsd_lib/tests/unit/resources/v2_2/chassis/test_chassis.py new file mode 100644 index 0000000..c2d1beb --- /dev/null +++ b/rsd_lib/tests/unit/resources/v2_2/chassis/test_chassis.py @@ -0,0 +1,75 @@ +# 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_2.chassis import chassis +from rsd_lib.resources.v2_2.chassis import power + + +class ChassisTestCase(testtools.TestCase): + def setUp(self): + super(ChassisTestCase, self).setUp() + self.conn = mock.Mock() + + with open( + "rsd_lib/tests/unit/json_samples/v2_2/chassis.json", "r" + ) as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.chassis_inst = chassis.Chassis( + self.conn, "/redfish/v1/Chassis/chassis1", redfish_version="1.0.2" + ) + + def test_power(self): + # | GIVEN | + self.conn.get.return_value.json.reset_mock() + with open("rsd_lib/tests/unit/json_samples/v2_2/power.json", "r") as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN | + actual_power = self.chassis_inst.power + # | THEN | + self.assertIsInstance(actual_power, power.Power) + 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_power, self.chassis_inst.power) + self.conn.get.return_value.json.assert_not_called() + + def test_power_on_refresh(self): + # | GIVEN | + with open("rsd_lib/tests/unit/json_samples/v2_2/power.json", "r") as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.chassis_inst.power, power.Power) + + # On refreshing the chassis instance... + with open( + "rsd_lib/tests/unit/json_samples/v2_2/chassis.json", "r" + ) as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.chassis_inst.invalidate() + self.chassis_inst.refresh(force=False) + + # | GIVEN | + with open("rsd_lib/tests/unit/json_samples/v2_2/power.json", "r") as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.chassis_inst.power, power.Power) diff --git a/rsd_lib/tests/unit/resources/v2_2/chassis/test_power.py b/rsd_lib/tests/unit/resources/v2_2/chassis/test_power.py new file mode 100644 index 0000000..72f75a2 --- /dev/null +++ b/rsd_lib/tests/unit/resources/v2_2/chassis/test_power.py @@ -0,0 +1,43 @@ +# 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_2.chassis import power + + +class PowerTestCase(testtools.TestCase): + def setUp(self): + super(PowerTestCase, self).setUp() + self.conn = mock.Mock() + with open("rsd_lib/tests/unit/json_samples/v2_2/power.json", "r") as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + + self.power_inst = power.Power( + self.conn, + "/redfish/v1/Chassis/Rack1/Power", + redfish_version="1.1.0", + ) + + def test__parse_attributes(self): + self.power_inst._parse_attributes() + self.assertEqual( + "Off", self.power_inst.power_supplies[0].indicator_led + ) + self.assertEqual( + 245, self.power_inst.oem.intel_rackscale.input_ac_power_watts + )