diff --git a/proliantutils/ilo/client.py b/proliantutils/ilo/client.py index 6835819..9a8ef3f 100644 --- a/proliantutils/ilo/client.py +++ b/proliantutils/ilo/client.py @@ -78,6 +78,10 @@ SUPPORTED_REDFISH_METHODS = [ 'set_pending_boot_mode', 'reset_ilo_credential', 'reset_bios_to_default', + 'get_secure_boot_mode', + 'set_secure_boot_mode', + 'reset_secure_boot_keys', + 'clear_secure_boot_keys' ] LOG = log.get_logger(__name__) diff --git a/proliantutils/redfish/redfish.py b/proliantutils/redfish/redfish.py index 063fd0b..37cb233 100644 --- a/proliantutils/redfish/redfish.py +++ b/proliantutils/redfish/redfish.py @@ -67,7 +67,13 @@ PERSISTENT_BOOT_MAP = { sushy.BOOT_SOURCE_TARGET_UEFI_TARGET: 'NETWORK', sushy.BOOT_SOURCE_TARGET_NONE: 'NONE' } -# Assuming only one sushy_system and sushy_manager present as part of + +GET_SECUREBOOT_CURRENT_BOOT_MAP = { + sys_cons.SECUREBOOT_CURRENT_BOOT_ENABLED: True, + sys_cons.SECUREBOOT_CURRENT_BOOT_DISABLED: False +} + +# Assuming only one system and one manager present as part of # collection, as we are dealing with iLO's here. PROLIANT_MANAGER_ID = '1' PROLIANT_SYSTEM_ID = '1' @@ -635,3 +641,106 @@ class RedfishOperations(operations.IloOperations): {'error': str(e)}) LOG.debug(msg) raise exception.IloError(msg) + + def get_secure_boot_mode(self): + """Get the status of secure boot. + + :returns: True, if enabled, else False + :raises: IloError, on an error from iLO. + :raises: IloCommandNotSupportedError, if the command is not supported + on the server. + """ + sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID) + try: + secure_boot_enabled = GET_SECUREBOOT_CURRENT_BOOT_MAP.get( + sushy_system.secure_boot.current_boot) + except sushy.exceptions.SushyError as e: + msg = (self._('The Redfish controller failed to provide ' + 'information about secure boot on the server. ' + 'Error: %(error)s') % + {'error': str(e)}) + LOG.debug(msg) + raise exception.IloCommandNotSupportedError(msg) + + if secure_boot_enabled: + LOG.debug(self._("Secure boot is Enabled")) + else: + LOG.debug(self._("Secure boot is Disabled")) + return secure_boot_enabled + + def set_secure_boot_mode(self, secure_boot_enable): + """Enable/Disable secure boot on the server. + + Resetting the server post updating this settings is needed + from the caller side to make this into effect. + :param secure_boot_enable: True, if secure boot needs to be + enabled for next boot, else False. + :raises: IloError, on an error from iLO. + :raises: IloCommandNotSupportedError, if the command is not supported + on the server. + """ + if self._is_boot_mode_uefi(): + sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID) + try: + sushy_system.secure_boot.enable_secure_boot(secure_boot_enable) + except exception.InvalidInputError as e: + msg = (self._('Invalid input. Error %(error)s') + % {'error': str(e)}) + LOG.debug(msg) + raise exception.IloError(msg) + except sushy.exceptions.SushyError as e: + msg = (self._('The Redfish controller failed to set secure ' + 'boot settings on the server. Error: %(error)s') + % {'error': str(e)}) + LOG.debug(msg) + raise exception.IloError(msg) + else: + msg = (self._('System is not in UEFI boot mode. "SecureBoot" ' + 'related resources cannot be changed.')) + raise exception.IloCommandNotSupportedInBiosError(msg) + + def reset_secure_boot_keys(self): + """Reset secure boot keys to manufacturing defaults. + + :raises: IloError, on an error from iLO. + :raises: IloCommandNotSupportedError, if the command is not supported + on the server. + """ + if self._is_boot_mode_uefi(): + sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID) + try: + sushy_system.secure_boot.reset_keys( + sys_cons.SECUREBOOT_RESET_KEYS_DEFAULT) + except sushy.exceptions.SushyError as e: + msg = (self._('The Redfish controller failed to reset secure ' + 'boot keys on the server. Error %(error)s') + % {'error': str(e)}) + LOG.debug(msg) + raise exception.IloError(msg) + else: + msg = (self._('System is not in UEFI boot mode. "SecureBoot" ' + 'related resources cannot be changed.')) + raise exception.IloCommandNotSupportedInBiosError(msg) + + def clear_secure_boot_keys(self): + """Reset all keys. + + :raises: IloError, on an error from iLO. + :raises: IloCommandNotSupportedError, if the command is not supported + on the server. + """ + if self._is_boot_mode_uefi(): + sushy_system = self._get_sushy_system(PROLIANT_SYSTEM_ID) + try: + sushy_system.secure_boot.reset_keys( + sys_cons.SECUREBOOT_RESET_KEYS_DELETE_ALL) + except sushy.exceptions.SushyError as e: + msg = (self._('The Redfish controller failed to clear secure ' + 'boot keys on the server. Error %(error)s') + % {'error': str(e)}) + LOG.debug(msg) + raise exception.IloError(msg) + else: + msg = (self._('System is not in UEFI boot mode. "SecureBoot" ' + 'related resources cannot be changed.')) + raise exception.IloCommandNotSupportedInBiosError(msg) diff --git a/proliantutils/redfish/resources/system/constants.py b/proliantutils/redfish/resources/system/constants.py index 6f5b82e..59fc0ed 100644 --- a/proliantutils/redfish/resources/system/constants.py +++ b/proliantutils/redfish/resources/system/constants.py @@ -35,3 +35,14 @@ BOOT_SOURCE_TARGET_HDD = 'Hdd' SRIOV_ENABLED = 'sriov enabled' SRIOV_DISABLED = 'sriov disabled' + +# Secure Boot current boot constants + +SECUREBOOT_CURRENT_BOOT_ENABLED = 'enabled' +SECUREBOOT_CURRENT_BOOT_DISABLED = 'disabled' + +# Secure Boot reset keys constants + +SECUREBOOT_RESET_KEYS_DEFAULT = 'default' +SECUREBOOT_RESET_KEYS_DELETE_ALL = 'delete all' +SECUREBOOT_RESET_KEYS_DELETE_PK = 'delete pk' diff --git a/proliantutils/redfish/resources/system/mappings.py b/proliantutils/redfish/resources/system/mappings.py index a935c62..9f140bb 100644 --- a/proliantutils/redfish/resources/system/mappings.py +++ b/proliantutils/redfish/resources/system/mappings.py @@ -41,3 +41,20 @@ SRIOV_MAP = { 'Enabled': constants.SRIOV_ENABLED, 'Disabled': constants.SRIOV_DISABLED } + +SECUREBOOT_CURRENT_BOOT_MAP = { + 'Enabled': constants.SECUREBOOT_CURRENT_BOOT_ENABLED, + 'Disabled': constants.SECUREBOOT_CURRENT_BOOT_DISABLED, +} + +SECUREBOOT_CURRENT_BOOT_MAP_REV = ( + utils.revert_dictionary(SECUREBOOT_CURRENT_BOOT_MAP)) + +SECUREBOOT_RESET_KEYS_MAP = { + 'ResetAllKeysToDefault': constants.SECUREBOOT_RESET_KEYS_DEFAULT, + 'DeleteAllKeys': constants.SECUREBOOT_RESET_KEYS_DELETE_ALL, + 'DeletePK': constants.SECUREBOOT_RESET_KEYS_DELETE_PK, +} + +SECUREBOOT_RESET_KEYS_MAP_REV = ( + utils.revert_dictionary(SECUREBOOT_RESET_KEYS_MAP)) diff --git a/proliantutils/redfish/resources/system/secure_boot.py b/proliantutils/redfish/resources/system/secure_boot.py new file mode 100644 index 0000000..d2e6c04 --- /dev/null +++ b/proliantutils/redfish/resources/system/secure_boot.py @@ -0,0 +1,120 @@ +# Copyright 2017 Hewlett Packard Enterprise Development LP +# +# 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. + +__author__ = 'HPE' + +from sushy.resources import base + +from proliantutils import exception +from proliantutils import log +from proliantutils.redfish.resources.system import mappings + +LOG = log.get_logger(__name__) + + +class ResetKeysActionField(base.CompositeField): + allowed_values = base.Field('ResetKeysType@Redfish.AllowableValues', + adapter=list) + + target_uri = base.Field('target', required=True) + + +class ActionsField(base.CompositeField): + reset_keys = ResetKeysActionField('#SecureBoot.ResetKeys') + + +class SecureBoot(base.ResourceBase): + """A class representing SecureBoot resource""" + + name = base.Field('Name') + """secure boot resource name""" + + current_boot = base.MappedField( + 'SecureBootCurrentBoot', mappings.SECUREBOOT_CURRENT_BOOT_MAP) + """current secure boot""" + + enable = base.Field('SecureBootEnable', required=True) + """secure boot enable""" + + # Note(deray): May need mapping if this gets used. + mode = base.Field('SecureBootMode') + """secure boot mode""" + + _actions = ActionsField('Actions', required=True) + + def enable_secure_boot(self, secure_boot_enable): + """Enable/Disable secure boot on the server. + + Caller needs to reset the server after issuing this command + to bring this into effect. + :param secure_boot_enable: True, if secure boot needs to be + enabled for next boot, else False. + :raises: InvalidInputError, if the validation of the input fails + :raises: SushyError, on an error from iLO. + """ + if not isinstance(secure_boot_enable, bool): + msg = ('The parameter "%(parameter)s" value "%(value)s" is ' + 'invalid. Valid values are: True/False.' % + {'parameter': 'secure_boot_enable', + 'value': secure_boot_enable}) + raise exception.InvalidInputError(msg) + + self._conn.patch(self.path, + data={'SecureBootEnable': secure_boot_enable}) + + def _get_reset_keys_action_element(self): + reset_keys_action = self._actions.reset_keys + if not reset_keys_action: + raise exception.MissingAttributeError( + attribute='Actions/#SecureBoot.ResetKeys', + resource=self.path) + return reset_keys_action + + def get_allowed_reset_keys_values(self): + """Get the allowed values for resetting the system. + + :returns: A set with the allowed values. + """ + reset_keys_action = self._get_reset_keys_action_element() + + if not reset_keys_action.allowed_values: + LOG.warning('Could not figure out the allowed values for the ' + 'reset keys in secure boot %s', self.path) + return set(mappings.SECUREBOOT_RESET_KEYS_MAP_REV) + + return set([mappings.SECUREBOOT_RESET_KEYS_MAP[v] for v in + set(mappings.SECUREBOOT_RESET_KEYS_MAP). + intersection(reset_keys_action.allowed_values)]) + + def reset_keys(self, target_value): + """Resets the secure boot keys. + + :param target_value: The target value to be set. + :raises: InvalidInputError, if the target value is not + allowed. + :raises: SushyError, on an error from iLO. + """ + valid_keys_resets = self.get_allowed_reset_keys_values() + if target_value not in valid_keys_resets: + msg = ('The parameter "%(parameter)s" value "%(target_value)s" is ' + 'invalid. Valid values are: %(valid_keys_reset_values)s' % + {'parameter': 'target_value', 'target_value': target_value, + 'valid_keys_reset_values': valid_keys_resets}) + raise exception.InvalidInputError(msg) + + value = mappings.SECUREBOOT_RESET_KEYS_MAP_REV[target_value] + target_uri = ( + self._get_reset_keys_action_element().target_uri) + + self._conn.post(target_uri, data={'ResetKeysType': value}) diff --git a/proliantutils/redfish/resources/system/system.py b/proliantutils/redfish/resources/system/system.py index cfeee0f..3e6c660 100644 --- a/proliantutils/redfish/resources/system/system.py +++ b/proliantutils/redfish/resources/system/system.py @@ -23,8 +23,10 @@ from proliantutils import log from proliantutils.redfish.resources.system import bios from proliantutils.redfish.resources.system import mappings from proliantutils.redfish.resources.system import pci_device +from proliantutils.redfish.resources.system import secure_boot from proliantutils.redfish import utils + LOG = log.get_logger(__name__) PERSISTENT_BOOT_DEVICE_MAP = { @@ -55,11 +57,10 @@ class HPESystem(system.System): """ _hpe_actions = HpeActionsField(['Oem', 'Hpe', 'Actions'], required=True) - """Oem specific system extensibility actions""" - _bios_settings = None - + _bios_settings = None # ref to BIOSSettings instance + _secure_boot = None # ref to SecureBoot instance _pci_devices = None def _get_hpe_push_power_button_action_element(self): @@ -95,7 +96,7 @@ class HPESystem(system.System): @property def bios_settings(self): - """Property to provide reference to bios_settings instance + """Property to provide reference to `BIOSSettings` instance It is calculated once when the first time it is queried. On refresh, this property gets reset. @@ -158,6 +159,22 @@ class HPESystem(system.System): self, ['Oem', 'Hpe', 'Links', 'PCIDevices'])) return self._pci_devices + @property + def secure_boot(self): + """Property to provide reference to `SecureBoot` instance + + It is calculated once when the first time it is queried. On refresh, + this property gets reset. + """ + if self._secure_boot is None: + self._secure_boot = secure_boot.SecureBoot( + self._conn, utils.get_subresource_path_by(self, 'SecureBoot'), + redfish_version=self.redfish_version) + + return self._secure_boot + def refresh(self): super(HPESystem, self).refresh() + self._bios_settings = None self._pci_devices = None + self._secure_boot = None diff --git a/proliantutils/tests/redfish/json_samples/secure_boot.json b/proliantutils/tests/redfish/json_samples/secure_boot.json new file mode 100644 index 0000000..9ba57e4 --- /dev/null +++ b/proliantutils/tests/redfish/json_samples/secure_boot.json @@ -0,0 +1,23 @@ +{ + "default": { + "@odata.context": "/redfish/v1/$metadata#Systems/1/SecureBoot", + "@odata.id": "/redfish/v1/Systems/1/SecureBoot", + "@odata.type": "#SecureBoot.v1_0_0.SecureBoot", + "Id": "SecureBoot", + "Name": "UEFI Secure Boot", + "Actions": { + "#SecureBoot.ResetKeys": { + "target": "/redfish/v1/Systems/1/SecureBoot/Actions/SecureBoot.ResetKeys", + "ResetKeysType@Redfish.AllowableValues": [ + "ResetAllKeysToDefault", + "DeleteAllKeys", + "DeletePK" + ] + }, + "Oem": {} + }, + "SecureBootEnable": false, + "SecureBootCurrentBoot": "Disabled", + "SecureBootMode": "UserMode" + } +} diff --git a/proliantutils/tests/redfish/resources/system/test_secure_boot.py b/proliantutils/tests/redfish/resources/system/test_secure_boot.py new file mode 100644 index 0000000..7657716 --- /dev/null +++ b/proliantutils/tests/redfish/resources/system/test_secure_boot.py @@ -0,0 +1,106 @@ +# Copyright 2017 Hewlett Packard Enterprise Development LP +# 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 proliantutils import exception +from proliantutils.redfish.resources.system import constants as sys_cons +from proliantutils.redfish.resources.system import secure_boot + + +class SecureBootTestCase(testtools.TestCase): + + def setUp(self): + super(SecureBootTestCase, self).setUp() + self.conn = mock.MagicMock() + with open('proliantutils/tests/redfish/' + 'json_samples/secure_boot.json', 'r') as f: + self.conn.get.return_value.json.return_value = ( + json.loads(f.read())['default']) + + self.secure_boot_inst = secure_boot.SecureBoot( + self.conn, '/redfish/v1/Systems/1/SecureBoot', + redfish_version='1.0.2') + + def test_field_attributes(self): + self.assertEqual('UEFI Secure Boot', self.secure_boot_inst.name) + self.assertEqual(sys_cons.SECUREBOOT_CURRENT_BOOT_DISABLED, + self.secure_boot_inst.current_boot) + self.assertFalse(self.secure_boot_inst.enable) + self.assertEqual('UserMode', self.secure_boot_inst.mode) + + def test_enable_secure_boot(self): + self.secure_boot_inst.enable_secure_boot(True) + self.secure_boot_inst._conn.patch.assert_called_once_with( + '/redfish/v1/Systems/1/SecureBoot', + data={'SecureBootEnable': True}) + + def test_enable_secure_boot_invalid_value(self): + self.assertRaisesRegex( + exception.InvalidInputError, + 'The parameter "secure_boot_enable" value "some-non-boolean" is ' + 'invalid. Valid values are: True/False.', + self.secure_boot_inst.enable_secure_boot, 'some-non-boolean') + + def test__get_reset_keys_action_element(self): + value = self.secure_boot_inst._get_reset_keys_action_element() + self.assertEqual('/redfish/v1/Systems/1/SecureBoot/Actions/' + 'SecureBoot.ResetKeys', + value.target_uri) + self.assertEqual(['ResetAllKeysToDefault', + 'DeleteAllKeys', + 'DeletePK'], value.allowed_values) + + def test__get_reset_keys_action_element_missing_action(self): + self.secure_boot_inst._actions.reset_keys = None + self.assertRaisesRegex( + exception.MissingAttributeError, + 'Actions/#SecureBoot.ResetKeys is missing', + self.secure_boot_inst._get_reset_keys_action_element) + + def test_get_allowed_reset_keys_values(self): + values = self.secure_boot_inst.get_allowed_reset_keys_values() + expected = set([sys_cons.SECUREBOOT_RESET_KEYS_DEFAULT, + sys_cons.SECUREBOOT_RESET_KEYS_DELETE_ALL, + sys_cons.SECUREBOOT_RESET_KEYS_DELETE_PK]) + self.assertEqual(expected, values) + self.assertIsInstance(values, set) + + @mock.patch.object(secure_boot.LOG, 'warning', autospec=True) + def test_get_allowed_reset_keys_values_no_values_specified( + self, mock_log): + self.secure_boot_inst._actions.reset_keys.allowed_values = None + values = self.secure_boot_inst.get_allowed_reset_keys_values() + # Assert it returns all values if it can't get the specific ones + expected = set([sys_cons.SECUREBOOT_RESET_KEYS_DEFAULT, + sys_cons.SECUREBOOT_RESET_KEYS_DELETE_ALL, + sys_cons.SECUREBOOT_RESET_KEYS_DELETE_PK]) + self.assertEqual(expected, values) + self.assertIsInstance(values, set) + self.assertEqual(1, mock_log.call_count) + + def test_reset_keys(self): + self.secure_boot_inst.reset_keys( + sys_cons.SECUREBOOT_RESET_KEYS_DEFAULT) + self.secure_boot_inst._conn.post.assert_called_once_with( + '/redfish/v1/Systems/1/SecureBoot/Actions/SecureBoot.ResetKeys', + data={'ResetKeysType': 'ResetAllKeysToDefault'}) + + def test_reset_keys_invalid_value(self): + self.assertRaises(exception.InvalidInputError, + self.secure_boot_inst.reset_keys, 'invalid-value') diff --git a/proliantutils/tests/redfish/resources/system/test_system.py b/proliantutils/tests/redfish/resources/system/test_system.py index 7365c02..6bef11d 100644 --- a/proliantutils/tests/redfish/resources/system/test_system.py +++ b/proliantutils/tests/redfish/resources/system/test_system.py @@ -22,6 +22,7 @@ import testtools from proliantutils import exception from proliantutils.redfish.resources.system import bios from proliantutils.redfish.resources.system import constants as sys_cons +from proliantutils.redfish.resources.system import secure_boot from proliantutils.redfish.resources.system import system from sushy.resources.system import system as sushy_system @@ -106,6 +107,33 @@ class HPESystemTestCase(testtools.TestCase): sushy.BOOT_SOURCE_TARGET_CD, enabled=sushy.BOOT_SOURCE_ENABLED_ONCE) + def test_bios_settings_on_refresh(self): + # | GIVEN | + with open('proliantutils/tests/redfish/json_samples/bios.json', + 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.sys_inst.bios_settings, + bios.BIOSSettings) + + # On refreshing the system instance... + with open('proliantutils/tests/redfish/' + 'json_samples/system.json', 'r') as f: + self.conn.get.return_value.json.return_value = ( + json.loads(f.read())['default']) + self.sys_inst.refresh() + + # | WHEN & THEN | + self.assertIsNone(self.sys_inst._bios_settings) + + # | GIVEN | + with open('proliantutils/tests/redfish/json_samples/bios.json', + 'r') as f: + self.conn.get.return_value.json.return_value = json.loads(f.read()) + # | WHEN & THEN | + self.assertIsInstance(self.sys_inst.bios_settings, + bios.BIOSSettings) + def test_update_persistent_boot_uefi_target(self): with open('proliantutils/tests/redfish/' 'json_samples/bios.json', 'r') as f: @@ -169,3 +197,66 @@ class HPESystemTestCase(testtools.TestCase): self.assertIs(actual_pci, self.sys_inst.pci_devices) self.conn.get.return_value.json.assert_not_called() + + def test_secure_boot_with_missing_path_attr(self): + def _get_secure_boot(): + return self.sys_inst.secure_boot + + self.sys_inst._json.pop('SecureBoot') + self.assertRaisesRegex( + exception.MissingAttributeError, + 'attribute SecureBoot is missing', + _get_secure_boot) + + def test_secure_boot(self): + # check for the underneath variable value + self.assertIsNone(self.sys_inst._secure_boot) + # | GIVEN | + self.conn.get.return_value.json.reset_mock() + with open('proliantutils/tests/redfish/json_samples/secure_boot.json', + 'r') as f: + self.conn.get.return_value.json.return_value = ( + json.loads(f.read())['default']) + # | WHEN | + actual_secure_boot = self.sys_inst.secure_boot + # | THEN | + self.assertIsInstance(actual_secure_boot, + secure_boot.SecureBoot) + 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_secure_boot, + self.sys_inst.secure_boot) + self.conn.get.return_value.json.assert_not_called() + + def test_secure_boot_on_refresh(self): + # | GIVEN | + with open('proliantutils/tests/redfish/json_samples/secure_boot.json', + 'r') as f: + self.conn.get.return_value.json.return_value = ( + json.loads(f.read())['default']) + # | WHEN & THEN | + self.assertIsInstance(self.sys_inst.secure_boot, + secure_boot.SecureBoot) + + # On refreshing the system instance... + with open('proliantutils/tests/redfish/' + 'json_samples/system.json', 'r') as f: + self.conn.get.return_value.json.return_value = ( + json.loads(f.read())['default']) + self.sys_inst.refresh() + + # | WHEN & THEN | + self.assertIsNone(self.sys_inst._secure_boot) + + # | GIVEN | + with open('proliantutils/tests/redfish/json_samples/secure_boot.json', + 'r') as f: + self.conn.get.return_value.json.return_value = ( + json.loads(f.read())['default']) + # | WHEN & THEN | + self.assertIsInstance(self.sys_inst.secure_boot, + secure_boot.SecureBoot) diff --git a/proliantutils/tests/redfish/test_redfish.py b/proliantutils/tests/redfish/test_redfish.py index 1d1222c..2896964 100644 --- a/proliantutils/tests/redfish/test_redfish.py +++ b/proliantutils/tests/redfish/test_redfish.py @@ -717,3 +717,136 @@ class RedfishOperationsTestCase(testtools.TestCase): exception.IloError, "The Redfish controller is unable to update bios settings" " to default", self.rf_client.reset_bios_to_default) + + @mock.patch.object(redfish.LOG, 'debug', autospec=True) + def test_get_secure_boot_mode(self, log_debug_mock): + sushy_system_mock = self.sushy.get_system.return_value + type(sushy_system_mock.secure_boot).current_boot = mock.PropertyMock( + return_value=sys_cons.SECUREBOOT_CURRENT_BOOT_ENABLED) + self.rf_client.get_secure_boot_mode() + log_debug_mock.assert_called_once_with( + '[iLO 1.2.3.4] Secure boot is Enabled') + + log_debug_mock.reset_mock() + type(sushy_system_mock.secure_boot).current_boot = mock.PropertyMock( + return_value=sys_cons.SECUREBOOT_CURRENT_BOOT_DISABLED) + self.rf_client.get_secure_boot_mode() + log_debug_mock.assert_called_once_with( + '[iLO 1.2.3.4] Secure boot is Disabled') + + def test_get_secure_boot_mode_on_fail(self): + sushy_system_mock = self.sushy.get_system.return_value + type(sushy_system_mock).secure_boot = mock.PropertyMock( + side_effect=sushy.exceptions.SushyError) + self.assertRaisesRegex( + exception.IloCommandNotSupportedError, + 'The Redfish controller failed to provide ' + 'information about secure boot on the server.', + self.rf_client.get_secure_boot_mode) + + @mock.patch.object(redfish.RedfishOperations, '_is_boot_mode_uefi', + autospec=True) + def test_set_secure_boot_mode(self, _is_boot_mode_uefi_mock): + _is_boot_mode_uefi_mock.return_value = True + self.rf_client.set_secure_boot_mode(True) + secure_boot_mock = self.sushy.get_system.return_value.secure_boot + secure_boot_mock.enable_secure_boot.assert_called_once_with(True) + + @mock.patch.object(redfish.RedfishOperations, '_is_boot_mode_uefi', + autospec=True) + def test_set_secure_boot_mode_in_bios(self, _is_boot_mode_uefi_mock): + _is_boot_mode_uefi_mock.return_value = False + self.assertRaisesRegex( + exception.IloCommandNotSupportedInBiosError, + 'System is not in UEFI boot mode. "SecureBoot" related resources ' + 'cannot be changed.', + self.rf_client.set_secure_boot_mode, True) + + @mock.patch.object(redfish.RedfishOperations, '_is_boot_mode_uefi', + autospec=True) + def test_set_secure_boot_mode_on_fail(self, _is_boot_mode_uefi_mock): + _is_boot_mode_uefi_mock.return_value = True + secure_boot_mock = self.sushy.get_system.return_value.secure_boot + secure_boot_mock.enable_secure_boot.side_effect = ( + sushy.exceptions.SushyError) + self.assertRaisesRegex( + exception.IloError, + 'The Redfish controller failed to set secure boot settings ' + 'on the server.', + self.rf_client.set_secure_boot_mode, True) + + @mock.patch.object(redfish.RedfishOperations, '_is_boot_mode_uefi', + autospec=True) + def test_set_secure_boot_mode_for_invalid_value( + self, _is_boot_mode_uefi_mock): + _is_boot_mode_uefi_mock.return_value = True + secure_boot_mock = self.sushy.get_system.return_value.secure_boot + secure_boot_mock.enable_secure_boot.side_effect = ( + exception.InvalidInputError('Invalid input')) + self.assertRaises( + exception.IloError, + self.rf_client.set_secure_boot_mode, 'some-non-boolean') + + @mock.patch.object(redfish.RedfishOperations, '_is_boot_mode_uefi', + autospec=True) + def test_reset_secure_boot_keys(self, _is_boot_mode_uefi_mock): + _is_boot_mode_uefi_mock.return_value = True + self.rf_client.reset_secure_boot_keys() + sushy_system_mock = self.sushy.get_system.return_value + sushy_system_mock.secure_boot.reset_keys.assert_called_once_with( + sys_cons.SECUREBOOT_RESET_KEYS_DEFAULT) + + @mock.patch.object(redfish.RedfishOperations, '_is_boot_mode_uefi', + autospec=True) + def test_reset_secure_boot_keys_in_bios(self, _is_boot_mode_uefi_mock): + _is_boot_mode_uefi_mock.return_value = False + self.assertRaisesRegex( + exception.IloCommandNotSupportedInBiosError, + 'System is not in UEFI boot mode. "SecureBoot" related resources ' + 'cannot be changed.', + self.rf_client.reset_secure_boot_keys) + + @mock.patch.object(redfish.RedfishOperations, '_is_boot_mode_uefi', + autospec=True) + def test_reset_secure_boot_keys_on_fail(self, _is_boot_mode_uefi_mock): + _is_boot_mode_uefi_mock.return_value = True + sushy_system_mock = self.sushy.get_system.return_value + sushy_system_mock.secure_boot.reset_keys.side_effect = ( + sushy.exceptions.SushyError) + self.assertRaisesRegex( + exception.IloError, + 'The Redfish controller failed to reset secure boot keys ' + 'on the server.', + self.rf_client.reset_secure_boot_keys) + + @mock.patch.object(redfish.RedfishOperations, '_is_boot_mode_uefi', + autospec=True) + def test_clear_secure_boot_keys(self, _is_boot_mode_uefi_mock): + _is_boot_mode_uefi_mock.return_value = True + self.rf_client.clear_secure_boot_keys() + sushy_system_mock = self.sushy.get_system.return_value + sushy_system_mock.secure_boot.reset_keys.assert_called_once_with( + sys_cons.SECUREBOOT_RESET_KEYS_DELETE_ALL) + + @mock.patch.object(redfish.RedfishOperations, '_is_boot_mode_uefi', + autospec=True) + def test_clear_secure_boot_keys_in_bios(self, _is_boot_mode_uefi_mock): + _is_boot_mode_uefi_mock.return_value = False + self.assertRaisesRegex( + exception.IloCommandNotSupportedInBiosError, + 'System is not in UEFI boot mode. "SecureBoot" related resources ' + 'cannot be changed.', + self.rf_client.clear_secure_boot_keys) + + @mock.patch.object(redfish.RedfishOperations, '_is_boot_mode_uefi', + autospec=True) + def test_clear_secure_boot_keys_on_fail(self, _is_boot_mode_uefi_mock): + _is_boot_mode_uefi_mock.return_value = True + sushy_system_mock = self.sushy.get_system.return_value + sushy_system_mock.secure_boot.reset_keys.side_effect = ( + sushy.exceptions.SushyError) + self.assertRaisesRegex( + exception.IloError, + 'The Redfish controller failed to clear secure boot keys ' + 'on the server.', + self.rf_client.clear_secure_boot_keys)