Add `set_system_boot_options` method

Some BMC implementations do not support changing all attributes
of the Redfish `Boot` object. To address that, this patch adds
a new `set_system_boot_options` method to the `System` object
superseding the set_system_boot_source` method.

The new method has all boot parameters optional to allow for
more atomicity when PATCH'ing Redfish `Boot` object. When
sending HTTP PATCH request, this new method will only include
those items into the JSON document, that are explicitly passed
by the user.

This change might improve interoperability with BMCs that do
not handle certain attributes of the `Boot` object.

Change-Id: I4fa9f718de99def4da162acd4636a17d53ee9a51
Story: 2007355
Task: 38907
This commit is contained in:
Ilya Etingof 2020-03-02 13:09:21 +01:00
parent cf7d87ff85
commit 97cee86a2f
3 changed files with 124 additions and 27 deletions

View File

@ -0,0 +1,10 @@
---
fixes:
- |
Adds a new ``set_system_boot_options`` method to the ``System`` object
superseding the ``set_system_boot_source`` method. The new method has
all boot parameters optional to allow for more atomicity when PATCH'ing
Redfish ``Boot`` object. The new method will only include those items in
the PATCH document, that are explicitly passed by the user. This change
might improve interoperability with BMCs that do not handle certain
attributes of the ``Boot`` object.

View File

@ -16,6 +16,7 @@
# This is referred from Redfish standard schema.
# https://redfish.dmtf.org/schemas/ComputerSystem.v1_5_0.json
import collections
import logging
from sushy import exceptions
@ -207,41 +208,44 @@ class System(base.ResourceBase):
set(sys_maps.BOOT_SOURCE_TARGET_MAP).
intersection(self.boot.allowed_values)])
def set_system_boot_source(self, target,
enabled=sys_cons.BOOT_SOURCE_ENABLED_ONCE,
mode=None):
"""Set the boot source.
def set_system_boot_options(self, target=None, enabled=None, mode=None):
"""Set boot source and/or boot frequency and/or boot mode.
Set the boot source to use on next reboot of the System.
Set the boot source and/or boot frequency and/or boot mode to use
on next reboot of the System.
:param target: The target boot source.
:param target: The target boot source, optional.
:param enabled: The frequency, whether to set it for the next
reboot only (BOOT_SOURCE_ENABLED_ONCE) or persistent to all
future reboots (BOOT_SOURCE_ENABLED_CONTINUOUS) or disabled
(BOOT_SOURCE_ENABLED_DISABLED).
:param mode: The boot mode, UEFI (BOOT_SOURCE_MODE_UEFI) or
BIOS (BOOT_SOURCE_MODE_BIOS).
(BOOT_SOURCE_ENABLED_DISABLED), optional.
:param mode: The boot mode (UEFI: BOOT_SOURCE_MODE_UEFI or
BIOS: BOOT_SOURCE_MODE_BIOS), optional.
:raises: InvalidParameterValueError, if any information passed is
invalid.
"""
valid_targets = self.get_allowed_system_boot_source_values()
if target not in valid_targets:
raise exceptions.InvalidParameterValueError(
parameter='target', value=target, valid_values=valid_targets)
data = collections.defaultdict(dict)
if enabled not in sys_maps.BOOT_SOURCE_ENABLED_MAP_REV:
raise exceptions.InvalidParameterValueError(
parameter='enabled', value=enabled,
valid_values=list(sys_maps.BOOT_SOURCE_ENABLED_MAP_REV))
if target is not None:
valid_targets = self.get_allowed_system_boot_source_values()
if target not in valid_targets:
raise exceptions.InvalidParameterValueError(
parameter='target', value=target,
valid_values=valid_targets)
data = {
'Boot': {
'BootSourceOverrideTarget':
sys_maps.BOOT_SOURCE_TARGET_MAP_REV[target],
'BootSourceOverrideEnabled':
sys_maps.BOOT_SOURCE_ENABLED_MAP_REV[enabled]
}
}
fishy_target = sys_maps.BOOT_SOURCE_TARGET_MAP_REV[target]
data['Boot']['BootSourceOverrideTarget'] = fishy_target
if enabled is not None:
if enabled not in sys_maps.BOOT_SOURCE_ENABLED_MAP_REV:
raise exceptions.InvalidParameterValueError(
parameter='enabled', value=enabled,
valid_values=list(sys_maps.BOOT_SOURCE_ENABLED_MAP_REV))
fishy_freq = sys_maps.BOOT_SOURCE_ENABLED_MAP_REV[enabled]
data['Boot']['BootSourceOverrideEnabled'] = fishy_freq
if mode is not None:
if mode not in sys_maps.BOOT_SOURCE_MODE_MAP_REV:
@ -249,13 +253,38 @@ class System(base.ResourceBase):
parameter='mode', value=mode,
valid_values=list(sys_maps.BOOT_SOURCE_MODE_MAP_REV))
data['Boot']['BootSourceOverrideMode'] = (
sys_maps.BOOT_SOURCE_MODE_MAP_REV[mode])
fishy_mode = sys_maps.BOOT_SOURCE_MODE_MAP_REV[mode]
data['Boot']['BootSourceOverrideMode'] = fishy_mode
# TODO(lucasagomes): Check the return code and response body ?
# Probably we should call refresh() as well.
self._conn.patch(self.path, data=data)
# TODO(etingof): we should remove this method, eventually
def set_system_boot_source(
self, target, enabled=sys_cons.BOOT_SOURCE_ENABLED_ONCE,
mode=None):
"""Set boot source and/or boot frequency and/or boot mode.
Set the boot source and/or boot frequency and/or boot mode to use
on next reboot of the System.
This method is obsoleted by `set_system_boot_options`.
:param target: The target boot source.
:param enabled: The frequency, whether to set it for the next
reboot only (BOOT_SOURCE_ENABLED_ONCE) or persistent to all
future reboots (BOOT_SOURCE_ENABLED_CONTINUOUS) or disabled
(BOOT_SOURCE_ENABLED_DISABLED).
Default is `BOOT_SOURCE_ENABLED_ONCE`.
:param mode: The boot mode (UEFI: BOOT_SOURCE_MODE_UEFI or
BIOS: BOOT_SOURCE_MODE_BIOS), optional.
:raises: InvalidParameterValueError, if any information passed is
invalid.
"""
self.set_system_boot_options(target, enabled, mode)
def set_indicator_led(self, state):
"""Set IndicatorLED to the given state.

View File

@ -226,6 +226,64 @@ class SystemTestCase(base.TestCase):
self.assertIsInstance(values, set)
self.assertEqual(1, mock_log.call_count)
def test_set_system_boot_options(self):
self.sys_inst.set_system_boot_options(
sushy.BOOT_SOURCE_TARGET_PXE,
enabled=sushy.BOOT_SOURCE_ENABLED_CONTINUOUS,
mode=sushy.BOOT_SOURCE_MODE_UEFI)
self.sys_inst._conn.patch.assert_called_once_with(
'/redfish/v1/Systems/437XR1138R2',
data={'Boot': {'BootSourceOverrideEnabled': 'Continuous',
'BootSourceOverrideTarget': 'Pxe',
'BootSourceOverrideMode': 'UEFI'}})
def test_set_system_boot_options_no_mode_specified(self):
self.sys_inst.set_system_boot_options(
sushy.BOOT_SOURCE_TARGET_HDD,
enabled=sushy.BOOT_SOURCE_ENABLED_ONCE)
self.sys_inst._conn.patch.assert_called_once_with(
'/redfish/v1/Systems/437XR1138R2',
data={'Boot': {'BootSourceOverrideEnabled': 'Once',
'BootSourceOverrideTarget': 'Hdd'}})
def test_set_system_boot_options_no_target_specified(self):
self.sys_inst.set_system_boot_options(
enabled=sushy.BOOT_SOURCE_ENABLED_CONTINUOUS,
mode=sushy.BOOT_SOURCE_MODE_UEFI)
self.sys_inst._conn.patch.assert_called_once_with(
'/redfish/v1/Systems/437XR1138R2',
data={'Boot': {'BootSourceOverrideEnabled': 'Continuous',
'BootSourceOverrideMode': 'UEFI'}})
def test_set_system_boot_options_no_freq_specified(self):
self.sys_inst.set_system_boot_options(
target=sushy.BOOT_SOURCE_TARGET_PXE,
mode=sushy.BOOT_SOURCE_MODE_UEFI)
self.sys_inst._conn.patch.assert_called_once_with(
'/redfish/v1/Systems/437XR1138R2',
data={'Boot': {'BootSourceOverrideTarget': 'Pxe',
'BootSourceOverrideMode': 'UEFI'}})
def test_set_system_boot_options_nothing_specified(self):
self.sys_inst.set_system_boot_options()
self.sys_inst._conn.patch.assert_called_once_with(
'/redfish/v1/Systems/437XR1138R2', data={})
def test_set_system_boot_options_invalid_target(self):
self.assertRaises(exceptions.InvalidParameterValueError,
self.sys_inst.set_system_boot_source,
'invalid-target')
def test_set_system_boot_options_invalid_enabled(self):
with self.assertRaisesRegex(
exceptions.InvalidParameterValueError,
'"enabled" value.*{0}'.format(
list(sys_map.BOOT_SOURCE_ENABLED_MAP_REV))):
self.sys_inst.set_system_boot_options(
sushy.BOOT_SOURCE_TARGET_HDD,
enabled='invalid-enabled')
def test_set_system_boot_source(self):
self.sys_inst.set_system_boot_source(
sushy.BOOT_SOURCE_TARGET_PXE,