Added Redfish boot mode management
This change adds node boot mode management to the Redfish driver. Story: 1731013 Task: 9271 Change-Id: I3aa11cc0ce9da9cf6c801300370bd7ce420f434a
This commit is contained in:
@@ -17,6 +17,7 @@ from oslo_log import log
|
|||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
|
|
||||||
from ironic.common import boot_devices
|
from ironic.common import boot_devices
|
||||||
|
from ironic.common import boot_modes
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
from ironic.common.i18n import _
|
from ironic.common.i18n import _
|
||||||
from ironic.conductor import task_manager
|
from ironic.conductor import task_manager
|
||||||
@@ -37,6 +38,13 @@ if sushy:
|
|||||||
|
|
||||||
BOOT_DEVICE_MAP_REV = {v: k for k, v in BOOT_DEVICE_MAP.items()}
|
BOOT_DEVICE_MAP_REV = {v: k for k, v in BOOT_DEVICE_MAP.items()}
|
||||||
|
|
||||||
|
BOOT_MODE_MAP = {
|
||||||
|
sushy.BOOT_SOURCE_MODE_UEFI: boot_modes.UEFI,
|
||||||
|
sushy.BOOT_SOURCE_MODE_BIOS: boot_modes.LEGACY_BIOS
|
||||||
|
}
|
||||||
|
|
||||||
|
BOOT_MODE_MAP_REV = {v: k for k, v in BOOT_MODE_MAP.items()}
|
||||||
|
|
||||||
BOOT_DEVICE_PERSISTENT_MAP = {
|
BOOT_DEVICE_PERSISTENT_MAP = {
|
||||||
sushy.BOOT_SOURCE_ENABLED_CONTINUOUS: True,
|
sushy.BOOT_SOURCE_ENABLED_CONTINUOUS: True,
|
||||||
sushy.BOOT_SOURCE_ENABLED_ONCE: False
|
sushy.BOOT_SOURCE_ENABLED_ONCE: False
|
||||||
@@ -103,9 +111,7 @@ class RedfishManagement(base.ManagementInterface):
|
|||||||
:raises: RedfishError on an error from the Sushy library
|
:raises: RedfishError on an error from the Sushy library
|
||||||
"""
|
"""
|
||||||
system = redfish_utils.get_system(task.node)
|
system = redfish_utils.get_system(task.node)
|
||||||
# TODO(lucasagomes): set_system_boot_source() also supports mode
|
|
||||||
# for UEFI and BIOS we should get it from instance_info and pass
|
|
||||||
# it along this call
|
|
||||||
try:
|
try:
|
||||||
system.set_system_boot_source(
|
system.set_system_boot_source(
|
||||||
BOOT_DEVICE_MAP_REV[device],
|
BOOT_DEVICE_MAP_REV[device],
|
||||||
@@ -141,6 +147,81 @@ class RedfishManagement(base.ManagementInterface):
|
|||||||
'persistent': BOOT_DEVICE_PERSISTENT_MAP.get(
|
'persistent': BOOT_DEVICE_PERSISTENT_MAP.get(
|
||||||
system.boot.get('enabled'))}
|
system.boot.get('enabled'))}
|
||||||
|
|
||||||
|
def get_supported_boot_modes(self, task):
|
||||||
|
"""Get a list of the supported boot modes.
|
||||||
|
|
||||||
|
:param task: A task from TaskManager.
|
||||||
|
:returns: A list with the supported boot modes defined
|
||||||
|
in :mod:`ironic.common.boot_modes`. If boot
|
||||||
|
mode support can't be determined, empty list
|
||||||
|
is returned.
|
||||||
|
"""
|
||||||
|
return list(BOOT_MODE_MAP_REV)
|
||||||
|
|
||||||
|
@task_manager.require_exclusive_lock
|
||||||
|
def set_boot_mode(self, task, mode):
|
||||||
|
"""Set the boot mode for a node.
|
||||||
|
|
||||||
|
Set the boot mode to use on next reboot of the node.
|
||||||
|
|
||||||
|
:param task: A task from TaskManager.
|
||||||
|
:param mode: The boot mode, one of
|
||||||
|
:mod:`ironic.common.boot_modes`.
|
||||||
|
:raises: InvalidParameterValue if an invalid boot mode is
|
||||||
|
specified.
|
||||||
|
:raises: MissingParameterValue if a required parameter is missing
|
||||||
|
:raises: RedfishConnectionError when it fails to connect to Redfish
|
||||||
|
:raises: RedfishError on an error from the Sushy library
|
||||||
|
"""
|
||||||
|
system = redfish_utils.get_system(task.node)
|
||||||
|
|
||||||
|
boot_device = system.boot.get('target')
|
||||||
|
if not boot_device:
|
||||||
|
error_msg = (_('Cannot change boot mode on node %(node)s '
|
||||||
|
'because its boot device is not set.') %
|
||||||
|
{'node': task.node.uuid})
|
||||||
|
LOG.error(error_msg)
|
||||||
|
raise exception.RedfishError(error_msg)
|
||||||
|
|
||||||
|
boot_override = system.boot.get('enabled')
|
||||||
|
if not boot_override:
|
||||||
|
error_msg = (_('Cannot change boot mode on node %(node)s '
|
||||||
|
'because its boot source override is not set.') %
|
||||||
|
{'node': task.node.uuid})
|
||||||
|
LOG.error(error_msg)
|
||||||
|
raise exception.RedfishError(error_msg)
|
||||||
|
|
||||||
|
try:
|
||||||
|
system.set_system_boot_source(
|
||||||
|
boot_device,
|
||||||
|
enabled=boot_override,
|
||||||
|
mode=BOOT_MODE_MAP_REV[mode])
|
||||||
|
|
||||||
|
except sushy.exceptions.SushyError as e:
|
||||||
|
error_msg = (_('Setting boot mode to %(mode)s '
|
||||||
|
'failed for node %(node)s. '
|
||||||
|
'Error: %(error)s') %
|
||||||
|
{'node': task.node.uuid, 'mode': mode,
|
||||||
|
'error': e})
|
||||||
|
LOG.error(error_msg)
|
||||||
|
raise exception.RedfishError(error=error_msg)
|
||||||
|
|
||||||
|
def get_boot_mode(self, task):
|
||||||
|
"""Get the current boot mode for a node.
|
||||||
|
|
||||||
|
Provides the current boot mode of the node.
|
||||||
|
|
||||||
|
:param task: A task from TaskManager.
|
||||||
|
:raises: MissingParameterValue if a required parameter is missing
|
||||||
|
:raises: DriverOperationError or its derivative in case
|
||||||
|
of driver runtime error.
|
||||||
|
:returns: The boot mode, one of :mod:`ironic.common.boot_mode` or
|
||||||
|
None if it is unknown.
|
||||||
|
"""
|
||||||
|
system = redfish_utils.get_system(task.node)
|
||||||
|
|
||||||
|
return BOOT_MODE_MAP.get(system.boot.get('mode'))
|
||||||
|
|
||||||
def get_sensors_data(self, task):
|
def get_sensors_data(self, task):
|
||||||
"""Get sensors data.
|
"""Get sensors data.
|
||||||
|
|
||||||
|
@@ -17,6 +17,7 @@ import mock
|
|||||||
from oslo_utils import importutils
|
from oslo_utils import importutils
|
||||||
|
|
||||||
from ironic.common import boot_devices
|
from ironic.common import boot_devices
|
||||||
|
from ironic.common import boot_modes
|
||||||
from ironic.common import exception
|
from ironic.common import exception
|
||||||
from ironic.conductor import task_manager
|
from ironic.conductor import task_manager
|
||||||
from ironic.drivers.modules.redfish import management as redfish_mgmt
|
from ironic.drivers.modules.redfish import management as redfish_mgmt
|
||||||
@@ -153,6 +154,68 @@ class RedfishManagementTestCase(db_base.DbTestCase):
|
|||||||
'persistent': True}
|
'persistent': True}
|
||||||
self.assertEqual(expected, response)
|
self.assertEqual(expected, response)
|
||||||
|
|
||||||
|
def test_get_supported_boot_modes(self):
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
supported_boot_modes = (
|
||||||
|
task.driver.management.get_supported_boot_modes(task))
|
||||||
|
self.assertEqual(list(redfish_mgmt.BOOT_MODE_MAP_REV),
|
||||||
|
supported_boot_modes)
|
||||||
|
|
||||||
|
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||||
|
def test_set_boot_mode(self, mock_get_system):
|
||||||
|
fake_system = mock.Mock()
|
||||||
|
mock_get_system.return_value = fake_system
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
expected_values = [
|
||||||
|
(boot_modes.LEGACY_BIOS, sushy.BOOT_SOURCE_MODE_BIOS),
|
||||||
|
(boot_modes.UEFI, sushy.BOOT_SOURCE_MODE_UEFI)
|
||||||
|
]
|
||||||
|
|
||||||
|
for mode, expected in expected_values:
|
||||||
|
task.driver.management.set_boot_mode(task, mode=mode)
|
||||||
|
|
||||||
|
# Asserts
|
||||||
|
fake_system.set_system_boot_source.assert_called_once_with(
|
||||||
|
mock.ANY, enabled=mock.ANY, mode=mode)
|
||||||
|
mock_get_system.assert_called_once_with(task.node)
|
||||||
|
|
||||||
|
# Reset mocks
|
||||||
|
fake_system.set_system_boot_source.reset_mock()
|
||||||
|
mock_get_system.reset_mock()
|
||||||
|
|
||||||
|
@mock.patch('ironic.drivers.modules.redfish.management.sushy')
|
||||||
|
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||||
|
def test_set_boot_mode_fail(self, mock_get_system, mock_sushy):
|
||||||
|
fake_system = mock.Mock()
|
||||||
|
mock_sushy.exceptions.SushyError = MockedSushyError
|
||||||
|
fake_system.set_system_boot_source.side_effect = MockedSushyError
|
||||||
|
mock_get_system.return_value = fake_system
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=False) as task:
|
||||||
|
self.assertRaisesRegex(
|
||||||
|
exception.RedfishError, 'Setting boot mode',
|
||||||
|
task.driver.management.set_boot_mode, task, boot_modes.UEFI)
|
||||||
|
fake_system.set_system_boot_source.assert_called_once_with(
|
||||||
|
mock.ANY, enabled=mock.ANY, mode=boot_modes.UEFI)
|
||||||
|
mock_get_system.assert_called_once_with(task.node)
|
||||||
|
|
||||||
|
@mock.patch.object(redfish_utils, 'get_system', autospec=True)
|
||||||
|
def test_get_boot_mode(self, mock_get_system):
|
||||||
|
boot_attribute = {
|
||||||
|
'target': sushy.BOOT_SOURCE_TARGET_PXE,
|
||||||
|
'enabled': sushy.BOOT_SOURCE_ENABLED_CONTINUOUS,
|
||||||
|
'mode': sushy.BOOT_SOURCE_MODE_BIOS,
|
||||||
|
}
|
||||||
|
fake_system = mock.Mock(boot=boot_attribute)
|
||||||
|
mock_get_system.return_value = fake_system
|
||||||
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
|
shared=True) as task:
|
||||||
|
response = task.driver.management.get_boot_mode(task)
|
||||||
|
expected = boot_modes.LEGACY_BIOS
|
||||||
|
self.assertEqual(expected, response)
|
||||||
|
|
||||||
def test_get_sensors_data(self):
|
def test_get_sensors_data(self):
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=True) as task:
|
shared=True) as task:
|
||||||
|
@@ -163,6 +163,8 @@ SUSHY_CONSTANTS_SPEC = (
|
|||||||
'RESET_NMI',
|
'RESET_NMI',
|
||||||
'BOOT_SOURCE_ENABLED_CONTINUOUS',
|
'BOOT_SOURCE_ENABLED_CONTINUOUS',
|
||||||
'BOOT_SOURCE_ENABLED_ONCE',
|
'BOOT_SOURCE_ENABLED_ONCE',
|
||||||
|
'BOOT_SOURCE_MODE_BIOS',
|
||||||
|
'BOOT_SOURCE_MODE_UEFI',
|
||||||
)
|
)
|
||||||
|
|
||||||
XCLARITY_SPEC = (
|
XCLARITY_SPEC = (
|
||||||
|
@@ -241,7 +241,9 @@ if not sushy:
|
|||||||
RESET_FORCE_RESTART='force restart',
|
RESET_FORCE_RESTART='force restart',
|
||||||
RESET_NMI='nmi',
|
RESET_NMI='nmi',
|
||||||
BOOT_SOURCE_ENABLED_CONTINUOUS='continuous',
|
BOOT_SOURCE_ENABLED_CONTINUOUS='continuous',
|
||||||
BOOT_SOURCE_ENABLED_ONCE='once'
|
BOOT_SOURCE_ENABLED_ONCE='once',
|
||||||
|
BOOT_SOURCE_MODE_BIOS='bios',
|
||||||
|
BOOT_SOURCE_MODE_UEFI='uefi'
|
||||||
)
|
)
|
||||||
|
|
||||||
sys.modules['sushy'] = sushy
|
sys.modules['sushy'] = sushy
|
||||||
|
@@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Adds support for reading and setting bare metal node's boot mode
|
||||||
|
using the ``redfish`` management interface.
|
Reference in New Issue
Block a user