From c961d8fa804a0d9b44f037b4b05b7edb76e21ed6 Mon Sep 17 00:00:00 2001 From: Ilya Etingof Date: Fri, 7 Feb 2020 19:27:28 +0100 Subject: [PATCH] Automatically discover available system/manager/chassis The ``get_system``, ``get_manager`` and ``get_chassis`` methods have been modified not to require the ``identity`` parameter referring to a particular resource instance. If ``identity`` is omited, sushy will default to the only available resource for as long as it's single and therefore deterministic. The intent is to simplify user API by not requiring the consumer to discover available resources prior to requesting one. Change-Id: Ifbf8b05154b619d4831c26870ee97c3dce555fd2 Story: 2007258 Task: 38707 --- ...add-default-identity-10c5dd23bed0e915.yaml | 10 ++ sushy/exceptions.py | 4 + sushy/main.py | 50 +++++++- sushy/tests/unit/test_main.py | 108 ++++++++++++++++++ 4 files changed, 166 insertions(+), 6 deletions(-) create mode 100644 releasenotes/notes/add-default-identity-10c5dd23bed0e915.yaml diff --git a/releasenotes/notes/add-default-identity-10c5dd23bed0e915.yaml b/releasenotes/notes/add-default-identity-10c5dd23bed0e915.yaml new file mode 100644 index 00000000..6aa2a8b9 --- /dev/null +++ b/releasenotes/notes/add-default-identity-10c5dd23bed0e915.yaml @@ -0,0 +1,10 @@ +--- +features: + - | + The ``get_system``, ``get_manager`` and ``get_chassis`` methods modified + not to require the ``identity`` parameter referring to a particular + resource instance. If ``identity`` is omited, sushy will default to the + only available resource for as long as it's single and therefore + deterministic. + The intent is to simplify user API by not requiring the consumer to + discover available resources prior to requesting one. diff --git a/sushy/exceptions.py b/sushy/exceptions.py index 9f1e01ae..3d6d18b7 100644 --- a/sushy/exceptions.py +++ b/sushy/exceptions.py @@ -60,6 +60,10 @@ class ArchiveParsingError(SushyError): message = 'Failed parsing archive "%(path)s": %(error)s' +class UnknownDefaultError(SushyError): + message = 'Failed at determining default for "%(entity)s": %(error)s' + + class ExtensionError(SushyError): message = ('Sushy Extension Error: %(error)s') diff --git a/sushy/main.py b/sushy/main.py index d397c32d..d378e127 100644 --- a/sushy/main.py +++ b/sushy/main.py @@ -182,12 +182,25 @@ class Sushy(base.ResourceBase): redfish_version=self.redfish_version, registries=self.registries) - def get_system(self, identity): + def get_system(self, identity=None): """Given the identity return a System object - :param identity: The identity of the System resource + :param identity: The identity of the System resource. If not given, + sushy will default to the single available System or fail + if there appear to be more or less then one System listed. + :raises: `UnknownDefaultError` if default system can't be determined. :returns: The System object """ + if identity is None: + systems_collection = self.get_system_collection() + listed_systems = systems_collection.get_members() + if len(listed_systems) != 1: + raise exceptions.UnknownDefaultError( + entity='ComputerSystem', + error='System count is not exactly one') + + identity = listed_systems[0].path + return system.System(self._conn, identity, redfish_version=self.redfish_version, registries=self.registries) @@ -207,12 +220,25 @@ class Sushy(base.ResourceBase): redfish_version=self.redfish_version, registries=self.registries) - def get_chassis(self, identity): + def get_chassis(self, identity=None): """Given the identity return a Chassis object - :param identity: The identity of the Chassis resource + :param identity: The identity of the Chassis resource. If not given, + sushy will default to the single available chassis or fail + if there appear to be more or less then one Chassis listed. + :raises: `UnknownDefaultError` if default system can't be determined. :returns: The Chassis object """ + if identity is None: + chassis_collection = self.get_chassis_collection() + listed_chassis = chassis_collection.get_members() + if len(listed_chassis) != 1: + raise exceptions.UnknownDefaultError( + entity='Chassis', + error='Chassis count is not exactly one') + + identity = listed_chassis[0].path + return chassis.Chassis(self._conn, identity, redfish_version=self.redfish_version, registries=self.registries) @@ -257,12 +283,24 @@ class Sushy(base.ResourceBase): redfish_version=self.redfish_version, registries=self.registries) - def get_manager(self, identity): + def get_manager(self, identity=None): """Given the identity return a Manager object - :param identity: The identity of the Manager resource + :param identity: The identity of the Manager resource. If not given, + sushy will default to the single available Manager or fail + if there appear to be more or less then one Manager listed. :returns: The Manager object """ + if identity is None: + managers_collection = self.get_manager_collection() + listed_managers = managers_collection.get_members() + if len(listed_managers) != 1: + raise exceptions.UnknownDefaultError( + entity='Manager', + error='Manager count is not exactly one') + + identity = listed_managers[0].path + return manager.Manager(self._conn, identity, redfish_version=self.redfish_version, registries=self.registries) diff --git a/sushy/tests/unit/test_main.py b/sushy/tests/unit/test_main.py index 92b9e3d6..9750b268 100644 --- a/sushy/tests/unit/test_main.py +++ b/sushy/tests/unit/test_main.py @@ -116,6 +116,41 @@ class MainTestCase(base.TestCase): redfish_version=self.root.redfish_version, registries=mock_registries) + @mock.patch.object(system, 'SystemCollection', autospec=True) + @mock.patch.object(system, 'System', autospec=True) + @mock.patch('sushy.Sushy.registries', autospec=True) + def test_get_system_default_ok( + self, mock_registries, mock_system, mock_system_collection): + self.root._standard_message_registries_path = None + mock_system.path = 'fake-system-id' + mock_members = mock_system_collection.return_value.get_members + mock_members.return_value = [mock_system] + self.root.get_system() + mock_system_collection.assert_called_once_with( + self.root._conn, '/redfish/v1/Systems', + redfish_version=self.root.redfish_version, + registries=mock_registries + ) + mock_system.assert_called_once_with( + self.root._conn, 'fake-system-id', + redfish_version=self.root.redfish_version, + registries=mock_registries) + + @mock.patch.object(system, 'SystemCollection', autospec=True) + @mock.patch.object(system, 'System', autospec=True) + @mock.patch('sushy.Sushy.registries', autospec=True) + def test_get_system_default_failure( + self, mock_registries, mock_system, mock_system_collection): + self.root._standard_message_registries_path = None + mock_members = mock_system_collection.return_value.get_members + mock_members.return_value = [] + self.assertRaises(exceptions.UnknownDefaultError, self.root.get_system) + mock_system_collection.assert_called_once_with( + self.root._conn, '/redfish/v1/Systems', + redfish_version=self.root.redfish_version, + registries=mock_registries + ) + @mock.patch.object(chassis, 'Chassis', autospec=True) def test_get_chassis(self, mock_chassis): self.root.get_chassis('fake-chassis-id') @@ -123,6 +158,43 @@ class MainTestCase(base.TestCase): self.root._conn, 'fake-chassis-id', self.root.redfish_version, self.root.registries) + @mock.patch.object(chassis, 'ChassisCollection', autospec=True) + @mock.patch.object(chassis, 'Chassis', autospec=True) + @mock.patch('sushy.Sushy.registries', autospec=True) + def test_get_chassis_default_ok( + self, mock_registries, mock_chassis, mock_chassis_collection): + self.root._standard_message_registries_path = None + mock_chassis.path = 'fake-chassis-id' + mock_members = mock_chassis_collection.return_value.get_members + mock_members.return_value = [mock_chassis] + self.root.get_chassis() + mock_chassis_collection.assert_called_once_with( + self.root._conn, '/redfish/v1/Chassis', + redfish_version=self.root.redfish_version, + registries=mock_registries + ) + mock_chassis.assert_called_once_with( + self.root._conn, 'fake-chassis-id', + redfish_version=self.root.redfish_version, + registries=mock_registries + ) + + @mock.patch.object(chassis, 'ChassisCollection', autospec=True) + @mock.patch.object(chassis, 'Chassis', autospec=True) + @mock.patch('sushy.Sushy.registries', autospec=True) + def test_get_chassis_default_failure( + self, mock_registries, mock_chassis, mock_chassis_collection): + self.root._standard_message_registries_path = None + mock_members = mock_chassis_collection.return_value.get_members + mock_members.return_value = [] + self.assertRaises( + exceptions.UnknownDefaultError, self.root.get_chassis) + mock_chassis_collection.assert_called_once_with( + self.root._conn, '/redfish/v1/Chassis', + redfish_version=self.root.redfish_version, + registries=mock_registries + ) + @mock.patch.object(chassis, 'ChassisCollection', autospec=True) def test_get_chassis_collection(self, chassis_collection_mock): self.root.get_chassis_collection() @@ -158,6 +230,42 @@ class MainTestCase(base.TestCase): self.root._conn, 'fake-manager-id', self.root.redfish_version, self.root.registries) + @mock.patch.object(manager, 'ManagerCollection', autospec=True) + @mock.patch.object(manager, 'Manager', autospec=True) + @mock.patch('sushy.Sushy.registries', autospec=True) + def test_get_manager_default_ok( + self, mock_registries, mock_manager, mock_manager_collection): + self.root._standard_message_registries_path = None + mock_manager.path = 'fake-manager-id' + mock_members = mock_manager_collection.return_value.get_members + mock_members.return_value = [mock_manager] + self.root.get_manager() + mock_manager_collection.assert_called_once_with( + self.root._conn, '/redfish/v1/Managers', + redfish_version=self.root.redfish_version, + registries=mock_registries + ) + mock_manager.assert_called_once_with( + self.root._conn, 'fake-manager-id', + redfish_version=self.root.redfish_version, + registries=mock_registries) + + @mock.patch.object(manager, 'ManagerCollection', autospec=True) + @mock.patch.object(manager, 'Manager', autospec=True) + @mock.patch('sushy.Sushy.registries', autospec=True) + def test_get_manager_default_failure( + self, mock_registries, mock_manager, mock_system_collection): + self.root._standard_message_registries_path = None + mock_members = mock_system_collection.return_value.get_members + mock_members.return_value = [] + self.assertRaises( + exceptions.UnknownDefaultError, self.root.get_manager) + mock_system_collection.assert_called_once_with( + self.root._conn, '/redfish/v1/Managers', + redfish_version=self.root.redfish_version, + registries=mock_registries + ) + @mock.patch.object(sessionservice, 'SessionService', autospec=True) def test_get_sessionservice(self, mock_sess_serv): self.root.get_session_service()