Merge "Common framework for configuring secure boot"
This commit is contained in:
commit
2a7871d060
@ -972,6 +972,40 @@ class ManagementInterface(BaseInterface):
|
|||||||
raise exception.UnsupportedDriverExtension(
|
raise exception.UnsupportedDriverExtension(
|
||||||
driver=task.node.driver, extension='get_boot_mode')
|
driver=task.node.driver, extension='get_boot_mode')
|
||||||
|
|
||||||
|
def get_secure_boot_state(self, task):
|
||||||
|
"""Get the current secure boot state for the node.
|
||||||
|
|
||||||
|
NOTE: Not all drivers support this method. Older hardware
|
||||||
|
may not implement that.
|
||||||
|
|
||||||
|
: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.
|
||||||
|
:raises: UnsupportedDriverExtension if secure boot is
|
||||||
|
not supported by the driver or the hardware
|
||||||
|
:returns: Boolean
|
||||||
|
"""
|
||||||
|
raise exception.UnsupportedDriverExtension(
|
||||||
|
driver=task.node.driver, extension='get_secure_boot_state')
|
||||||
|
|
||||||
|
def set_secure_boot_state(self, task, state):
|
||||||
|
"""Set the current secure boot state for the node.
|
||||||
|
|
||||||
|
NOTE: Not all drivers support this method. Older hardware
|
||||||
|
may not implement that.
|
||||||
|
|
||||||
|
:param task: A task from TaskManager.
|
||||||
|
:param state: A new state as a boolean.
|
||||||
|
:raises: MissingParameterValue if a required parameter is missing
|
||||||
|
:raises: DriverOperationError or its derivative in case
|
||||||
|
of driver runtime error.
|
||||||
|
:raises: UnsupportedDriverExtension if secure boot is
|
||||||
|
not supported by the driver or the hardware
|
||||||
|
"""
|
||||||
|
raise exception.UnsupportedDriverExtension(
|
||||||
|
driver=task.node.driver, extension='set_secure_boot_state')
|
||||||
|
|
||||||
@abc.abstractmethod
|
@abc.abstractmethod
|
||||||
def get_sensors_data(self, task):
|
def get_sensors_data(self, task):
|
||||||
"""Get sensors data method.
|
"""Get sensors data method.
|
||||||
|
@ -14,11 +14,13 @@
|
|||||||
# under the License.
|
# under the License.
|
||||||
|
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
|
from oslo_utils import excutils
|
||||||
|
|
||||||
from ironic.common import boot_modes
|
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.common import utils as common_utils
|
from ironic.common import utils as common_utils
|
||||||
|
from ironic.conductor import task_manager
|
||||||
from ironic.conductor import utils as manager_utils
|
from ironic.conductor import utils as manager_utils
|
||||||
from ironic.conf import CONF
|
from ironic.conf import CONF
|
||||||
from ironic.drivers import utils as driver_utils
|
from ironic.drivers import utils as driver_utils
|
||||||
@ -296,3 +298,53 @@ def get_boot_mode(node):
|
|||||||
'bios': boot_modes.LEGACY_BIOS,
|
'bios': boot_modes.LEGACY_BIOS,
|
||||||
'uefi': boot_modes.UEFI})
|
'uefi': boot_modes.UEFI})
|
||||||
return CONF.deploy.default_boot_mode
|
return CONF.deploy.default_boot_mode
|
||||||
|
|
||||||
|
|
||||||
|
@task_manager.require_exclusive_lock
|
||||||
|
def configure_secure_boot_if_needed(task):
|
||||||
|
"""Configures secure boot if it has been requested for the node."""
|
||||||
|
if not is_secure_boot_requested(task.node):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
task.driver.management.set_secure_boot_state(task, True)
|
||||||
|
except exception.UnsupportedDriverExtension:
|
||||||
|
# TODO(dtantsur): make a failure in Xena
|
||||||
|
LOG.warning('Secure boot was requested for node %(node)s but its '
|
||||||
|
'management interface %(driver)s does not support it. '
|
||||||
|
'This warning will become an error in a future release.',
|
||||||
|
{'node': task.node.uuid,
|
||||||
|
'driver': task.node.management_interface})
|
||||||
|
except Exception as exc:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.error('Failed to configure secure boot for node %(node)s: '
|
||||||
|
'%(error)s',
|
||||||
|
{'node': task.node.uuid, 'error': exc},
|
||||||
|
exc_info=not isinstance(exc, exception.IronicException))
|
||||||
|
else:
|
||||||
|
LOG.info('Secure boot has been enabled for node %s', task.node.uuid)
|
||||||
|
|
||||||
|
|
||||||
|
@task_manager.require_exclusive_lock
|
||||||
|
def deconfigure_secure_boot_if_needed(task):
|
||||||
|
"""Deconfigures secure boot if it has been requested for the node."""
|
||||||
|
if not is_secure_boot_requested(task.node):
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
task.driver.management.set_secure_boot_state(task, False)
|
||||||
|
except exception.UnsupportedDriverExtension:
|
||||||
|
# NOTE(dtantsur): don't make it a hard failure to allow tearing down
|
||||||
|
# misconfigured nodes.
|
||||||
|
LOG.debug('Secure boot was requested for node %(node)s but its '
|
||||||
|
'management interface %(driver)s does not support it.',
|
||||||
|
{'node': task.node.uuid,
|
||||||
|
'driver': task.node.management_interface})
|
||||||
|
except Exception as exc:
|
||||||
|
with excutils.save_and_reraise_exception():
|
||||||
|
LOG.error('Failed to deconfigure secure boot for node %(node)s: '
|
||||||
|
'%(error)s',
|
||||||
|
{'node': task.node.uuid, 'error': exc},
|
||||||
|
exc_info=not isinstance(exc, exception.IronicException))
|
||||||
|
else:
|
||||||
|
LOG.info('Secure boot has been disabled for node %s', task.node.uuid)
|
||||||
|
@ -133,6 +133,8 @@ class PXEBaseMixin(object):
|
|||||||
pxe_utils.clean_up_pxe_env(task, images_info,
|
pxe_utils.clean_up_pxe_env(task, images_info,
|
||||||
ipxe_enabled=self.ipxe_enabled)
|
ipxe_enabled=self.ipxe_enabled)
|
||||||
|
|
||||||
|
boot_mode_utils.deconfigure_secure_boot_if_needed(task)
|
||||||
|
|
||||||
@METRICS.timer('PXEBaseMixin.prepare_ramdisk')
|
@METRICS.timer('PXEBaseMixin.prepare_ramdisk')
|
||||||
def prepare_ramdisk(self, task, ramdisk_params):
|
def prepare_ramdisk(self, task, ramdisk_params):
|
||||||
"""Prepares the boot of Ironic ramdisk using PXE.
|
"""Prepares the boot of Ironic ramdisk using PXE.
|
||||||
@ -240,6 +242,7 @@ class PXEBaseMixin(object):
|
|||||||
:returns: None
|
:returns: None
|
||||||
"""
|
"""
|
||||||
boot_mode_utils.sync_boot_mode(task)
|
boot_mode_utils.sync_boot_mode(task)
|
||||||
|
boot_mode_utils.configure_secure_boot_if_needed(task)
|
||||||
|
|
||||||
node = task.node
|
node = task.node
|
||||||
boot_option = deploy_utils.get_boot_option(node)
|
boot_option = deploy_utils.get_boot_option(node)
|
||||||
|
@ -16,8 +16,12 @@
|
|||||||
from unittest import mock
|
from unittest import mock
|
||||||
|
|
||||||
from ironic.common import boot_modes
|
from ironic.common import boot_modes
|
||||||
|
from ironic.common import exception
|
||||||
|
from ironic.conductor import task_manager
|
||||||
from ironic.drivers.modules import boot_mode_utils
|
from ironic.drivers.modules import boot_mode_utils
|
||||||
|
from ironic.drivers.modules import fake
|
||||||
from ironic.tests import base as tests_base
|
from ironic.tests import base as tests_base
|
||||||
|
from ironic.tests.unit.db import base as db_base
|
||||||
from ironic.tests.unit.objects import utils as obj_utils
|
from ironic.tests.unit.objects import utils as obj_utils
|
||||||
|
|
||||||
|
|
||||||
@ -64,3 +68,67 @@ class GetBootModeTestCase(tests_base.TestCase):
|
|||||||
boot_mode = boot_mode_utils.get_boot_mode(self.node)
|
boot_mode = boot_mode_utils.get_boot_mode(self.node)
|
||||||
self.assertEqual(boot_modes.UEFI, boot_mode)
|
self.assertEqual(boot_modes.UEFI, boot_mode)
|
||||||
self.assertEqual(0, mock_log.warning.call_count)
|
self.assertEqual(0, mock_log.warning.call_count)
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch.object(fake.FakeManagement, 'set_secure_boot_state', autospec=True)
|
||||||
|
class SecureBootTestCase(db_base.DbTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(SecureBootTestCase, self).setUp()
|
||||||
|
self.node = obj_utils.create_test_node(
|
||||||
|
self.context, driver='fake-hardware',
|
||||||
|
instance_info={'capabilities': {'secure_boot': 'true'}})
|
||||||
|
self.task = task_manager.TaskManager(self.context, self.node.id)
|
||||||
|
|
||||||
|
def test_configure_none_requested(self, mock_set_state):
|
||||||
|
self.task.node.instance_info = {}
|
||||||
|
boot_mode_utils.configure_secure_boot_if_needed(self.task)
|
||||||
|
self.assertFalse(mock_set_state.called)
|
||||||
|
|
||||||
|
@mock.patch.object(boot_mode_utils.LOG, 'warning', autospec=True)
|
||||||
|
def test_configure_unsupported(self, mock_warn, mock_set_state):
|
||||||
|
mock_set_state.side_effect = exception.UnsupportedDriverExtension
|
||||||
|
# Will become a failure in Xena
|
||||||
|
boot_mode_utils.configure_secure_boot_if_needed(self.task)
|
||||||
|
mock_set_state.assert_called_once_with(self.task.driver.management,
|
||||||
|
self.task, True)
|
||||||
|
self.assertTrue(mock_warn.called)
|
||||||
|
|
||||||
|
def test_configure_exception(self, mock_set_state):
|
||||||
|
mock_set_state.side_effect = RuntimeError('boom')
|
||||||
|
self.assertRaises(RuntimeError,
|
||||||
|
boot_mode_utils.configure_secure_boot_if_needed,
|
||||||
|
self.task)
|
||||||
|
mock_set_state.assert_called_once_with(self.task.driver.management,
|
||||||
|
self.task, True)
|
||||||
|
|
||||||
|
def test_configure(self, mock_set_state):
|
||||||
|
boot_mode_utils.configure_secure_boot_if_needed(self.task)
|
||||||
|
mock_set_state.assert_called_once_with(self.task.driver.management,
|
||||||
|
self.task, True)
|
||||||
|
|
||||||
|
def test_deconfigure_none_requested(self, mock_set_state):
|
||||||
|
self.task.node.instance_info = {}
|
||||||
|
boot_mode_utils.deconfigure_secure_boot_if_needed(self.task)
|
||||||
|
self.assertFalse(mock_set_state.called)
|
||||||
|
|
||||||
|
@mock.patch.object(boot_mode_utils.LOG, 'warning', autospec=True)
|
||||||
|
def test_deconfigure_unsupported(self, mock_warn, mock_set_state):
|
||||||
|
mock_set_state.side_effect = exception.UnsupportedDriverExtension
|
||||||
|
boot_mode_utils.deconfigure_secure_boot_if_needed(self.task)
|
||||||
|
mock_set_state.assert_called_once_with(self.task.driver.management,
|
||||||
|
self.task, False)
|
||||||
|
self.assertFalse(mock_warn.called)
|
||||||
|
|
||||||
|
def test_deconfigure(self, mock_set_state):
|
||||||
|
boot_mode_utils.deconfigure_secure_boot_if_needed(self.task)
|
||||||
|
mock_set_state.assert_called_once_with(self.task.driver.management,
|
||||||
|
self.task, False)
|
||||||
|
|
||||||
|
def test_deconfigure_exception(self, mock_set_state):
|
||||||
|
mock_set_state.side_effect = RuntimeError('boom')
|
||||||
|
self.assertRaises(RuntimeError,
|
||||||
|
boot_mode_utils.deconfigure_secure_boot_if_needed,
|
||||||
|
self.task)
|
||||||
|
mock_set_state.assert_called_once_with(self.task.driver.management,
|
||||||
|
self.task, False)
|
||||||
|
@ -34,6 +34,7 @@ from ironic.conductor import task_manager
|
|||||||
from ironic.conductor import utils as manager_utils
|
from ironic.conductor import utils as manager_utils
|
||||||
from ironic.drivers import base as drivers_base
|
from ironic.drivers import base as drivers_base
|
||||||
from ironic.drivers.modules import agent_base
|
from ironic.drivers.modules import agent_base
|
||||||
|
from ironic.drivers.modules import boot_mode_utils
|
||||||
from ironic.drivers.modules import deploy_utils
|
from ironic.drivers.modules import deploy_utils
|
||||||
from ironic.drivers.modules import ipxe
|
from ironic.drivers.modules import ipxe
|
||||||
from ironic.drivers.modules import pxe_base
|
from ironic.drivers.modules import pxe_base
|
||||||
@ -890,10 +891,13 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
|||||||
boot_devices.PXE,
|
boot_devices.PXE,
|
||||||
persistent=True)
|
persistent=True)
|
||||||
|
|
||||||
|
@mock.patch.object(boot_mode_utils, 'configure_secure_boot_if_needed',
|
||||||
|
autospec=True)
|
||||||
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
||||||
@mock.patch.object(pxe_utils, 'clean_up_pxe_config', autospec=True)
|
@mock.patch.object(pxe_utils, 'clean_up_pxe_config', autospec=True)
|
||||||
def test_prepare_instance_localboot(self, clean_up_pxe_config_mock,
|
def test_prepare_instance_localboot(self, clean_up_pxe_config_mock,
|
||||||
set_boot_device_mock):
|
set_boot_device_mock,
|
||||||
|
secure_boot_mock):
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
instance_info = task.node.instance_info
|
instance_info = task.node.instance_info
|
||||||
instance_info['capabilities'] = {'boot_option': 'local'}
|
instance_info['capabilities'] = {'boot_option': 'local'}
|
||||||
@ -905,6 +909,7 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
|||||||
set_boot_device_mock.assert_called_once_with(task,
|
set_boot_device_mock.assert_called_once_with(task,
|
||||||
boot_devices.DISK,
|
boot_devices.DISK,
|
||||||
persistent=True)
|
persistent=True)
|
||||||
|
secure_boot_mock.assert_called_once_with(task)
|
||||||
|
|
||||||
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
||||||
@mock.patch.object(pxe_utils, 'clean_up_pxe_config', autospec=True)
|
@mock.patch.object(pxe_utils, 'clean_up_pxe_config', autospec=True)
|
||||||
@ -957,10 +962,13 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
|||||||
self.assertFalse(cache_mock.called)
|
self.assertFalse(cache_mock.called)
|
||||||
self.assertFalse(dhcp_factory_mock.return_value.update_dhcp.called)
|
self.assertFalse(dhcp_factory_mock.return_value.update_dhcp.called)
|
||||||
|
|
||||||
|
@mock.patch.object(boot_mode_utils, 'deconfigure_secure_boot_if_needed',
|
||||||
|
autospec=True)
|
||||||
@mock.patch.object(pxe_utils, 'clean_up_pxe_env', autospec=True)
|
@mock.patch.object(pxe_utils, 'clean_up_pxe_env', autospec=True)
|
||||||
@mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True)
|
@mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True)
|
||||||
def test_clean_up_instance(self, get_image_info_mock,
|
def test_clean_up_instance(self, get_image_info_mock,
|
||||||
clean_up_pxe_env_mock):
|
clean_up_pxe_env_mock,
|
||||||
|
secure_boot_mock):
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
image_info = {'kernel': ['', '/path/to/kernel'],
|
image_info = {'kernel': ['', '/path/to/kernel'],
|
||||||
'ramdisk': ['', '/path/to/ramdisk']}
|
'ramdisk': ['', '/path/to/ramdisk']}
|
||||||
@ -970,6 +978,7 @@ class iPXEBootTestCase(db_base.DbTestCase):
|
|||||||
task, image_info, ipxe_enabled=True)
|
task, image_info, ipxe_enabled=True)
|
||||||
get_image_info_mock.assert_called_once_with(
|
get_image_info_mock.assert_called_once_with(
|
||||||
task, ipxe_enabled=True)
|
task, ipxe_enabled=True)
|
||||||
|
secure_boot_mock.assert_called_once_with(task)
|
||||||
|
|
||||||
|
|
||||||
@mock.patch.object(ipxe.iPXEBoot, '__init__', lambda self: None)
|
@mock.patch.object(ipxe.iPXEBoot, '__init__', lambda self: None)
|
||||||
|
@ -1238,7 +1238,7 @@ class CleanUpFullFlowTestCase(db_base.DbTestCase):
|
|||||||
mock_get_deploy_image_info.return_value = {}
|
mock_get_deploy_image_info.return_value = {}
|
||||||
|
|
||||||
with task_manager.acquire(self.context, self.node.uuid,
|
with task_manager.acquire(self.context, self.node.uuid,
|
||||||
shared=True) as task:
|
shared=False) as task:
|
||||||
task.driver.deploy.clean_up(task)
|
task.driver.deploy.clean_up(task)
|
||||||
mock_get_instance_image_info.assert_called_with(task,
|
mock_get_instance_image_info.assert_called_with(task,
|
||||||
ipxe_enabled=False)
|
ipxe_enabled=False)
|
||||||
|
@ -35,6 +35,7 @@ from ironic.conductor import task_manager
|
|||||||
from ironic.conductor import utils as manager_utils
|
from ironic.conductor import utils as manager_utils
|
||||||
from ironic.drivers import base as drivers_base
|
from ironic.drivers import base as drivers_base
|
||||||
from ironic.drivers.modules import agent_base
|
from ironic.drivers.modules import agent_base
|
||||||
|
from ironic.drivers.modules import boot_mode_utils
|
||||||
from ironic.drivers.modules import deploy_utils
|
from ironic.drivers.modules import deploy_utils
|
||||||
from ironic.drivers.modules import fake
|
from ironic.drivers.modules import fake
|
||||||
from ironic.drivers.modules import ipxe
|
from ironic.drivers.modules import ipxe
|
||||||
@ -694,10 +695,13 @@ class PXEBootTestCase(db_base.DbTestCase):
|
|||||||
set_boot_device_mock.assert_called_once_with(
|
set_boot_device_mock.assert_called_once_with(
|
||||||
task, boot_devices.DISK, persistent=True)
|
task, boot_devices.DISK, persistent=True)
|
||||||
|
|
||||||
|
@mock.patch.object(boot_mode_utils, 'configure_secure_boot_if_needed',
|
||||||
|
autospec=True)
|
||||||
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
||||||
@mock.patch.object(pxe_utils, 'clean_up_pxe_config', autospec=True)
|
@mock.patch.object(pxe_utils, 'clean_up_pxe_config', autospec=True)
|
||||||
def test_prepare_instance_localboot(self, clean_up_pxe_config_mock,
|
def test_prepare_instance_localboot(self, clean_up_pxe_config_mock,
|
||||||
set_boot_device_mock):
|
set_boot_device_mock,
|
||||||
|
secure_boot_mock):
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
instance_info = task.node.instance_info
|
instance_info = task.node.instance_info
|
||||||
instance_info['capabilities'] = {'boot_option': 'local'}
|
instance_info['capabilities'] = {'boot_option': 'local'}
|
||||||
@ -709,6 +713,7 @@ class PXEBootTestCase(db_base.DbTestCase):
|
|||||||
set_boot_device_mock.assert_called_once_with(task,
|
set_boot_device_mock.assert_called_once_with(task,
|
||||||
boot_devices.DISK,
|
boot_devices.DISK,
|
||||||
persistent=True)
|
persistent=True)
|
||||||
|
secure_boot_mock.assert_called_once_with(task)
|
||||||
|
|
||||||
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
||||||
@mock.patch.object(pxe_utils, 'clean_up_pxe_config', autospec=True)
|
@mock.patch.object(pxe_utils, 'clean_up_pxe_config', autospec=True)
|
||||||
@ -784,10 +789,13 @@ class PXEBootTestCase(db_base.DbTestCase):
|
|||||||
def test_prepare_instance_ramdisk_pxe_conf_exists(self):
|
def test_prepare_instance_ramdisk_pxe_conf_exists(self):
|
||||||
self._test_prepare_instance_ramdisk(config_file_exits=False)
|
self._test_prepare_instance_ramdisk(config_file_exits=False)
|
||||||
|
|
||||||
|
@mock.patch.object(boot_mode_utils, 'deconfigure_secure_boot_if_needed',
|
||||||
|
autospec=True)
|
||||||
@mock.patch.object(pxe_utils, 'clean_up_pxe_env', autospec=True)
|
@mock.patch.object(pxe_utils, 'clean_up_pxe_env', autospec=True)
|
||||||
@mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True)
|
@mock.patch.object(pxe_utils, 'get_instance_image_info', autospec=True)
|
||||||
def test_clean_up_instance(self, get_image_info_mock,
|
def test_clean_up_instance(self, get_image_info_mock,
|
||||||
clean_up_pxe_env_mock):
|
clean_up_pxe_env_mock,
|
||||||
|
secure_boot_mock):
|
||||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||||
image_info = {'kernel': ['', '/path/to/kernel'],
|
image_info = {'kernel': ['', '/path/to/kernel'],
|
||||||
'ramdisk': ['', '/path/to/ramdisk']}
|
'ramdisk': ['', '/path/to/ramdisk']}
|
||||||
@ -797,6 +805,7 @@ class PXEBootTestCase(db_base.DbTestCase):
|
|||||||
ipxe_enabled=False)
|
ipxe_enabled=False)
|
||||||
get_image_info_mock.assert_called_once_with(task,
|
get_image_info_mock.assert_called_once_with(task,
|
||||||
ipxe_enabled=False)
|
ipxe_enabled=False)
|
||||||
|
secure_boot_mock.assert_called_once_with(task)
|
||||||
|
|
||||||
|
|
||||||
class PXERamdiskDeployTestCase(db_base.DbTestCase):
|
class PXERamdiskDeployTestCase(db_base.DbTestCase):
|
||||||
|
16
releasenotes/notes/secure-boot-cf1c134bfb75768d.yaml
Normal file
16
releasenotes/notes/secure-boot-cf1c134bfb75768d.yaml
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- |
|
||||||
|
The ``pxe`` and ``ipxe`` boot interfaces now automatically configure
|
||||||
|
secure boot if the management interface supports it.
|
||||||
|
deprecations:
|
||||||
|
- |
|
||||||
|
Currently the bare metal API permits setting the ``secure_boot`` capability
|
||||||
|
for nodes, which driver does not support setting secure boot. This is
|
||||||
|
deprecated and will become a failure in the Xena cycle.
|
||||||
|
other:
|
||||||
|
- |
|
||||||
|
Extends ``ManagementInterface`` with two new calls:
|
||||||
|
``get_secure_boot_state`` and ``set_secure_boot_state``. They are
|
||||||
|
optional and may be implemented for hardware that supports dynamically
|
||||||
|
enabling/disabling secure boot.
|
Loading…
x
Reference in New Issue
Block a user