Changes for secure boot support for iLO drivers
This patch implements common changes to support secure boot for iLO drivers. Change-Id: I9b5baf5db0f87c09209cd2f38c1e61ae389363aa Implements: blueprint uefi-secure-boot
This commit is contained in:
parent
f0be66f96c
commit
f1e6bce0b2
@ -675,6 +675,21 @@ steps on the Ironic conductor node to configure PXE UEFI environment.
|
||||
|
||||
ironic node-update <node-uuid> add properties/capabilities='boot_mode:uefi'
|
||||
|
||||
#. For deploying signed images, update the Ironic node with ``secure_boot``
|
||||
capability in node's properties.
|
||||
field::
|
||||
|
||||
ironic node-update <node-uuid> add properties/capabilities='secure_boot:true'
|
||||
|
||||
#. Ensure the public key of the signed image is loaded into baremetal to deploy
|
||||
signed images.
|
||||
For HP Proliant Gen9 servers, one can enroll public key using iLO System
|
||||
Utilities UI. Please refer to section ``Accessing Secure Boot options`` in
|
||||
HP UEFI System Utilities User Guide http://www.hp.com/ctg/Manual/c04398276.pdf.
|
||||
Also, one can refer to white paper on Secure Boot on Linux for HP Proliant
|
||||
Servers at http://h20195.www2.hp.com/V2/getpdf.aspx/4AA5-4496ENW.pdf for
|
||||
more details.
|
||||
|
||||
#. Make sure that bare metal node is configured to boot in UEFI boot mode and
|
||||
boot device is set to network/pxe.
|
||||
|
||||
|
@ -1013,3 +1013,21 @@ def parse_root_device_hints(node):
|
||||
hints.append("%s=%s" % (key, value))
|
||||
|
||||
return ','.join(hints)
|
||||
|
||||
|
||||
def is_secure_boot_requested(node):
|
||||
"""Returns True if secure_boot is requested for deploy.
|
||||
|
||||
This method checks node property for secure_boot and returns True
|
||||
if it is requested.
|
||||
|
||||
:param node: a single Node.
|
||||
:raises: InvalidParameterValue if the capabilities string is not a
|
||||
dictionary or is malformed.
|
||||
:returns: True if secure_boot is requested.
|
||||
"""
|
||||
|
||||
capabilities = parse_instance_info_capabilities(node)
|
||||
sec_boot = capabilities.get('secure_boot', 'false').lower()
|
||||
|
||||
return sec_boot == 'true'
|
||||
|
@ -274,6 +274,96 @@ def _prepare_agent_vmedia_boot(task):
|
||||
_reboot_into(task, deploy_iso, deploy_ramdisk_opts)
|
||||
|
||||
|
||||
def _disable_secure_boot(task):
|
||||
"""Disables secure boot on node, if secure boot is enabled on node.
|
||||
|
||||
This method checks if secure boot is enabled on node. If enabled, it
|
||||
disables same and returns True.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:returns: It returns True, if secure boot was successfully disabled on
|
||||
the node.
|
||||
It returns False, if secure boot on node is in disabled state
|
||||
or if secure boot feature is not supported by the node.
|
||||
:raises: IloOperationError, if some operation on iLO failed.
|
||||
"""
|
||||
cur_sec_state = False
|
||||
try:
|
||||
cur_sec_state = ilo_common.get_secure_boot_mode(task)
|
||||
except exception.IloOperationNotSupported:
|
||||
LOG.debug('Secure boot mode is not supported for node %s',
|
||||
task.node.uuid)
|
||||
return False
|
||||
|
||||
if cur_sec_state:
|
||||
LOG.debug('Disabling secure boot for node %s', task.node.uuid)
|
||||
ilo_common.set_secure_boot_mode(task, False)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def _prepare_node_for_deploy(task):
|
||||
"""Common preparatory steps for all iLO drivers.
|
||||
|
||||
This method performs common preparatory steps required for all drivers.
|
||||
1. Power off node
|
||||
2. Disables secure boot, if it is in enabled state.
|
||||
3. Updates boot_mode capability to 'uefi' if secure boot is requested.
|
||||
4. Changes boot mode of the node if secure boot is disabled currently.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:raises: IloOperationError, if some operation on iLO failed.
|
||||
"""
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
|
||||
# Boot mode can be changed only if secure boot is in disabled state.
|
||||
# secure boot and boot mode cannot be changed together.
|
||||
change_boot_mode = True
|
||||
|
||||
# Disable secure boot on the node if it is in enabled state.
|
||||
if _disable_secure_boot(task):
|
||||
change_boot_mode = False
|
||||
|
||||
# Set boot_mode capability to uefi for secure boot
|
||||
if deploy_utils.is_secure_boot_requested(task.node):
|
||||
LOG.debug('Secure boot deploy requested for node %s', task.node.uuid)
|
||||
_enable_uefi_capability(task)
|
||||
|
||||
if change_boot_mode:
|
||||
ilo_common.update_boot_mode(task)
|
||||
|
||||
|
||||
def _update_secure_boot_mode(task, mode):
|
||||
"""Changes secure boot mode for next boot on the node.
|
||||
|
||||
This method changes secure boot mode on the node for next boot. It changes
|
||||
the secure boot mode setting on node only if the deploy has requested for
|
||||
the secure boot.
|
||||
During deploy, this method is used to enable secure boot on the node by
|
||||
passing 'mode' as 'True'.
|
||||
During teardown, this method is used to disable secure boot on the node by
|
||||
passing 'mode' as 'False'.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
:param mode: Boolean value requesting the next state for secure boot
|
||||
:raises: IloOperationNotSupported, if operation is not supported on iLO
|
||||
:raises: IloOperationError, if some operation on iLO failed.
|
||||
"""
|
||||
if deploy_utils.is_secure_boot_requested(task.node):
|
||||
ilo_common.set_secure_boot_mode(task, mode)
|
||||
LOG.info(_LI('Changed secure boot to %(mode)s for node %(node)s'),
|
||||
{'mode': mode, 'node': task.node.uuid})
|
||||
|
||||
|
||||
def _enable_uefi_capability(task):
|
||||
"""Adds capability boot_mode='uefi' into Node property.
|
||||
|
||||
:param task: a TaskManager instance containing the node to act on.
|
||||
"""
|
||||
driver_utils.rm_node_capability(task, 'boot_mode')
|
||||
driver_utils.add_node_capability(task, 'boot_mode', 'uefi')
|
||||
|
||||
|
||||
class IloVirtualMediaIscsiDeploy(base.DeployInterface):
|
||||
|
||||
def get_properties(self):
|
||||
|
@ -268,6 +268,143 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase):
|
||||
'deploy-iso-uuid',
|
||||
deploy_opts)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'is_secure_boot_requested')
|
||||
@mock.patch.object(ilo_common, 'set_secure_boot_mode')
|
||||
def test__update_secure_boot_passed_true(self,
|
||||
func_set_secure_boot_mode,
|
||||
func_is_secure_boot_requested):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
func_is_secure_boot_requested.return_value = True
|
||||
ilo_deploy._update_secure_boot_mode(task, True)
|
||||
func_set_secure_boot_mode.assert_called_once_with(task, True)
|
||||
|
||||
@mock.patch.object(deploy_utils, 'is_secure_boot_requested')
|
||||
@mock.patch.object(ilo_common, 'set_secure_boot_mode')
|
||||
def test__update_secure_boot_passed_False(self,
|
||||
func_set_secure_boot_mode,
|
||||
func_is_secure_boot_requested):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
func_is_secure_boot_requested.return_value = False
|
||||
ilo_deploy._update_secure_boot_mode(task, False)
|
||||
self.assertFalse(func_set_secure_boot_mode.called)
|
||||
|
||||
@mock.patch.object(driver_utils, 'add_node_capability')
|
||||
@mock.patch.object(driver_utils, 'rm_node_capability')
|
||||
def test__enable_uefi_capability(self, func_rm_node_capability,
|
||||
func_add_node_capability):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
ilo_deploy._enable_uefi_capability(task)
|
||||
func_rm_node_capability.assert_called_once_with(task,
|
||||
'boot_mode')
|
||||
func_add_node_capability.assert_called_once_with(task,
|
||||
'boot_mode',
|
||||
'uefi')
|
||||
|
||||
@mock.patch.object(ilo_common, 'set_secure_boot_mode')
|
||||
@mock.patch.object(ilo_common, 'get_secure_boot_mode')
|
||||
def test__disable_secure_boot_false(self,
|
||||
func_get_secure_boot_mode,
|
||||
func_set_secure_boot_mode):
|
||||
func_get_secure_boot_mode.return_value = False
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
returned_state = ilo_deploy._disable_secure_boot(task)
|
||||
func_get_secure_boot_mode.assert_called_once_with(task)
|
||||
self.assertFalse(func_set_secure_boot_mode.called)
|
||||
self.assertFalse(returned_state)
|
||||
|
||||
@mock.patch.object(ilo_common, 'set_secure_boot_mode')
|
||||
@mock.patch.object(ilo_common, 'get_secure_boot_mode')
|
||||
def test__disable_secure_boot_true(self,
|
||||
func_get_secure_boot_mode,
|
||||
func_set_secure_boot_mode):
|
||||
func_get_secure_boot_mode.return_value = True
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
returned_state = ilo_deploy._disable_secure_boot(task)
|
||||
func_get_secure_boot_mode.assert_called_once_with(task)
|
||||
func_set_secure_boot_mode.assert_called_once_with(task, False)
|
||||
self.assertTrue(returned_state)
|
||||
|
||||
@mock.patch.object(ilo_deploy, 'exception')
|
||||
@mock.patch.object(ilo_common, 'get_secure_boot_mode')
|
||||
def test__disable_secure_boot_exception(self,
|
||||
func_get_secure_boot_mode,
|
||||
exception_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
exception_mock.IloOperationNotSupported = Exception
|
||||
func_get_secure_boot_mode.side_effect = Exception
|
||||
returned_state = ilo_deploy._disable_secure_boot(task)
|
||||
func_get_secure_boot_mode.assert_called_once_with(task)
|
||||
self.assertFalse(returned_state)
|
||||
|
||||
@mock.patch.object(ilo_common, 'update_boot_mode')
|
||||
@mock.patch.object(deploy_utils, 'is_secure_boot_requested')
|
||||
@mock.patch.object(ilo_deploy, '_disable_secure_boot')
|
||||
@mock.patch.object(manager_utils, 'node_power_action')
|
||||
def test__prepare_node_for_deploy(self,
|
||||
func_node_power_action,
|
||||
func_disable_secure_boot,
|
||||
func_is_secure_boot_requested,
|
||||
func_update_boot_mode):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
func_disable_secure_boot.return_value = False
|
||||
func_is_secure_boot_requested.return_value = False
|
||||
ilo_deploy._prepare_node_for_deploy(task)
|
||||
func_node_power_action.assert_called_once_with(task,
|
||||
states.POWER_OFF)
|
||||
func_disable_secure_boot.assert_called_once_with(task)
|
||||
func_is_secure_boot_requested.assert_called_once_with(task.node)
|
||||
func_update_boot_mode.assert_called_once_with(task)
|
||||
|
||||
@mock.patch.object(ilo_common, 'update_boot_mode')
|
||||
@mock.patch.object(deploy_utils, 'is_secure_boot_requested')
|
||||
@mock.patch.object(ilo_deploy, '_disable_secure_boot')
|
||||
@mock.patch.object(manager_utils, 'node_power_action')
|
||||
def test__prepare_node_for_deploy_sec_boot_on(self,
|
||||
func_node_power_action,
|
||||
func_disable_secure_boot,
|
||||
func_is_secure_boot_req,
|
||||
func_update_boot_mode):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
func_disable_secure_boot.return_value = True
|
||||
func_is_secure_boot_req.return_value = False
|
||||
ilo_deploy._prepare_node_for_deploy(task)
|
||||
func_node_power_action.assert_called_once_with(task,
|
||||
states.POWER_OFF)
|
||||
func_disable_secure_boot.assert_called_once_with(task)
|
||||
func_is_secure_boot_req.assert_called_once_with(task.node)
|
||||
self.assertFalse(func_update_boot_mode.called)
|
||||
|
||||
@mock.patch.object(ilo_common, 'update_boot_mode')
|
||||
@mock.patch.object(ilo_deploy, '_enable_uefi_capability')
|
||||
@mock.patch.object(deploy_utils, 'is_secure_boot_requested')
|
||||
@mock.patch.object(ilo_deploy, '_disable_secure_boot')
|
||||
@mock.patch.object(manager_utils, 'node_power_action')
|
||||
def test__prepare_node_for_deploy_sec_boot_req(self,
|
||||
func_node_power_action,
|
||||
func_disable_secure_boot,
|
||||
func_is_secure_boot_req,
|
||||
func_enable_uefi_cap,
|
||||
func_update_boot_mode):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
func_disable_secure_boot.return_value = True
|
||||
func_is_secure_boot_req.return_value = True
|
||||
ilo_deploy._prepare_node_for_deploy(task)
|
||||
func_node_power_action.assert_called_once_with(task,
|
||||
states.POWER_OFF)
|
||||
func_disable_secure_boot.assert_called_once_with(task)
|
||||
func_is_secure_boot_req.assert_called_once_with(task.node)
|
||||
func_enable_uefi_cap.assert_called_once_with(task)
|
||||
self.assertFalse(func_update_boot_mode.called)
|
||||
|
||||
|
||||
class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase):
|
||||
|
||||
|
@ -1452,6 +1452,18 @@ class ParseInstanceInfoCapabilitiesTestCase(tests_base.TestCase):
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
utils.parse_instance_info_capabilities, self.node)
|
||||
|
||||
def test_is_secure_boot_requested_true(self):
|
||||
self.node.instance_info = {'capabilities': {"secure_boot": "true"}}
|
||||
self.assertTrue(utils.is_secure_boot_requested(self.node))
|
||||
|
||||
def test_is_secure_boot_requested_false(self):
|
||||
self.node.instance_info = {'capabilities': {"secure_boot": "false"}}
|
||||
self.assertFalse(utils.is_secure_boot_requested(self.node))
|
||||
|
||||
def test_is_secure_boot_requested_invalid(self):
|
||||
self.node.instance_info = {'capabilities': {"secure_boot": "invalid"}}
|
||||
self.assertFalse(utils.is_secure_boot_requested(self.node))
|
||||
|
||||
|
||||
class TrySetBootDeviceTestCase(db_base.DbTestCase):
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user