From 910d573c30ac2b090f8b0781233aa59bcfae5fdf Mon Sep 17 00:00:00 2001 From: DhuldevValekar3 Date: Tue, 21 Apr 2020 07:27:56 -0500 Subject: [PATCH] Add resources to put iDRAC in known good state This change adds the iDRACCardService, JobCollection, JobService, and LCService resources. Those offer actions to reset the iDRAC, get jobs, delete jobs, and check if iDRAC is ready. Change-Id: I29ce49c0865a610f2fb10f5072893bbf51b47303 Story: 2007617 Task: 40985 --- .../resources/manager/constants.py | 9 ++ .../resources/manager/idrac_card_service.py | 85 +++++++++++++++++++ .../resources/manager/job_collection.py | 56 ++++++++++++ .../resources/manager/job_service.py | 59 +++++++++++++ .../resources/manager/lifecycle_service.py | 62 ++++++++++++++ sushy_oem_idrac/resources/manager/manager.py | 56 ++++++++++++ sushy_oem_idrac/resources/manager/mappings.py | 7 ++ .../unit/json_samples/idrac_card_service.json | 17 ++++ .../json_samples/job_collection_expanded.json | 29 +++++++ .../tests/unit/json_samples/job_service.json | 13 +++ .../unit/json_samples/lifecycle_service.json | 13 +++ .../tests/unit/test_idrac_card_service.py | 65 ++++++++++++++ .../tests/unit/test_job_collection.py | 46 ++++++++++ .../tests/unit/test_job_service.py | 50 +++++++++++ .../tests/unit/test_lifecycle_service.py | 71 ++++++++++++++++ sushy_oem_idrac/tests/unit/test_manager.py | 63 ++++++++++++++ 16 files changed, 701 insertions(+) create mode 100644 sushy_oem_idrac/resources/manager/idrac_card_service.py create mode 100644 sushy_oem_idrac/resources/manager/job_collection.py create mode 100644 sushy_oem_idrac/resources/manager/job_service.py create mode 100644 sushy_oem_idrac/resources/manager/lifecycle_service.py create mode 100644 sushy_oem_idrac/tests/unit/json_samples/idrac_card_service.json create mode 100644 sushy_oem_idrac/tests/unit/json_samples/job_collection_expanded.json create mode 100644 sushy_oem_idrac/tests/unit/json_samples/job_service.json create mode 100644 sushy_oem_idrac/tests/unit/json_samples/lifecycle_service.json create mode 100644 sushy_oem_idrac/tests/unit/test_idrac_card_service.py create mode 100644 sushy_oem_idrac/tests/unit/test_job_collection.py create mode 100644 sushy_oem_idrac/tests/unit/test_job_service.py create mode 100644 sushy_oem_idrac/tests/unit/test_lifecycle_service.py diff --git a/sushy_oem_idrac/resources/manager/constants.py b/sushy_oem_idrac/resources/manager/constants.py index 0228ced..d6a0d29 100644 --- a/sushy_oem_idrac/resources/manager/constants.py +++ b/sushy_oem_idrac/resources/manager/constants.py @@ -28,3 +28,12 @@ EXPORT_NIC_CONFIG = 'NIC' EXPORT_RAID_CONFIG = 'RAID' """Export RAID related configuration""" + +# iDRAC Reset action constants + + +RESET_IDRAC_GRACEFUL_RESTART = 'graceful restart' +"""Perform a graceful shutdown followed by a restart of the system""" + +RESET_IDRAC_FORCE_RESTART = 'force restart' +"""Perform an immediate (non-graceful) shutdown, followed by a restart""" diff --git a/sushy_oem_idrac/resources/manager/idrac_card_service.py b/sushy_oem_idrac/resources/manager/idrac_card_service.py new file mode 100644 index 0000000..f7d6f3a --- /dev/null +++ b/sushy_oem_idrac/resources/manager/idrac_card_service.py @@ -0,0 +1,85 @@ +# Copyright (c) 2020-2021 Dell Inc. or its subsidiaries. +# +# 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 logging + +from sushy import exceptions +from sushy.resources import base + +from sushy_oem_idrac.resources.manager import constants as mgr_cons +from sushy_oem_idrac.resources.manager import mappings as mgr_maps + +LOG = logging.getLogger(__name__) + + +class ForceActionField(base.CompositeField): + target_uri = base.Field('target', required=True) + allowed_values = base.Field('Force@Redfish.AllowableValues', + adapter=list) + + +class ActionsField(base.CompositeField): + reset_idrac = ForceActionField('#DelliDRACCardService.iDRACReset') + + +class DelliDRACCardService(base.ResourceBase): + + _actions = ActionsField('Actions') + identity = base.Field('Id', required=True) + + def __init__(self, connector, identity, redfish_version=None, + registries=None): + """A class representing a DelliDRACCardService. + + :param connector: A Connector instance + :param identity: The identity of the DelliDRACCardService resource + :param redfish_version: The version of Redfish. Used to construct + the object according to schema of the given version. + :param registries: Dict of Redfish Message Registry objects to be + used in any resource that needs registries to parse messages. + """ + super(DelliDRACCardService, self).__init__( + connector, identity, redfish_version, registries) + + def get_allowed_reset_idrac_values(self): + """Get the allowed values for resetting the idrac. + + :returns: A set of allowed values. + """ + reset_action = self._actions.reset_idrac + + if not reset_action.allowed_values: + LOG.warning('Could not figure out the allowed values for the ' + 'reset idrac action for %s', self.identity) + return set(mgr_maps.RESET_IDRAC_VALUE_MAP_REV) + + return set([mgr_maps.RESET_IDRAC_VALUE_MAP[value] for value in + set(mgr_maps.RESET_IDRAC_VALUE_MAP). + intersection(reset_action.allowed_values)]) + + def reset_idrac(self): + """Reset the iDRAC. + + """ + reset_type = mgr_cons.RESET_IDRAC_GRACEFUL_RESTART + valid_resets = self.get_allowed_reset_idrac_values() + if reset_type not in valid_resets: + raise exceptions.InvalidParameterValueError( + parameter='value', value=reset_type, valid_values=valid_resets) + reset_type = mgr_maps.RESET_IDRAC_VALUE_MAP_REV[reset_type] + target_uri = self._actions.reset_idrac.target_uri + payload = {"Force": reset_type} + LOG.debug('Resetting the iDRAC %s ...', self.identity) + self._conn.post(target_uri, data=payload) + LOG.info('The iDRAC %s is being reset', self.identity) diff --git a/sushy_oem_idrac/resources/manager/job_collection.py b/sushy_oem_idrac/resources/manager/job_collection.py new file mode 100644 index 0000000..aea19be --- /dev/null +++ b/sushy_oem_idrac/resources/manager/job_collection.py @@ -0,0 +1,56 @@ +# Copyright (c) 2020-2021 Dell Inc. or its subsidiaries. +# +# 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 logging + +from sushy.resources import base + +LOG = logging.getLogger(__name__) + + +class DellJobCollection(base.ResourceBase): + + identity = base.Field('Id', required=True) + _JOB_EXPAND = '?$expand=.($levels=1)' + + def __init__(self, connector, identity, redfish_version=None, + registries=None): + """A class representing a DellJobCollection. + + :param connector: A Connector instance + :param identity: The identity of the DellJobCollection resource + :param redfish_version: The version of Redfish. Used to construct + the object according to schema of the given version. + :param registries: Dict of Redfish Message Registry objects to be + used in any resource that needs registries to parse messages + """ + super(DellJobCollection, self).__init__( + connector, identity, redfish_version, registries) + + def get_unfinished_jobs(self): + """Get the unfinished jobs. + + :returns: A list of unfinished jobs. + """ + job_expand_uri = '%s%s' % (self._path, self._JOB_EXPAND) + unfinished_jobs = [] + LOG.debug('Getting unfinished jobs...') + job_response = self._conn.get(job_expand_uri) + data = job_response.json() + for job in data[u'Members']: + if ((job[u'JobState'] == 'Scheduled') or ( + job[u'JobState'] == 'Running')): + unfinished_jobs.append(job['Id']) + LOG.info('Got unfinished jobs') + return unfinished_jobs diff --git a/sushy_oem_idrac/resources/manager/job_service.py b/sushy_oem_idrac/resources/manager/job_service.py new file mode 100644 index 0000000..db3dc55 --- /dev/null +++ b/sushy_oem_idrac/resources/manager/job_service.py @@ -0,0 +1,59 @@ +# Copyright (c) 2020-2021 Dell Inc. or its subsidiaries. +# +# 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 logging + +from sushy.resources import base +from sushy.resources import common + +LOG = logging.getLogger(__name__) + + +class ActionsField(base.CompositeField): + delete_job_queue = common.ActionField("#DellJobService.DeleteJobQueue") + + +class DellJobService(base.ResourceBase): + + _actions = ActionsField('Actions') + identity = base.Field('Id', required=True) + + def __init__(self, connector, identity, redfish_version=None, + registries=None): + """A class representing a DellJobService. + + :param connector: A Connector instance + :param identity: The identity of the DellJobService resource + :param redfish_version: The version of Redfish. Used to construct + the object according to schema of the given version. + :param registries: Dict of Redfish Message Registry objects to be + used in any resource that needs registries to parse messages. + """ + super(DellJobService, self).__init__( + connector, identity, redfish_version, registries) + + def delete_jobs(self, job_ids=['JID_CLEARALL']): + """Delete the given jobs, or all jobs. + + :param job_ids: a list of job ids to delete. Clearing all the + jobs may be accomplished using the keyword JID_CLEARALL + as the job_id. + """ + target_uri = self._actions.delete_job_queue.target_uri + for job_id in job_ids: + LOG.debug('Deleting the job %s', job_id) + payload = {'JobID': job_id} + self._conn.post(target_uri, + data=payload) + LOG.info('Deleted the job %s', job_id) diff --git a/sushy_oem_idrac/resources/manager/lifecycle_service.py b/sushy_oem_idrac/resources/manager/lifecycle_service.py new file mode 100644 index 0000000..8a5099e --- /dev/null +++ b/sushy_oem_idrac/resources/manager/lifecycle_service.py @@ -0,0 +1,62 @@ +# Copyright (c) 2020-2021 Dell Inc. or its subsidiaries. +# +# 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 logging + +from sushy.resources import base +from sushy.resources import common + +LOG = logging.getLogger(__name__) + + +class ActionsField(base.CompositeField): + remote_service_api_status = common.ActionField( + "#DellLCService.GetRemoteServicesAPIStatus") + + +class DellLCService(base.ResourceBase): + + _actions = ActionsField('Actions') + _IDRAC_READY_STATUS_CODE = 200 + _IDRAC_READY_STATUS = 'Ready' + identity = base.Field('Id', required=True) + + def __init__(self, connector, identity, redfish_version=None, + registries=None): + """A class representing a DellLCService. + + :param connector: A Connector instance + :param identity: The identity of the DellLCService resource + :param redfish_version: The version of Redfish. Used to construct + the object according to schema of the given version. + :param registries: Dict of Redfish Message Registry objects to be + used in any resource that needs registries to parse messages. + """ + super(DellLCService, self).__init__( + connector, identity, redfish_version, registries) + + def is_idrac_ready(self): + """Indicates if the iDRAC is ready to accept commands. + + :returns: A boolean value True/False based on remote service api status + response. + """ + target_uri = self._actions.remote_service_api_status.target_uri + LOG.debug('Checking to see if the iDRAC is ready...') + idrac_ready_response = self._conn.post(target_uri, data={}) + if idrac_ready_response.status_code != self._IDRAC_READY_STATUS_CODE: + return False + data = idrac_ready_response.json() + lc_status = data['LCStatus'] + return lc_status == self._IDRAC_READY_STATUS diff --git a/sushy_oem_idrac/resources/manager/manager.py b/sushy_oem_idrac/resources/manager/manager.py index 334e24b..6dc2f72 100644 --- a/sushy_oem_idrac/resources/manager/manager.py +++ b/sushy_oem_idrac/resources/manager/manager.py @@ -19,11 +19,16 @@ import sushy from sushy.resources import base from sushy.resources import common from sushy.resources.oem import base as oem_base +from sushy import utils as sushy_utils from sushy_oem_idrac import asynchronous from sushy_oem_idrac import constants from sushy_oem_idrac.resources import common as res_common from sushy_oem_idrac.resources.manager import constants as mgr_cons +from sushy_oem_idrac.resources.manager import idrac_card_service +from sushy_oem_idrac.resources.manager import job_collection +from sushy_oem_idrac.resources.manager import job_service +from sushy_oem_idrac.resources.manager import lifecycle_service from sushy_oem_idrac.resources.manager import mappings as mgr_maps from sushy_oem_idrac import utils @@ -102,6 +107,57 @@ VFDD\ def export_system_configuration_uri(self): return self._actions.export_system_configuration.target_uri + @property + @sushy_utils.cache_it + def idrac_card_service(self): + """Property to reference `DelliDRACCardService` instance of this manager. + + """ + path = sushy_utils.get_sub_resource_path_by( + self, ["Links", "Oem", "Dell", "DelliDRACCardService"], + is_collection=False) + + return idrac_card_service.DelliDRACCardService( + self._conn, path, self.redfish_version, self.registries) + + @property + @sushy_utils.cache_it + def lifecycle_service(self): + """Property to reference `DellLCService` instance of this manager. + + """ + path = sushy_utils.get_sub_resource_path_by( + self, ["Links", "Oem", "Dell", "DellLCService"], + is_collection=False) + + return lifecycle_service.DellLCService( + self._conn, path, self.redfish_version, self.registries) + + @property + @sushy_utils.cache_it + def job_service(self): + """Property to reference `DellJobService` instance of this manager. + + """ + path = sushy_utils.get_sub_resource_path_by( + self, ["Links", "Oem", "Dell", "DellJobService"], + is_collection=False) + + return job_service.DellJobService( + self._conn, path, self.redfish_version, self.registries) + + @property + @sushy_utils.cache_it + def job_collection(self): + """Property to reference `DellJobService` instance of this manager. + + """ + path = sushy_utils.get_sub_resource_path_by( + self, ["Links", "Oem", "Dell", "Jobs"], is_collection=False) + + return job_collection.DellJobCollection( + self._conn, path, self.redfish_version, self.registries) + def set_virtual_boot_device(self, device, persistent=False, manager=None, system=None): """Set boot device for a node. diff --git a/sushy_oem_idrac/resources/manager/mappings.py b/sushy_oem_idrac/resources/manager/mappings.py index 5289b55..bbf9cd8 100644 --- a/sushy_oem_idrac/resources/manager/mappings.py +++ b/sushy_oem_idrac/resources/manager/mappings.py @@ -25,3 +25,10 @@ EXPORT_CONFIG_VALUE_MAP = { } EXPORT_CONFIG_VALUE_MAP_REV = utils.revert_dictionary(EXPORT_CONFIG_VALUE_MAP) + +RESET_IDRAC_VALUE_MAP = { + 'Graceful': mgr_cons.RESET_IDRAC_GRACEFUL_RESTART, + 'Force': mgr_cons.RESET_IDRAC_FORCE_RESTART, +} + +RESET_IDRAC_VALUE_MAP_REV = utils.revert_dictionary(RESET_IDRAC_VALUE_MAP) diff --git a/sushy_oem_idrac/tests/unit/json_samples/idrac_card_service.json b/sushy_oem_idrac/tests/unit/json_samples/idrac_card_service.json new file mode 100644 index 0000000..caa7e1f --- /dev/null +++ b/sushy_oem_idrac/tests/unit/json_samples/idrac_card_service.json @@ -0,0 +1,17 @@ +{ + "@odata.context": "/redfish/v1/$metadata#DelliDRACCard.DelliDRACCardService", + "@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DelliDRACCardService", + "@odata.type": "#DelliDRACCardService.v1_1_0.DelliDRACCardService", + "Actions": { + "#DelliDRACCardService.iDRACReset": { + "Force@Redfish.AllowableValues": [ + "Graceful", + "Force" + ], + "target": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DelliDRACCardService/Actions/DelliDRACCardService.iDRACReset" + } + }, + "Description": "The DelliDRACCardService resource provides some actions to support iDRAC configurations.", + "Id": "DelliDRACCardService", + "Name": "DelliDRACCardService" +} diff --git a/sushy_oem_idrac/tests/unit/json_samples/job_collection_expanded.json b/sushy_oem_idrac/tests/unit/json_samples/job_collection_expanded.json new file mode 100644 index 0000000..aae3536 --- /dev/null +++ b/sushy_oem_idrac/tests/unit/json_samples/job_collection_expanded.json @@ -0,0 +1,29 @@ +{ + "@odata.context": "/redfish/v1/$metadata#DellJobCollection.DellJobCollection", + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs", + "@odata.type": "#DellJobCollection.DellJobCollection", + "Description": "Collection of Job Instances", + "Id": "JobQueue", + "Members": [ + { + "@odata.context": "/redfish/v1/$metadata#DellJob.DellJob", + "@odata.id": "/redfish/v1/Managers/iDRAC.Embedded.1/Jobs/RID_878460711202", + "@odata.type": "#DellJob.v1_0_2.DellJob", + "CompletionTime": "2020-04-25T15:21:33", + "Description": "Job Instance", + "EndTime": "TIME_NA", + "Id": "RID_878460711202", + "JobState": "Running", + "JobType": "RebootForce", + "Message": "Reboot is complete.", + "MessageArgs": [ ], + "MessageArgs@odata.count": 0, + "MessageId": "RED030", + "Name": "Reboot3", + "PercentComplete": 100, + "StartTime": "TIME_NOW", + "TargetSettingsURI": null + } + ], + "Name": "JobQueue" +} diff --git a/sushy_oem_idrac/tests/unit/json_samples/job_service.json b/sushy_oem_idrac/tests/unit/json_samples/job_service.json new file mode 100644 index 0000000..dc7790f --- /dev/null +++ b/sushy_oem_idrac/tests/unit/json_samples/job_service.json @@ -0,0 +1,13 @@ +{ + "@odata.context": "/redfish/v1/$metadata#DellJobService.DellJobService", + "@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellJobService", + "@odata.type": "#DellJobService.v1_1_0.DellJobService", + "Actions": { + "#DellJobService.DeleteJobQueue": { + "target": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellJobService/Actions/DellJobService.DeleteJobQueue" + } + }, + "Description": "The DellJobService resource provides some actions to support Job management functionality.", + "Id": "Job Service", + "Name": "DellJobService" +} diff --git a/sushy_oem_idrac/tests/unit/json_samples/lifecycle_service.json b/sushy_oem_idrac/tests/unit/json_samples/lifecycle_service.json new file mode 100644 index 0000000..cd81e6e --- /dev/null +++ b/sushy_oem_idrac/tests/unit/json_samples/lifecycle_service.json @@ -0,0 +1,13 @@ +{ + "@odata.context": "/redfish/v1/$metadata#DellLCService.DellLCService", + "@odata.id": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService", + "@odata.type": "#DellLCService.v1_1_0.DellLCService", + "Actions": { + "#DellLCService.GetRemoteServicesAPIStatus": { + "target": "/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService/Actions/DellLCService.GetRemoteServicesAPIStatus" + } + }, + "Description": "The DellLCService resource provides some actions to support Lifecycle Controller functionality.", + "Id": "DellLCService", + "Name": "DellLCService" +} diff --git a/sushy_oem_idrac/tests/unit/test_idrac_card_service.py b/sushy_oem_idrac/tests/unit/test_idrac_card_service.py new file mode 100644 index 0000000..cb226cc --- /dev/null +++ b/sushy_oem_idrac/tests/unit/test_idrac_card_service.py @@ -0,0 +1,65 @@ +# Copyright (c) 2020-2021 Dell Inc. or its subsidiaries. +# +# 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 +from unittest import mock + +from oslotest.base import BaseTestCase + +from sushy_oem_idrac.resources.manager import constants as mgr_cons +from sushy_oem_idrac.resources.manager import idrac_card_service + + +class DelliDRACCardServiceTestCase(BaseTestCase): + + def setUp(self): + super(DelliDRACCardServiceTestCase, self).setUp() + self.conn = mock.Mock() + with open('sushy_oem_idrac/tests/unit/json_samples/' + 'idrac_card_service.json') as f: + mock_response = self.conn.get.return_value + mock_response.json.return_value = json.load(f) + mock_response.status_code = 200 + + mock_response = self.conn.post.return_value + mock_response.status_code = 202 + mock_response.headers.get.return_value = '1' + self.idrac_card_service = idrac_card_service.DelliDRACCardService( + self.conn, '/redfish/v1/Dell/Managers/iDRAC.Embedded.1') + + def test_reset_idrac(self): + self.idrac_card_service.reset_idrac() + target_uri = ('/redfish/v1/Dell/Managers/iDRAC.Embedded.1' + '/DelliDRACCardService' + '/Actions/DelliDRACCardService.iDRACReset') + self.conn.post.assert_called_once_with(target_uri, + data={'Force': 'Graceful'}) + + def test_get_allowed_reset_idrac_values(self): + expected_values = {mgr_cons.RESET_IDRAC_GRACEFUL_RESTART, + mgr_cons.RESET_IDRAC_FORCE_RESTART} + allowed_values = \ + self.idrac_card_service.get_allowed_reset_idrac_values() + self.assertEqual(expected_values, allowed_values) + + def test_get_allowed_reset_idrac_values_not_provided_by_idrac(self): + idrac_service_json = self.idrac_card_service.json + base_property = '#DelliDRACCardService.iDRACReset' + remove_property = 'Force@Redfish.AllowableValues' + idrac_service_json['Actions'][base_property].pop(remove_property) + expected_values = {mgr_cons.RESET_IDRAC_GRACEFUL_RESTART, + mgr_cons.RESET_IDRAC_FORCE_RESTART} + allowed_values = \ + self.idrac_card_service.get_allowed_reset_idrac_values() + self.assertEqual(expected_values, allowed_values) diff --git a/sushy_oem_idrac/tests/unit/test_job_collection.py b/sushy_oem_idrac/tests/unit/test_job_collection.py new file mode 100644 index 0000000..8ff86b9 --- /dev/null +++ b/sushy_oem_idrac/tests/unit/test_job_collection.py @@ -0,0 +1,46 @@ +# Copyright (c) 2020-2021 Dell Inc. or its subsidiaries. +# +# 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 +from unittest import mock + +from oslotest.base import BaseTestCase + +from sushy_oem_idrac.resources.manager import job_collection + + +class DellJobCollectionTestCase(BaseTestCase): + + def setUp(self): + super(DellJobCollectionTestCase, self).setUp() + self.conn = mock.Mock() + with open('sushy_oem_idrac/tests/unit/json_samples/' + 'job_collection_expanded.json') as f: + mock_response = self.conn.get.return_value + mock_response.json.return_value = json.load(f) + mock_response.status_code = 200 + + mock_response = self.conn.post.return_value + mock_response.status_code = 202 + mock_response.headers.get.return_value = '1' + self.job_collection = job_collection.DellJobCollection( + self.conn, '/redfish/v1/Managers/iDRAC.Embedded.1/Jobs') + + def test_get_unfinished_jobs(self): + expected_unfinished_jobs = ['RID_878460711202'] + actual_unfinished_jobs = self.job_collection.get_unfinished_jobs() + target_uri = ('/redfish/v1/Managers/iDRAC.Embedded.1' + '/Jobs?$expand=.($levels=1)') + self.conn.get.assert_called_with(target_uri) + self.assertEqual(expected_unfinished_jobs, actual_unfinished_jobs) diff --git a/sushy_oem_idrac/tests/unit/test_job_service.py b/sushy_oem_idrac/tests/unit/test_job_service.py new file mode 100644 index 0000000..e00cd19 --- /dev/null +++ b/sushy_oem_idrac/tests/unit/test_job_service.py @@ -0,0 +1,50 @@ +# Copyright (c) 2020-2021 Dell Inc. or its subsidiaries. +# +# 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 +from unittest import mock + +from oslotest.base import BaseTestCase + +from sushy_oem_idrac.resources.manager import job_service + + +class DellJobServiceTestCase(BaseTestCase): + + def setUp(self): + super(DellJobServiceTestCase, self).setUp() + self.conn = mock.Mock() + with open('sushy_oem_idrac/tests/unit/json_samples/' + 'job_service.json') as f: + mock_response = self.conn.get.return_value + mock_response.json.return_value = json.load(f) + mock_response.status_code = 200 + + mock_response = self.conn.post.return_value + mock_response.status_code = 202 + mock_response.headers.get.return_value = '1' + self.job_service = job_service.DellJobService( + self.conn, + '/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellJobService') + + def test_delete_jobs(self): + job_ids = ['JID_471269252011', 'JID_471269252012'] + self.job_service.delete_jobs(job_ids=job_ids) + target_uri = ('/redfish/v1/Dell/Managers' + '/iDRAC.Embedded.1/DellJobService' + '/Actions/''DellJobService.DeleteJobQueue') + for job_id in job_ids: + self.conn.post.assert_any_call(target_uri, + data={'JobID': job_id}) + self.assertEqual(2, self.conn.post.call_count) diff --git a/sushy_oem_idrac/tests/unit/test_lifecycle_service.py b/sushy_oem_idrac/tests/unit/test_lifecycle_service.py new file mode 100644 index 0000000..4a00405 --- /dev/null +++ b/sushy_oem_idrac/tests/unit/test_lifecycle_service.py @@ -0,0 +1,71 @@ +# Copyright (c) 2020-2021 Dell Inc. or its subsidiaries. +# +# 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 +from unittest import mock + +from oslotest.base import BaseTestCase + +from sushy_oem_idrac.resources.manager import lifecycle_service + + +class DellLCServiceTestCase(BaseTestCase): + + def setUp(self): + super(DellLCServiceTestCase, self).setUp() + self.conn = mock.Mock() + with open('sushy_oem_idrac/tests/unit/json_samples/' + 'lifecycle_service.json') as f: + mock_response = self.conn.get.return_value + mock_response.json.return_value = json.load(f) + mock_response.status_code = 200 + + mock_response = self.conn.post.return_value + mock_response.status_code = 202 + mock_response.headers.get.return_value = '1' + self.lifecycle_service = lifecycle_service.DellLCService( + self.conn, + '/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService') + + def test_is_idrac_ready_true(self): + mock_response = self.conn.post.return_value + mock_response.status_code = 200 + mock_response.json.return_value = { + "LCStatus": "Ready", + "RTStatus": "Ready", + "ServerStatus": "OutOfPOST", + "Status": "Ready" + } + target_uri = ('/redfish/v1/Dell/Managers/iDRAC.Embedded.1' + '/DellLCService' + '/Actions/DellLCService.GetRemoteServicesAPIStatus') + idrac_ready_response = self.lifecycle_service.is_idrac_ready() + self.conn.post.assert_called_once_with(target_uri, data={}) + self.assertTrue(idrac_ready_response) + + def test_is_idrac_ready_false(self): + mock_response = self.conn.post.return_value + mock_response.status_code = 202 + mock_response.json.return_value = { + "LCStatus": "NotReady", + "RTStatus": "NotReady", + "ServerStatus": "OutOfPOST", + "Status": "NotReady" + } + target_uri = ('/redfish/v1/Dell/Managers/iDRAC.Embedded.1' + '/DellLCService' + '/Actions/DellLCService.GetRemoteServicesAPIStatus') + idrac_ready_response = self.lifecycle_service.is_idrac_ready() + self.conn.post.assert_called_once_with(target_uri, data={}) + self.assertFalse(idrac_ready_response) diff --git a/sushy_oem_idrac/tests/unit/test_manager.py b/sushy_oem_idrac/tests/unit/test_manager.py index e06a349..65ef86c 100644 --- a/sushy_oem_idrac/tests/unit/test_manager.py +++ b/sushy_oem_idrac/tests/unit/test_manager.py @@ -22,6 +22,10 @@ import sushy from sushy.resources.manager import manager from sushy_oem_idrac.resources.manager import constants as mgr_cons +from sushy_oem_idrac.resources.manager import idrac_card_service as idrac_card +from sushy_oem_idrac.resources.manager import job_collection as jc +from sushy_oem_idrac.resources.manager import job_service as job +from sushy_oem_idrac.resources.manager import lifecycle_service as lifecycle class ManagerTestCase(BaseTestCase): @@ -152,3 +156,62 @@ class ManagerTestCase(BaseTestCase): self.assertRaises(sushy.exceptions.ExtensionError, oem.get_pxe_port_macs_bios, ethernet_interfaces_mac) + + def test_idrac_card_service(self): + oem = self.manager.get_oem_extension('Dell') + with open('sushy_oem_idrac/tests/unit/json_samples/' + 'idrac_card_service.json') as f: + mock_response = self.conn.get.return_value + mock_response.json.return_value = json.load(f) + mock_response.status_code = 200 + idrac_card_service = oem.idrac_card_service + self.assertEqual( + '/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DelliDRACCardService', + idrac_card_service.path) + self.assertIsInstance(idrac_card_service, + idrac_card.DelliDRACCardService) + + @mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {}) + def test_lifecycle_service(self): + oem = self.manager.get_oem_extension('Dell') + with open('sushy_oem_idrac/tests/unit/json_samples/' + 'lifecycle_service.json') as f: + mock_response = self.conn.get.return_value + mock_response.json.return_value = json.load(f) + mock_response.status_code = 200 + lifecycle_service = oem.lifecycle_service + self.assertEqual( + '/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellLCService', + lifecycle_service.path) + self.assertIsInstance(lifecycle_service, + lifecycle.DellLCService) + + @mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {}) + def test_job_service(self): + oem = self.manager.get_oem_extension('Dell') + with open('sushy_oem_idrac/tests/unit/json_samples/' + 'job_service.json') as f: + mock_response = self.conn.get.return_value + mock_response.json.return_value = json.load(f) + mock_response.status_code = 200 + job_service = oem.job_service + self.assertEqual( + '/redfish/v1/Dell/Managers/iDRAC.Embedded.1/DellJobService', + job_service.path) + self.assertIsInstance(job_service, + job.DellJobService) + + @mock.patch('sushy.resources.oem.common._global_extn_mgrs_by_resource', {}) + def test_job_collection(self): + oem = self.manager.get_oem_extension('Dell') + with open('sushy_oem_idrac/tests/unit/json_samples/' + 'job_collection_expanded.json') as f: + mock_response = self.conn.get.return_value + mock_response.json.return_value = json.load(f) + mock_response.status_code = 200 + job_collection = oem.job_collection + self.assertEqual( + '/redfish/v1/Managers/iDRAC.Embedded.1/Jobs', + job_collection.path) + self.assertIsInstance(job_collection, + jc.DellJobCollection)