From bbbadbd0b56708368415b0efeb98cf8aa9168e1b Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Wed, 5 Dec 2018 17:55:04 +0100 Subject: [PATCH] Add Chassis<->ComputerSystem/Manager linkage Redfish data model rests on three interlinked entities - ComputerSystem(s), Manager(s) and Chassis. As of this moment, sushy does not support traversal via Chassis despite the availability of such linkage in the JSON documents sushy feeds on. This change establishes Chassis<->ComputerSystem/Managers links. Change-Id: If26f95b3ef6d70419b3c37b3e9eabe41be258c8d Story: 2004512 Task: 28308 --- .../add-chassis-linkage-d8e567f9c791169d.yaml | 7 ++++ sushy/resources/chassis/chassis.py | 39 +++++++++++++++++++ sushy/resources/manager/manager.py | 19 +++++++++ sushy/resources/system/system.py | 23 +++++++++-- .../unit/resources/chassis/test_chassis.py | 26 +++++++++++++ .../unit/resources/manager/test_manager.py | 13 +++++++ .../unit/resources/system/test_system.py | 13 +++++++ 7 files changed, 136 insertions(+), 4 deletions(-) create mode 100644 releasenotes/notes/add-chassis-linkage-d8e567f9c791169d.yaml diff --git a/releasenotes/notes/add-chassis-linkage-d8e567f9c791169d.yaml b/releasenotes/notes/add-chassis-linkage-d8e567f9c791169d.yaml new file mode 100644 index 00000000..19084e9e --- /dev/null +++ b/releasenotes/notes/add-chassis-linkage-d8e567f9c791169d.yaml @@ -0,0 +1,7 @@ +--- +features: + - | + Establishes linkage between Chassis and ComputerSystem/Managers + resources as references at sushy data abstraction level. That + makes it possible to look up Chassis by Manager/ComputerSystem or + any other way around. diff --git a/sushy/resources/chassis/chassis.py b/sushy/resources/chassis/chassis.py index 6eeed641..a88fb5f7 100644 --- a/sushy/resources/chassis/chassis.py +++ b/sushy/resources/chassis/chassis.py @@ -17,7 +17,9 @@ from sushy import exceptions from sushy.resources import base from sushy.resources.chassis import mappings as cha_maps from sushy.resources import common +from sushy.resources.manager import manager from sushy.resources import mappings as res_maps +from sushy import utils import logging @@ -194,6 +196,43 @@ class Chassis(base.ResourceBase): self._conn.post(target_uri, data={'ResetType': value}) LOG.info('The Chassis %s is being reset', self.identity) + @property + @utils.cache_it + def managers(self): + """A list of managers for this chassis. + + Returns a list of `Manager` objects representing the managers + that manage this chassis. + + :raises: MissingAttributeError if '@odata.id' field is missing. + :returns: A list of `Manager` instances + """ + paths = utils.get_sub_resource_path_by( + self, ["Links", "ManagedBy"], is_collection=True) + + return [manager.Manager(self._conn, path, + redfish_version=self.redfish_version) + for path in paths] + + @property + @utils.cache_it + def systems(self): + """A list of systems residing in this chassis. + + Returns a list of `System` objects representing systems being + mounted in this chassis/cabinet. + + :raises: MissingAttributeError if '@odata.id' field is missing. + :returns: A list of `System` instances + """ + paths = utils.get_sub_resource_path_by( + self, ["Links", "ComputerSystems"], is_collection=True) + + from sushy.resources.system import system + return [system.System(self._conn, path, + redfish_version=self.redfish_version) + for path in paths] + class ChassisCollection(base.ResourceCollectionBase): diff --git a/sushy/resources/manager/manager.py b/sushy/resources/manager/manager.py index 9c17c706..4211f20b 100644 --- a/sushy/resources/manager/manager.py +++ b/sushy/resources/manager/manager.py @@ -214,6 +214,25 @@ class Manager(base.ResourceBase): redfish_version=self.redfish_version) for path in paths] + @property + @utils.cache_it + def chassis(self): + """A list of chassis managed by this manager. + + Returns a list of `Chassis` objects representing the chassis + or cabinets managed by this manager. + + :raises: MissingAttributeError if '@odata.id' field is missing. + :returns: A list of `Chassis` instances + """ + paths = utils.get_sub_resource_path_by( + self, ["Links", "ManagerForChassis"], is_collection=True) + + from sushy.resources.chassis import chassis + return [chassis.Chassis(self._conn, path, + redfish_version=self.redfish_version) + for path in paths] + class ManagerCollection(base.ResourceCollectionBase): diff --git a/sushy/resources/system/system.py b/sushy/resources/system/system.py index 18851e7e..69c44b86 100644 --- a/sushy/resources/system/system.py +++ b/sushy/resources/system/system.py @@ -20,6 +20,7 @@ import logging from sushy import exceptions from sushy.resources import base +from sushy.resources.chassis import chassis from sushy.resources import common from sushy.resources.manager import manager from sushy.resources import mappings as res_maps @@ -245,10 +246,6 @@ class System(base.ResourceBase): # Probably we should call refresh() as well. self._conn.patch(self.path, data=data) - # TODO(lucasagomes): All system have a Manager and Chassis object, - # include a get_manager() and get_chassis() once we have an abstraction - # for those resources. - def _get_processor_collection_path(self): """Helper function to find the ProcessorCollection path""" return utils.get_sub_resource_path_by(self, 'Processors') @@ -355,6 +352,24 @@ class System(base.ResourceBase): redfish_version=self.redfish_version) for path in paths] + @property + @utils.cache_it + def chassis(self): + """A list of chassis where this system resides. + + Returns a list of `Chassis` objects representing the chassis + or cabinets where this system is mounted. + + :raises: MissingAttributeError if '@odata.id' field is missing. + :returns: A list of `Chassis` instances + """ + paths = utils.get_sub_resource_path_by( + self, ["Links", "Chassis"], is_collection=True) + + return [chassis.Chassis(self._conn, path, + redfish_version=self.redfish_version) + for path in paths] + class SystemCollection(base.ResourceCollectionBase): diff --git a/sushy/tests/unit/resources/chassis/test_chassis.py b/sushy/tests/unit/resources/chassis/test_chassis.py index 8fc87ff8..bed4319b 100644 --- a/sushy/tests/unit/resources/chassis/test_chassis.py +++ b/sushy/tests/unit/resources/chassis/test_chassis.py @@ -19,6 +19,8 @@ import mock import sushy from sushy import exceptions from sushy.resources.chassis import chassis +from sushy.resources.manager import manager +from sushy.resources.system import system from sushy.tests.unit import base @@ -118,6 +120,30 @@ class ChassisTestCase(base.TestCase): self.assertRaises(exceptions.InvalidParameterValueError, self.chassis.reset_chassis, 'invalid-value') + def test_managers(self): + # | GIVEN | + with open('sushy/tests/unit/json_samples/' + 'manager.json') as f: + self.conn.get.return_value.json.return_value = json.load(f) + + # | WHEN & THEN | + actual_managers = self.chassis.managers + self.assertIsInstance(actual_managers[0], manager.Manager) + self.assertEqual( + '/redfish/v1/Managers/Blade1BMC', actual_managers[0].path) + + def test_systems(self): + # | GIVEN | + with open('sushy/tests/unit/json_samples/' + 'system.json') as f: + self.conn.get.return_value.json.return_value = json.load(f) + + # | WHEN & THEN | + actual_systems = self.chassis.systems + self.assertIsInstance(actual_systems[0], system.System) + self.assertEqual( + '/redfish/v1/Systems/529QB9450R6', actual_systems[0].path) + class ChassisCollectionTestCase(base.TestCase): diff --git a/sushy/tests/unit/resources/manager/test_manager.py b/sushy/tests/unit/resources/manager/test_manager.py index 565cc7ca..60c9c365 100644 --- a/sushy/tests/unit/resources/manager/test_manager.py +++ b/sushy/tests/unit/resources/manager/test_manager.py @@ -16,6 +16,7 @@ import mock import sushy from sushy import exceptions +from sushy.resources.chassis import chassis from sushy.resources.manager import manager from sushy.resources.manager import virtual_media from sushy.resources.system import system @@ -279,6 +280,18 @@ class ManagerTestCase(base.TestCase): self.assertEqual( '/redfish/v1/Systems/437XR1138R2', actual_systems[0].path) + def test_chassis(self): + # | GIVEN | + with open('sushy/tests/unit/json_samples/' + 'chassis.json') as f: + self.conn.get.return_value.json.return_value = json.load(f) + + # | WHEN & THEN | + actual_chassis = self.manager.chassis + self.assertIsInstance(actual_chassis[0], chassis.Chassis) + self.assertEqual( + '/redfish/v1/Chassis/1U', actual_chassis[0].path) + class ManagerCollectionTestCase(base.TestCase): diff --git a/sushy/tests/unit/resources/system/test_system.py b/sushy/tests/unit/resources/system/test_system.py index f6deb747..6d6c4cdd 100644 --- a/sushy/tests/unit/resources/system/test_system.py +++ b/sushy/tests/unit/resources/system/test_system.py @@ -19,6 +19,7 @@ import mock import sushy from sushy import exceptions +from sushy.resources.chassis import chassis from sushy.resources import constants as res_cons from sushy.resources.manager import manager from sushy.resources.system import bios @@ -491,6 +492,18 @@ class SystemTestCase(base.TestCase): self.assertEqual( '/redfish/v1/Managers/BMC', actual_managers[0].path) + def test_chassis(self): + # | GIVEN | + with open('sushy/tests/unit/json_samples/' + 'chassis.json') as f: + self.conn.get.return_value.json.return_value = json.load(f) + + # | WHEN & THEN | + actual_chassis = self.sys_inst.chassis + self.assertIsInstance(actual_chassis[0], chassis.Chassis) + self.assertEqual( + '/redfish/v1/Chassis/1U', actual_chassis[0].path) + class SystemCollectionTestCase(base.TestCase):