Secure boot support for irmc-pxe driver
This patch adds secure boot support for irmc-pxe boot interface as follows: - Implement secure boot support for irmc-pxe boot interface - Update version of python-scciclient supporting secure boot - Update irmc-pxe driver documentation Change-Id: Ie82ff07421d23b5c0d26e2d2fbde33fc9f8e3c42 Partial-Bug: #1694649
This commit is contained in:
parent
15c31b9023
commit
802c86ef04
@ -22,7 +22,7 @@ Prerequisites
|
||||
* Install `python-scciclient <https://pypi.python.org/pypi/python-scciclient>`_
|
||||
and `pysnmp <https://pypi.python.org/pypi/pysnmp>`_ packages::
|
||||
|
||||
$ pip install "python-scciclient>=0.4.0" pysnmp
|
||||
$ pip install "python-scciclient>=0.5.0" pysnmp
|
||||
|
||||
Drivers
|
||||
=======
|
||||
@ -55,6 +55,8 @@ Node configuration
|
||||
irmc_username.
|
||||
- ``properties/capabilities`` property to be ``boot_mode:uefi`` if
|
||||
UEFI boot is required.
|
||||
- ``properties/capabilities`` property to be ``boot_mode:uefi,secure_boot:true`` if
|
||||
UEFI Secure Boot is required.
|
||||
|
||||
* All of nodes are configured by setting the following configuration
|
||||
options in ``[irmc]`` section of ``/etc/ironic/ironic.conf``:
|
||||
|
@ -8,7 +8,7 @@ proliantutils>=2.2.1
|
||||
pysnmp
|
||||
python-ironic-inspector-client>=1.5.0
|
||||
python-oneviewclient<3.0.0,>=2.5.2
|
||||
python-scciclient>=0.4.0
|
||||
python-scciclient>=0.5.0
|
||||
UcsSdk==0.8.2.2
|
||||
python-dracclient>=1.3.0
|
||||
|
||||
|
@ -692,3 +692,39 @@ class IRMCPXEBoot(pxe.PXEBoot):
|
||||
irmc_management.backup_bios_config(task)
|
||||
|
||||
super(IRMCPXEBoot, self).prepare_ramdisk(task, ramdisk_params)
|
||||
|
||||
@METRICS.timer('IRMCPXEBoot.prepare_instance')
|
||||
def prepare_instance(self, task):
|
||||
"""Prepares the boot of instance.
|
||||
|
||||
This method prepares the boot of the instance after reading
|
||||
relevant information from the node's instance_info. In case of netboot,
|
||||
it updates the dhcp entries and switches the PXE config. In case of
|
||||
localboot, it cleans up the PXE config.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:returns: None
|
||||
:raises: IRMCOperationError, if some operation on iRMC failed.
|
||||
"""
|
||||
|
||||
super(IRMCPXEBoot, self).prepare_instance(task)
|
||||
node = task.node
|
||||
if deploy_utils.is_secure_boot_requested(node):
|
||||
irmc_common.set_secure_boot_mode(node, enable=True)
|
||||
|
||||
@METRICS.timer('IRMCPXEBoot.clean_up_instance')
|
||||
def clean_up_instance(self, task):
|
||||
"""Cleans up the boot of instance.
|
||||
|
||||
This method cleans up the environment that was setup for booting
|
||||
the instance. It unlinks the instance kernel/ramdisk in node's
|
||||
directory in tftproot and removes the PXE config.
|
||||
|
||||
:param task: a task from TaskManager.
|
||||
:raises: IRMCOperationError, if some operation on iRMC failed.
|
||||
:returns: None
|
||||
"""
|
||||
node = task.node
|
||||
if deploy_utils.is_secure_boot_requested(node):
|
||||
irmc_common.set_secure_boot_mode(node, enable=False)
|
||||
super(IRMCPXEBoot, self).clean_up_instance(task)
|
||||
|
@ -17,6 +17,7 @@ Common functionalities shared between different iRMC modules.
|
||||
"""
|
||||
import six
|
||||
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import importutils
|
||||
|
||||
from ironic.common import exception
|
||||
@ -24,7 +25,9 @@ from ironic.common.i18n import _
|
||||
from ironic.conf import CONF
|
||||
|
||||
scci = importutils.try_import('scciclient.irmc.scci')
|
||||
elcm = importutils.try_import('scciclient.irmc.elcm')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
REQUIRED_PROPERTIES = {
|
||||
'irmc_address': _("IP address or hostname of the iRMC. Required."),
|
||||
'irmc_username': _("Username for the iRMC with administrator privileges. "
|
||||
@ -195,3 +198,26 @@ def get_irmc_report(node):
|
||||
port=driver_info['irmc_port'],
|
||||
auth_method=driver_info['irmc_auth_method'],
|
||||
client_timeout=driver_info['irmc_client_timeout'])
|
||||
|
||||
|
||||
def set_secure_boot_mode(node, enable):
|
||||
"""Enable or disable UEFI Secure Boot
|
||||
|
||||
Enable or disable UEFI Secure Boot
|
||||
|
||||
:param node: An ironic node object.
|
||||
:param enable: Boolean value. True if the secure boot to be
|
||||
enabled.
|
||||
:raises: IRMCOperationError if the operation fails.
|
||||
"""
|
||||
driver_info = parse_driver_info(node)
|
||||
|
||||
try:
|
||||
elcm.set_secure_boot_mode(driver_info, enable)
|
||||
LOG.info("Set secure boot to %(flag)s for node %(node)s",
|
||||
{'flag': enable, 'node': node.uuid})
|
||||
except scci.SCCIError as irmc_exception:
|
||||
LOG.error("Failed to set secure boot to %(flag)s for node %(node)s",
|
||||
{'flag': enable, 'node': node.uuid})
|
||||
raise exception.IRMCOperationError(operation=_("set_secure_boot_mode"),
|
||||
error=irmc_exception)
|
||||
|
@ -1097,3 +1097,121 @@ class IRMCPXEBootTestCase(db_base.DbTestCase):
|
||||
self.assertFalse(mock_backup_bios.called)
|
||||
mock_parent_prepare.assert_called_once_with(
|
||||
task.driver.boot, task, {})
|
||||
|
||||
@mock.patch.object(irmc_common, 'set_secure_boot_mode', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance', spec_set=True,
|
||||
autospec=True)
|
||||
def test_prepare_instance_with_secure_boot(self, mock_prepare_instance,
|
||||
mock_set_secure_boot_mode):
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.instance_info = {
|
||||
'capabilities': {
|
||||
"secure_boot": "true"
|
||||
}
|
||||
}
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.boot.prepare_instance(task)
|
||||
mock_set_secure_boot_mode.assert_called_once_with(task.node,
|
||||
enable=True)
|
||||
mock_prepare_instance.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
|
||||
@mock.patch.object(irmc_common, 'set_secure_boot_mode', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance', spec_set=True,
|
||||
autospec=True)
|
||||
def test_prepare_instance_with_secure_boot_false(
|
||||
self, mock_prepare_instance, mock_set_secure_boot_mode):
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.instance_info = {
|
||||
'capabilities': {
|
||||
"secure_boot": "false"
|
||||
}
|
||||
}
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.boot.prepare_instance(task)
|
||||
self.assertFalse(mock_set_secure_boot_mode.called)
|
||||
mock_prepare_instance.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
|
||||
@mock.patch.object(irmc_common, 'set_secure_boot_mode', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'prepare_instance', spec_set=True,
|
||||
autospec=True)
|
||||
def test_prepare_instance_without_secure_boot(self, mock_prepare_instance,
|
||||
mock_set_secure_boot_mode):
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
self.node.target_provision_state = states.ACTIVE
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.boot.prepare_instance(task)
|
||||
self.assertFalse(mock_set_secure_boot_mode.called)
|
||||
mock_prepare_instance.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
|
||||
@mock.patch.object(irmc_common, 'set_secure_boot_mode', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_instance', spec_set=True,
|
||||
autospec=True)
|
||||
def test_clean_up_instance_with_secure_boot(self, mock_clean_up_instance,
|
||||
mock_set_secure_boot_mode):
|
||||
self.node.provision_state = states.CLEANING
|
||||
self.node.target_provision_state = states.AVAILABLE
|
||||
self.node.instance_info = {
|
||||
'capabilities': {
|
||||
"secure_boot": "true"
|
||||
}
|
||||
}
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.boot.clean_up_instance(task)
|
||||
mock_set_secure_boot_mode.assert_called_once_with(task.node,
|
||||
enable=False)
|
||||
mock_clean_up_instance.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
|
||||
@mock.patch.object(irmc_common, 'set_secure_boot_mode', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_instance', spec_set=True,
|
||||
autospec=True)
|
||||
def test_clean_up_instance_secure_boot_false(self, mock_clean_up_instance,
|
||||
mock_set_secure_boot_mode):
|
||||
self.node.provision_state = states.CLEANING
|
||||
self.node.target_provision_state = states.AVAILABLE
|
||||
self.node.instance_info = {
|
||||
'capabilities': {
|
||||
"secure_boot": "false"
|
||||
}
|
||||
}
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.boot.clean_up_instance(task)
|
||||
self.assertFalse(mock_set_secure_boot_mode.called)
|
||||
mock_clean_up_instance.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
|
||||
@mock.patch.object(irmc_common, 'set_secure_boot_mode', spec_set=True,
|
||||
autospec=True)
|
||||
@mock.patch.object(pxe.PXEBoot, 'clean_up_instance', spec_set=True,
|
||||
autospec=True)
|
||||
def test_clean_up_instance_without_secure_boot(
|
||||
self, mock_clean_up_instance, mock_set_secure_boot_mode):
|
||||
self.node.provision_state = states.CLEANING
|
||||
self.node.target_provision_state = states.AVAILABLE
|
||||
self.node.save()
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.boot.clean_up_instance(task)
|
||||
self.assertFalse(mock_set_secure_boot_mode.called)
|
||||
mock_clean_up_instance.assert_called_once_with(
|
||||
task.driver.boot, task)
|
||||
|
@ -210,3 +210,37 @@ class IRMCCommonMethodsTestCase(db_base.DbTestCase):
|
||||
def test_out_range_sensor_method(self):
|
||||
self.assertRaises(ValueError, cfg.CONF.set_override,
|
||||
'sensor_method', 'fake', 'irmc')
|
||||
|
||||
@mock.patch.object(irmc_common, 'elcm',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_ELCM_SPEC)
|
||||
def test_set_secure_boot_mode_enable(self, mock_elcm):
|
||||
mock_elcm.set_secure_boot_mode.return_value = 'set_secure_boot_mode'
|
||||
info = irmc_common.parse_driver_info(self.node)
|
||||
irmc_common.set_secure_boot_mode(self.node, True)
|
||||
mock_elcm.set_secure_boot_mode.assert_called_once_with(
|
||||
info, True)
|
||||
|
||||
@mock.patch.object(irmc_common, 'elcm',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_ELCM_SPEC)
|
||||
def test_set_secure_boot_mode_disable(self, mock_elcm):
|
||||
mock_elcm.set_secure_boot_mode.return_value = 'set_secure_boot_mode'
|
||||
info = irmc_common.parse_driver_info(self.node)
|
||||
irmc_common.set_secure_boot_mode(self.node, False)
|
||||
mock_elcm.set_secure_boot_mode.assert_called_once_with(
|
||||
info, False)
|
||||
|
||||
@mock.patch.object(irmc_common, 'elcm',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_ELCM_SPEC)
|
||||
@mock.patch.object(irmc_common, 'scci',
|
||||
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
|
||||
def test_set_secure_boot_mode_fail(self, mock_scci, mock_elcm):
|
||||
irmc_common.scci.SCCIError = Exception
|
||||
mock_elcm.set_secure_boot_mode.side_effect = Exception
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
self.assertRaises(exception.IRMCOperationError,
|
||||
irmc_common.set_secure_boot_mode,
|
||||
task.node, True)
|
||||
info = irmc_common.parse_driver_info(task.node)
|
||||
mock_elcm.set_secure_boot_mode.assert_called_once_with(
|
||||
info, True)
|
||||
|
@ -105,6 +105,7 @@ SCCICLIENT_IRMC_SCCI_SPEC = (
|
||||
SCCICLIENT_IRMC_ELCM_SPEC = (
|
||||
'backup_bios_config',
|
||||
'restore_bios_config',
|
||||
'set_secure_boot_mode',
|
||||
)
|
||||
|
||||
ONEVIEWCLIENT_SPEC = (
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds support to provision an instance in UEFI secure boot for
|
||||
``irmc-pxe`` boot interface.
|
Loading…
Reference in New Issue
Block a user