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)