Secure boot support for agent_ilo driver
This patch implements secure boot support for agent_ilo driver. Implements: blueprint uefi-secure-boot Change-Id: Ic83edd5744ba33e25dc1667dec3534d389be4e76
This commit is contained in:
parent
2f04e74c9a
commit
f734945746
@ -20,7 +20,6 @@ from oslo_utils import importutils
|
||||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import agent
|
||||
from ironic.drivers.modules.ilo import deploy
|
||||
from ironic.drivers.modules.ilo import inspect
|
||||
from ironic.drivers.modules.ilo import management
|
||||
@ -71,5 +70,5 @@ class IloVirtualMediaAgentDriver(base.BaseDriver):
|
||||
self.deploy = deploy.IloVirtualMediaAgentDeploy()
|
||||
self.console = deploy.IloConsoleInterface()
|
||||
self.management = management.IloManagement()
|
||||
self.vendor = agent.AgentVendorInterface()
|
||||
self.vendor = deploy.IloVirtualMediaAgentVendorInterface()
|
||||
self.inspect = inspect.IloInspect()
|
||||
|
@ -438,7 +438,7 @@ class AgentVendorInterface(agent_base_vendor.BaseAgentVendor):
|
||||
LOG.debug('prepare_image got response %(res)s for node %(node)s',
|
||||
{'res': res, 'node': node.uuid})
|
||||
|
||||
def _check_deploy_success(self, node):
|
||||
def check_deploy_success(self, node):
|
||||
# should only ever be called after we've validated that
|
||||
# the prepare_image command is complete
|
||||
command = self._client.get_commands_status(node)[-1]
|
||||
@ -449,7 +449,7 @@ class AgentVendorInterface(agent_base_vendor.BaseAgentVendor):
|
||||
node = task.node
|
||||
LOG.debug('Preparing to reboot to instance for node %s',
|
||||
node.uuid)
|
||||
error = self._check_deploy_success(node)
|
||||
error = self.check_deploy_success(node)
|
||||
if error is not None:
|
||||
# TODO(jimrollenhagen) power off if using neutron dhcp to
|
||||
# align with pxe driver?
|
||||
|
@ -493,6 +493,9 @@ class IloVirtualMediaAgentDeploy(base.DeployInterface):
|
||||
:param task: a TaskManager instance
|
||||
:raises: MissingParameterValue if some parameters are missing.
|
||||
"""
|
||||
|
||||
driver_utils.validate_boot_mode_capability(task.node)
|
||||
driver_utils.validate_secure_boot_capability(task.node)
|
||||
_parse_driver_info(task.node)
|
||||
|
||||
@task_manager.require_exclusive_lock
|
||||
@ -520,6 +523,16 @@ class IloVirtualMediaAgentDeploy(base.DeployInterface):
|
||||
:returns: states.DELETED
|
||||
"""
|
||||
manager_utils.node_power_action(task, states.POWER_OFF)
|
||||
try:
|
||||
_update_secure_boot_mode(task, False)
|
||||
# We need to handle IloOperationNotSupported exception so that if
|
||||
# User had incorrectly specified the Node capability 'secure_boot'
|
||||
# to a node that do not have such capability and attempted deploy.
|
||||
# Handling this exception here, will help user to tear down such a
|
||||
# Node.
|
||||
except exception.IloOperationNotSupported:
|
||||
LOG.warn(_LW('Secure boot mode is not supported for node %s'),
|
||||
task.node.uuid)
|
||||
return states.DELETED
|
||||
|
||||
def prepare(self, task):
|
||||
@ -530,7 +543,7 @@ class IloVirtualMediaAgentDeploy(base.DeployInterface):
|
||||
node = task.node
|
||||
node.instance_info = agent.build_instance_info_for_deploy(task)
|
||||
node.save()
|
||||
ilo_common.update_boot_mode(task)
|
||||
_prepare_node_for_deploy(task)
|
||||
|
||||
def clean_up(self, task):
|
||||
"""Clean up the deployment environment for this node.
|
||||
@ -599,6 +612,22 @@ class IloVirtualMediaAgentDeploy(base.DeployInterface):
|
||||
provider.delete_cleaning_ports(task)
|
||||
|
||||
|
||||
class IloVirtualMediaAgentVendorInterface(agent.AgentVendorInterface):
|
||||
"""Interface for vendor passthru rateled actions."""
|
||||
|
||||
def reboot_to_instance(self, task, **kwargs):
|
||||
node = task.node
|
||||
LOG.debug('Preparing to reboot to instance for node %s',
|
||||
node.uuid)
|
||||
|
||||
error = self.check_deploy_success(node)
|
||||
if error is None:
|
||||
_update_secure_boot_mode(task, True)
|
||||
|
||||
super(IloVirtualMediaAgentVendorInterface,
|
||||
self).reboot_to_instance(task, **kwargs)
|
||||
|
||||
|
||||
class IloPXEDeploy(pxe.PXEDeploy):
|
||||
|
||||
def prepare(self, task):
|
||||
|
@ -593,12 +593,19 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase):
|
||||
self.node = obj_utils.create_test_node(self.context,
|
||||
driver='agent_ilo', driver_info=INFO_DICT)
|
||||
|
||||
@mock.patch.object(driver_utils, 'validate_secure_boot_capability')
|
||||
@mock.patch.object(driver_utils, 'validate_boot_mode_capability')
|
||||
@mock.patch.object(ilo_deploy, '_parse_driver_info')
|
||||
def test_validate(self, parse_driver_info_mock):
|
||||
def test_validate(self,
|
||||
parse_driver_info_mock,
|
||||
validate_boot_mode_mock,
|
||||
validate_secure_boot_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.validate(task)
|
||||
parse_driver_info_mock.assert_called_once_with(task.node)
|
||||
validate_boot_mode_mock.assert_called_once_with(task.node)
|
||||
validate_secure_boot_mock.assert_called_once_with(task.node)
|
||||
|
||||
@mock.patch.object(ilo_deploy, '_prepare_agent_vmedia_boot')
|
||||
def test_deploy(self, vmedia_boot_mock):
|
||||
@ -608,27 +615,51 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase):
|
||||
vmedia_boot_mock.assert_called_once_with(task)
|
||||
self.assertEqual(states.DEPLOYWAIT, returned_state)
|
||||
|
||||
@mock.patch.object(ilo_deploy, '_update_secure_boot_mode')
|
||||
@mock.patch.object(manager_utils, 'node_power_action')
|
||||
def test_tear_down(self, node_power_action_mock):
|
||||
def test_tear_down(self,
|
||||
node_power_action_mock,
|
||||
update_secure_boot_mode_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
returned_state = task.driver.deploy.tear_down(task)
|
||||
node_power_action_mock.assert_called_once_with(task,
|
||||
states.POWER_OFF)
|
||||
update_secure_boot_mode_mock.assert_called_once_with(task, False)
|
||||
self.assertEqual(states.DELETED, returned_state)
|
||||
|
||||
@mock.patch.object(ilo_common, 'update_boot_mode')
|
||||
@mock.patch.object(ilo_deploy.LOG, 'warn')
|
||||
@mock.patch.object(ilo_deploy, 'exception')
|
||||
@mock.patch.object(ilo_deploy, '_update_secure_boot_mode')
|
||||
@mock.patch.object(manager_utils, 'node_power_action')
|
||||
def test_tear_down_handle_exception(self,
|
||||
node_power_action_mock,
|
||||
update_secure_boot_mode_mock,
|
||||
exception_mock,
|
||||
mock_log):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
exception_mock.IloOperationNotSupported = Exception
|
||||
update_secure_boot_mode_mock.side_effect = Exception
|
||||
returned_state = task.driver.deploy.tear_down(task)
|
||||
node_power_action_mock.assert_called_once_with(task,
|
||||
states.POWER_OFF)
|
||||
update_secure_boot_mode_mock.assert_called_once_with(task, False)
|
||||
self.assertTrue(mock_log.called)
|
||||
self.assertEqual(states.DELETED, returned_state)
|
||||
|
||||
@mock.patch.object(ilo_deploy, '_prepare_node_for_deploy')
|
||||
@mock.patch.object(agent, 'build_instance_info_for_deploy')
|
||||
def test_prepare(self,
|
||||
build_instance_info_mock,
|
||||
update_boot_mode_mock):
|
||||
func_prepare_node_for_deploy):
|
||||
deploy_opts = {'a': 'b'}
|
||||
build_instance_info_mock.return_value = deploy_opts
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.deploy.prepare(task)
|
||||
update_boot_mode_mock.assert_called_once_with(task)
|
||||
self.assertEqual(deploy_opts, task.node.instance_info)
|
||||
func_prepare_node_for_deploy.assert_called_once_with(task)
|
||||
|
||||
@mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.delete_cleaning_ports')
|
||||
@mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.create_cleaning_ports')
|
||||
@ -1081,3 +1112,44 @@ class IloPXEVendorPassthruTestCase(db_base.DbTestCase):
|
||||
set_boot_device_mock.assert_called_with(task, boot_devices.PXE,
|
||||
True)
|
||||
pxe_vendorpassthru_mock.assert_called_once_with(task, **kwargs)
|
||||
|
||||
|
||||
class IloVirtualMediaAgentVendorInterfaceTestCase(db_base.DbTestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(IloVirtualMediaAgentVendorInterfaceTestCase, self).setUp()
|
||||
mgr_utils.mock_the_extension_manager(driver="agent_ilo")
|
||||
self.node = obj_utils.create_test_node(self.context,
|
||||
driver='agent_ilo', driver_info=INFO_DICT)
|
||||
|
||||
@mock.patch.object(agent.AgentVendorInterface, 'reboot_to_instance')
|
||||
@mock.patch.object(agent.AgentVendorInterface, 'check_deploy_success')
|
||||
@mock.patch.object(ilo_deploy, '_update_secure_boot_mode')
|
||||
def test_reboot_to_instance(self, func_update_secure_boot_mode,
|
||||
check_deploy_success_mock,
|
||||
agent_reboot_to_instance_mock):
|
||||
kwargs = {'address': '123456'}
|
||||
check_deploy_success_mock.return_value = None
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.vendor.reboot_to_instance(task, **kwargs)
|
||||
check_deploy_success_mock.called_once_with(task.node)
|
||||
func_update_secure_boot_mode.assert_called_once_with(task, True)
|
||||
agent_reboot_to_instance_mock.assert_called_once_with(task,
|
||||
**kwargs)
|
||||
|
||||
@mock.patch.object(agent.AgentVendorInterface, 'reboot_to_instance')
|
||||
@mock.patch.object(agent.AgentVendorInterface, 'check_deploy_success')
|
||||
@mock.patch.object(ilo_deploy, '_update_secure_boot_mode')
|
||||
def test_reboot_to_instance_deploy_fail(self, func_update_secure_boot_mode,
|
||||
check_deploy_success_mock,
|
||||
agent_reboot_to_instance_mock):
|
||||
kwargs = {'address': '123456'}
|
||||
check_deploy_success_mock.return_value = "Error"
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.driver.vendor.reboot_to_instance(task, **kwargs)
|
||||
check_deploy_success_mock.called_once_with(task.node)
|
||||
self.assertFalse(func_update_secure_boot_mode.called)
|
||||
agent_reboot_to_instance_mock.assert_called_once_with(task,
|
||||
**kwargs)
|
||||
|
@ -335,9 +335,9 @@ class TestAgentVendor(db_base.DbTestCase):
|
||||
@mock.patch('ironic.conductor.utils.node_power_action')
|
||||
@mock.patch('ironic.conductor.utils.node_set_boot_device')
|
||||
@mock.patch('ironic.drivers.modules.agent.AgentVendorInterface'
|
||||
'._check_deploy_success')
|
||||
'.check_deploy_success')
|
||||
def test_reboot_to_instance(self, check_deploy_mock, bootdev_mock,
|
||||
power_mock):
|
||||
power_mock):
|
||||
check_deploy_mock.return_value = None
|
||||
|
||||
self.node.provision_state = states.DEPLOYING
|
||||
|
Loading…
x
Reference in New Issue
Block a user