From 2aead4f422d90907997c283cf929fcb25abdbf58 Mon Sep 17 00:00:00 2001 From: Anusha Ramineni Date: Mon, 16 Mar 2015 20:13:32 +0530 Subject: [PATCH] Support agent_ilo driver to perform cleaning Add support for agent_ilo driver to perform in-band cleaning operations. Implements: blueprint ilo-cleaning-support Change-Id: Ie34e30a163e63675dfd158f706dbc324cfd044bb --- etc/ironic/ironic.conf.sample | 11 +++ ironic/drivers/modules/ilo/deploy.py | 70 ++++++++++++++++++- ironic/tests/drivers/ilo/test_deploy.py | 89 +++++++++++++++++++++---- 3 files changed, 155 insertions(+), 15 deletions(-) diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample index 27440d3839..3510e40433 100644 --- a/etc/ironic/ironic.conf.sample +++ b/etc/ironic/ironic.conf.sample @@ -357,6 +357,7 @@ # set to 0, will not run during cleaning. (integer value) #agent_erase_devices_priority= + # # Options defined in ironic.drivers.modules.agent_base_vendor # @@ -840,6 +841,16 @@ #swift_object_expiry_timeout=900 +# +# Options defined in ironic.drivers.modules.ilo.deploy +# + +# Priority for erase devices clean step. If unset, it defaults +# to 10. If set to 0, the step will be disabled and will not +# run during cleaning. (integer value) +#clean_priority_erase_devices= + + # # Options defined in ironic.drivers.modules.ilo.management # diff --git a/ironic/drivers/modules/ilo/deploy.py b/ironic/drivers/modules/ilo/deploy.py index 41e8f6e9c7..b847b7f12e 100644 --- a/ironic/drivers/modules/ilo/deploy.py +++ b/ironic/drivers/modules/ilo/deploy.py @@ -21,6 +21,7 @@ from oslo_config import cfg from oslo_utils import excutils from ironic.common import boot_devices +from ironic.common import dhcp_factory from ironic.common import exception from ironic.common.glance_service import service_utils from ironic.common.i18n import _ @@ -48,6 +49,13 @@ LOG = logging.getLogger(__name__) CONF = cfg.CONF +clean_opts = [ + cfg.IntOpt('clean_priority_erase_devices', + help='Priority for erase devices clean step. If unset, ' + 'it defaults to 10. If set to 0, the step will be ' + 'disabled and will not run during cleaning.') + ] + REQUIRED_PROPERTIES = { 'ilo_deploy_iso': _("UUID (from Glance) of the deployment ISO. " "Required.") @@ -58,6 +66,7 @@ CONF.import_opt('pxe_append_params', 'ironic.drivers.modules.iscsi_deploy', group='pxe') CONF.import_opt('swift_ilo_container', 'ironic.drivers.modules.ilo.common', group='ilo') +CONF.register_opts(clean_opts, group='ilo') def _get_boot_iso_object_name(node): @@ -261,6 +270,14 @@ def _reboot_into(task, iso, ramdisk_options): manager_utils.node_power_action(task, states.REBOOT) +def _prepare_agent_vmedia_boot(task): + """prepare for vmedia boot.""" + + deploy_ramdisk_opts = agent.build_agent_options(task.node) + deploy_iso = task.node.driver_info['ilo_deploy_iso'] + _reboot_into(task, deploy_iso, deploy_ramdisk_opts) + + class IloVirtualMediaIscsiDeploy(base.DeployInterface): def get_properties(self): @@ -388,9 +405,7 @@ class IloVirtualMediaAgentDeploy(base.DeployInterface): image. :raises: IloOperationError, if some operation on iLO fails. """ - deploy_ramdisk_opts = agent.build_agent_options(task.node) - deploy_iso = task.node.driver_info['ilo_deploy_iso'] - _reboot_into(task, deploy_iso, deploy_ramdisk_opts) + _prepare_agent_vmedia_boot(task) return states.DEPLOYWAIT @@ -431,6 +446,55 @@ class IloVirtualMediaAgentDeploy(base.DeployInterface): """ pass + def get_clean_steps(self, task): + """Get the list of clean steps from the agent. + + :param task: a TaskManager object containing the node + :returns: A list of clean step dictionaries + """ + steps = deploy_utils.agent_get_clean_steps(task) + if CONF.ilo.clean_priority_erase_devices: + for step in steps: + if (step.get('step') == 'erase_devices' and + step.get('interface') == 'deploy'): + # Override with operator set priority + step['priority'] = CONF.ilo.clean_priority_erase_devices + + return steps + + def execute_clean_step(self, task, step): + """Execute a clean step asynchronously on the agent. + + :param task: a TaskManager object containing the node + :param step: a clean step dictionary to execute + :returns: states.CLEANING to signify the step will be completed async + """ + return deploy_utils.agent_execute_clean_step(task, step) + + def prepare_cleaning(self, task): + """Boot into the agent to prepare for cleaning.""" + # Create cleaning ports if necessary + provider = dhcp_factory.DHCPFactory().provider + + # If we have left over ports from a previous cleaning, remove them + if getattr(provider, 'delete_cleaning_ports', None): + provider.delete_cleaning_ports(task) + + if getattr(provider, 'create_cleaning_ports', None): + provider.create_cleaning_ports(task) + + _prepare_agent_vmedia_boot(task) + # Tell the conductor we are waiting for the agent to boot. + return states.CLEANING + + def tear_down_cleaning(self, task): + """Clean up the PXE and DHCP files after cleaning.""" + manager_utils.node_power_action(task, states.POWER_OFF) + # If we created cleaning ports, delete them + provider = dhcp_factory.DHCPFactory().provider + if getattr(provider, 'delete_cleaning_ports', None): + provider.delete_cleaning_ports(task) + class IloPXEDeploy(pxe.PXEDeploy): diff --git a/ironic/tests/drivers/ilo/test_deploy.py b/ironic/tests/drivers/ilo/test_deploy.py index de1f4b5671..d279bea65c 100644 --- a/ironic/tests/drivers/ilo/test_deploy.py +++ b/ironic/tests/drivers/ilo/test_deploy.py @@ -242,6 +242,21 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase): boot_devices.CDROM) node_power_action_mock.assert_called_once_with(task, states.REBOOT) + @mock.patch.object(ilo_deploy, '_reboot_into') + @mock.patch.object(agent, 'build_agent_options') + def test__prepare_agent_vmedia_boot(self, build_options_mock, + reboot_into_mock): + deploy_opts = {'a': 'b'} + build_options_mock.return_value = deploy_opts + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.node.driver_info['ilo_deploy_iso'] = 'deploy-iso-uuid' + ilo_deploy._prepare_agent_vmedia_boot(task) + build_options_mock.assert_called_once_with(task.node) + reboot_into_mock.assert_called_once_with(task, + 'deploy-iso-uuid', + deploy_opts) + class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase): @@ -406,21 +421,12 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase): task.driver.deploy.validate(task) parse_driver_info_mock.assert_called_once_with(task.node) - @mock.patch.object(ilo_deploy, '_reboot_into') - @mock.patch.object(agent, 'build_agent_options') - def test_deploy(self, build_options_mock, reboot_into_mock): + @mock.patch.object(ilo_deploy, '_prepare_agent_vmedia_boot') + def test_deploy(self, vmedia_boot_mock): with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: - deploy_opts = {'a': 'b'} - build_options_mock.return_value = deploy_opts - task.node.driver_info['ilo_deploy_iso'] = 'deploy-iso-uuid' - returned_state = task.driver.deploy.deploy(task) - - build_options_mock.assert_called_once_with(task.node) - reboot_into_mock.assert_called_once_with(task, - 'deploy-iso-uuid', - deploy_opts) + vmedia_boot_mock.assert_called_once_with(task) self.assertEqual(states.DEPLOYWAIT, returned_state) @mock.patch.object(manager_utils, 'node_power_action') @@ -445,6 +451,65 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase): update_boot_mode_mock.assert_called_once_with(task) self.assertEqual(deploy_opts, task.node.instance_info) + @mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.delete_cleaning_ports') + @mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.create_cleaning_ports') + @mock.patch.object(ilo_deploy, '_prepare_agent_vmedia_boot') + def test_prepare_cleaning(self, vmedia_boot_mock, create_port_mock, + delete_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + returned_state = task.driver.deploy.prepare_cleaning(task) + vmedia_boot_mock.assert_called_once_with(task) + self.assertEqual(states.CLEANING, returned_state) + create_port_mock.assert_called_once_with(task) + delete_mock.assert_called_once_with(task) + + @mock.patch('ironic.dhcp.neutron.NeutronDHCPApi.delete_cleaning_ports') + @mock.patch.object(manager_utils, 'node_power_action') + def test_tear_down_cleaning(self, power_mock, delete_mock): + with task_manager.acquire( + self.context, self.node['uuid'], shared=False) as task: + task.driver.deploy.tear_down_cleaning(task) + power_mock.assert_called_once_with(task, states.POWER_OFF) + delete_mock.assert_called_once_with(task) + + @mock.patch.object(deploy_utils, 'agent_execute_clean_step') + def test_execute_clean_step(self, execute_mock): + with task_manager.acquire( + self.context, self.node['uuid'], shared=False) as task: + task.driver.deploy.execute_clean_step(task, 'fake-step') + execute_mock.assert_called_once_with(task, 'fake-step') + + @mock.patch.object(deploy_utils, 'agent_get_clean_steps') + def test_get_clean_steps_with_conf_option(self, get_clean_step_mock): + self.config(clean_priority_erase_devices=20, group='ilo') + get_clean_step_mock.return_value = [{ + 'step': 'erase_devices', + 'priority': 10, + 'interface': 'deploy', + 'reboot_requested': False + }] + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + step = task.driver.deploy.get_clean_steps(task) + get_clean_step_mock.assert_called_once_with(task) + self.assertEqual(step[0].get('priority'), + CONF.ilo.clean_priority_erase_devices) + + @mock.patch.object(deploy_utils, 'agent_get_clean_steps') + def test_get_clean_steps_without_conf_option(self, get_clean_step_mock): + get_clean_step_mock.return_value = [{ + 'step': 'erase_devices', + 'priority': 10, + 'interface': 'deploy', + 'reboot_requested': False + }] + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + step = task.driver.deploy.get_clean_steps(task) + get_clean_step_mock.assert_called_once_with(task) + self.assertEqual(step[0].get('priority'), 10) + class VendorPassthruTestCase(db_base.DbTestCase):