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:
Shivanand Tendulker 2015-02-23 23:48:19 -08:00 committed by Chris Krelle
parent 2f04e74c9a
commit f734945746
5 changed files with 112 additions and 12 deletions

View File

@ -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()

View File

@ -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?

View File

@ -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):

View File

@ -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)

View File

@ -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