Merge "Changes 'deploy' and 'boot' interface for 'pxe_ilo' driver"

This commit is contained in:
Jenkins 2017-04-15 16:47:44 +00:00 committed by Gerrit Code Review
commit 2a2548336d
5 changed files with 147 additions and 318 deletions

View File

@ -37,6 +37,7 @@ from ironic.conductor import utils as manager_utils
from ironic.drivers import base
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers.modules import pxe
LOG = logging.getLogger(__name__)
@ -564,3 +565,71 @@ class IloVirtualMediaBoot(base.BootInterface):
i_info['ilo_boot_iso'] = boot_iso
node.instance_info = i_info
node.save()
class IloPXEBoot(pxe.PXEBoot):
@METRICS.timer('IloPXEBoot.prepare_ramdisk')
def prepare_ramdisk(self, task, ramdisk_params):
"""Prepares the boot of Ironic ramdisk using PXE.
This method prepares the boot of the deploy ramdisk after
reading relevant information from the node's driver_info and
instance_info.
:param task: a task from TaskManager.
:param ramdisk_params: the parameters to be passed to the ramdisk.
:returns: None
:raises: MissingParameterValue, if some information is missing in
node's driver_info or instance_info.
:raises: InvalidParameterValue, if some information provided is
invalid.
:raises: IronicException, if some power or set boot boot device
operation failed on the node.
:raises: IloOperationError, if some operation on iLO failed.
"""
if task.node.provision_state == states.DEPLOYING:
prepare_node_for_deploy(task)
super(IloPXEBoot, self).prepare_ramdisk(task, ramdisk_params)
@METRICS.timer('IloPXEBoot.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: IloOperationError, if some operation on iLO failed.
"""
# Set boot mode
ilo_common.update_boot_mode(task)
# Need to enable secure boot, if being requested
ilo_common.update_secure_boot_mode(task, True)
super(IloPXEBoot, self).prepare_instance(task)
@METRICS.timer('IloPXEBoot.clean_up_instance')
def clean_up_instance(self, task):
"""Cleans up the boot of instance.
This method cleans up the PXE environment that was setup for booting
the instance. It unlinks the instance kernel/ramdisk in the node's
directory in tftproot and removes it's PXE config.
:param task: a task from TaskManager.
:returns: None
:raises: IloOperationError, if some operation on iLO failed.
"""
LOG.debug("Cleaning up the instance.")
manager_utils.node_power_action(task, states.POWER_OFF)
disable_secure_boot_if_supported(task)
super(IloPXEBoot, self).clean_up_instance(task)

View File

@ -1,114 +0,0 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
iLO Deploy Driver(s) and supporting methods.
"""
from ironic_lib import metrics_utils
from oslo_log import log as logging
from ironic.common import boot_devices
from ironic.common import states
from ironic.conductor import task_manager
from ironic.conductor import utils as manager_utils
from ironic.drivers.modules.ilo import boot as ilo_boot
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers.modules import iscsi_deploy
LOG = logging.getLogger(__name__)
METRICS = metrics_utils.get_metrics_logger(__name__)
class IloIscsiDeployMixin(object):
@METRICS.timer('IloIscsiDeployMixin.tear_down')
@task_manager.require_exclusive_lock
def tear_down(self, task):
"""Tear down a previous deployment on the task's node.
Power off the node. All actual clean-up is done in the clean_up()
method which should be called separately.
:param task: a TaskManager instance containing the node to act on.
:returns: deploy state DELETED.
:raises: IloOperationError, if some operation on iLO failed.
"""
manager_utils.node_power_action(task, states.POWER_OFF)
ilo_boot.disable_secure_boot_if_supported(task)
return super(IloIscsiDeployMixin, self).tear_down(task)
@METRICS.timer('IloIscsiDeployMixin.prepare_cleaning')
def prepare_cleaning(self, task):
"""Boot into the agent to prepare for cleaning.
:param task: a TaskManager object containing the node
:returns: states.CLEANWAIT to signify an asynchronous prepare.
:raises NodeCleaningFailure: if the previous cleaning ports cannot
be removed or if new cleaning ports cannot be created
:raises: IloOperationError, if some operation on iLO failed.
"""
# Powering off the Node before initiating boot for node cleaning.
# If node is in system POST, setting boot device fails.
manager_utils.node_power_action(task, states.POWER_OFF)
return super(IloIscsiDeployMixin, self).prepare_cleaning(task)
@METRICS.timer('IloIscsiDeployMixin.continue_deploy')
@task_manager.require_exclusive_lock
def continue_deploy(self, task, **kwargs):
"""Method invoked when deployed with the IPA ramdisk.
This method is invoked during a heartbeat from an agent when
the node is in wait-call-back state.
This updates boot mode and secure boot settings, if required.
"""
ilo_common.update_boot_mode(task)
ilo_common.update_secure_boot_mode(task, True)
super(IloIscsiDeployMixin, self).continue_deploy(task, **kwargs)
class IloPXEDeploy(IloIscsiDeployMixin, iscsi_deploy.ISCSIDeploy):
@METRICS.timer('IloPXEDeploy.prepare')
def prepare(self, task):
"""Prepare the deployment environment for this task's node.
If the node's 'capabilities' property includes a boot_mode, that
boot mode will be applied for the node. Otherwise, the existing
boot mode of the node is used in the node's 'capabilities' property.
PXEDeploys' prepare method is then called, to prepare the deploy
environment for the node
:param task: a TaskManager instance containing the node to act on.
:raises: IloOperationError, if some operation on iLO failed.
:raises: InvalidParameterValue, if some information is invalid.
"""
if task.node.provision_state == states.DEPLOYING:
ilo_boot.prepare_node_for_deploy(task)
super(IloPXEDeploy, self).prepare(task)
@METRICS.timer('IloPXEDeploy.deploy')
def deploy(self, task):
"""Start deployment of the task's node.
This method sets the boot device to 'NETWORK' and then calls
PXEDeploy's deploy method to deploy on the given node.
:param task: a TaskManager instance containing the node to act on.
:returns: deploy state DEPLOYWAIT.
"""
manager_utils.node_set_boot_device(task, boot_devices.PXE)
return super(IloPXEDeploy, self).deploy(task)

View File

@ -26,8 +26,8 @@ from ironic.drivers import ipmi
from ironic.drivers.modules import agent
from ironic.drivers.modules.cimc import management as cimc_mgmt
from ironic.drivers.modules.cimc import power as cimc_power
from ironic.drivers.modules.ilo import boot as ilo_boot
from ironic.drivers.modules.ilo import console as ilo_console
from ironic.drivers.modules.ilo import deploy as ilo_deploy
from ironic.drivers.modules.ilo import inspect as ilo_inspect
from ironic.drivers.modules.ilo import management as ilo_management
from ironic.drivers.modules.ilo import power as ilo_power
@ -91,8 +91,8 @@ class PXEAndIloDriver(base.BaseDriver):
driver=self.__class__.__name__,
reason=_("Unable to import proliantutils library"))
self.power = ilo_power.IloPower()
self.boot = pxe.PXEBoot()
self.deploy = ilo_deploy.IloPXEDeploy()
self.boot = ilo_boot.IloPXEBoot()
self.deploy = iscsi_deploy.ISCSIDeploy()
self.vendor = ilo_vendor.VendorPassthru()
self.console = ilo_console.IloConsoleInterface()
self.management = ilo_management.IloManagement()

View File

@ -34,6 +34,7 @@ from ironic.conductor import utils as manager_utils
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.ilo import boot as ilo_boot
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers.modules import pxe
from ironic.drivers import utils as driver_utils
from ironic.tests.unit.conductor import mgr_utils
from ironic.tests.unit.db import base as db_base
@ -930,3 +931,77 @@ class IloVirtualMediaBootTestCase(db_base.DbTestCase):
mock.ANY, task, "12312642-09d3-467f-8e09-12385826a123")
update_boot_mode_mock.assert_called_once_with(task)
update_secure_boot_mode_mock.assert_called_once_with(task, True)
class IloPXEBootTestCase(db_base.DbTestCase):
def setUp(self):
super(IloPXEBootTestCase, self).setUp()
mgr_utils.mock_the_extension_manager(driver="pxe_ilo")
self.node = obj_utils.create_test_node(
self.context, driver='pxe_ilo', driver_info=INFO_DICT)
@mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True,
autospec=True)
@mock.patch.object(pxe.PXEBoot, 'prepare_ramdisk', spec_set=True,
autospec=True)
def test_prepare_ramdisk_not_deploying_not_cleaning(
self, pxe_boot_mock, prepare_node_mock):
self.node.provision_state = states.CLEANING
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.assertIsNone(
task.driver.boot.prepare_ramdisk(task, None))
self.assertFalse(prepare_node_mock.called)
pxe_boot_mock.assert_called_once_with(mock.ANY, task, None)
@mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True,
autospec=True)
@mock.patch.object(pxe.PXEBoot, 'prepare_ramdisk', spec_set=True,
autospec=True)
def test_prepare_ramdisk_in_deploying(self, pxe_boot_mock,
prepare_node_mock):
self.node.provision_state = states.DEPLOYING
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.assertIsNone(
task.driver.boot.prepare_ramdisk(task, None))
prepare_node_mock.assert_called_once_with(task)
pxe_boot_mock.assert_called_once_with(mock.ANY, task, None)
@mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True,
autospec=True)
@mock.patch.object(manager_utils, 'node_power_action', spec_set=True,
autospec=True)
@mock.patch.object(pxe.PXEBoot, 'clean_up_instance', spec_set=True,
autospec=True)
def test_clean_up_instance(self, pxe_cleanup_mock, node_power_mock,
update_secure_boot_mode_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.boot.clean_up_instance(task)
node_power_mock.assert_called_once_with(task, states.POWER_OFF)
update_secure_boot_mode_mock.assert_called_once_with(task, False)
pxe_cleanup_mock.assert_called_once_with(mock.ANY, task)
@mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True,
autospec=True)
@mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True,
autospec=True)
@mock.patch.object(pxe.PXEBoot, 'prepare_instance', spec_set=True,
autospec=True)
def test_prepare_instance(self, pxe_prepare_instance_mock,
update_boot_mode_mock,
update_secure_boot_mode_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.boot.prepare_instance(task)
update_boot_mode_mock.assert_called_once_with(task)
update_secure_boot_mode_mock.assert_called_once_with(task, True)
pxe_prepare_instance_mock.assert_called_once_with(mock.ANY, task)

View File

@ -1,201 +0,0 @@
# Copyright 2014 Hewlett-Packard Development Company, L.P.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""Test class for deploy methods used by iLO modules."""
import mock
import six
from ironic.common import boot_devices
from ironic.common import states
from ironic.conductor import task_manager
from ironic.conductor import utils as manager_utils
from ironic.conf import CONF
from ironic.drivers.modules.ilo import boot as ilo_boot
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers.modules import iscsi_deploy
from ironic.tests.unit.conductor import mgr_utils
from ironic.tests.unit.db import base as db_base
from ironic.tests.unit.db import utils as db_utils
from ironic.tests.unit.objects import utils as obj_utils
if six.PY3:
import io
file = io.BytesIO
INFO_DICT = db_utils.get_test_ilo_info()
class IloPXEDeployTestCase(db_base.DbTestCase):
def setUp(self):
super(IloPXEDeployTestCase, self).setUp()
mgr_utils.mock_the_extension_manager(driver="pxe_ilo")
self.node = obj_utils.create_test_node(
self.context, driver='pxe_ilo', driver_info=INFO_DICT)
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'validate', spec_set=True,
autospec=True)
def test_validate(self, pxe_validate_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.deploy.validate(task)
pxe_validate_mock.assert_called_once_with(mock.ANY, task)
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare', spec_set=True,
autospec=True)
@mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True,
autospec=True)
def test_prepare(self,
prepare_node_for_deploy_mock,
pxe_prepare_mock):
self.node.provision_state = states.DEPLOYING
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties['capabilities'] = 'boot_mode:uefi'
task.driver.deploy.prepare(task)
prepare_node_for_deploy_mock.assert_called_once_with(task)
pxe_prepare_mock.assert_called_once_with(mock.ANY, task)
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare', spec_set=True,
autospec=True)
@mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True,
autospec=True)
def test_prepare_active_node(self,
prepare_node_for_deploy_mock,
pxe_prepare_mock):
"""Ensure nodes in running states are not inadvertently changed"""
test_states = list(states.STABLE_STATES)
test_states.extend([states.CLEANING,
states.CLEANWAIT,
states.INSPECTING])
for state in test_states:
self.node.provision_state = state
self.node.save()
prepare_node_for_deploy_mock.reset_mock()
pxe_prepare_mock.reset_mock()
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties['capabilities'] = 'boot_mode:uefi'
task.driver.deploy.prepare(task)
self.assertFalse(prepare_node_for_deploy_mock.called)
pxe_prepare_mock.assert_called_once_with(mock.ANY, task)
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare', spec_set=True,
autospec=True)
@mock.patch.object(ilo_boot, 'prepare_node_for_deploy', spec_set=True,
autospec=True)
def test_prepare_whole_disk_image_uefi(self, prepare_node_for_deploy_mock,
pxe_prepare_mock):
CONF.set_override('default_boot_option', 'netboot', 'deploy')
self.node.provision_state = states.DEPLOYING
self.node.save()
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.properties['capabilities'] = 'boot_mode:uefi'
task.node.driver_internal_info['is_whole_disk_image'] = True
task.driver.deploy.prepare(task)
prepare_node_for_deploy_mock.assert_called_once_with(task)
pxe_prepare_mock.assert_called_once_with(mock.ANY, task)
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'deploy', spec_set=True,
autospec=True)
@mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True,
autospec=True)
def test_deploy_boot_mode_exists(self, set_persistent_mock,
pxe_deploy_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.deploy.deploy(task)
set_persistent_mock.assert_called_with(task, boot_devices.PXE)
pxe_deploy_mock.assert_called_once_with(mock.ANY, task)
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'tear_down',
spec_set=True, autospec=True)
@mock.patch.object(ilo_common, 'update_secure_boot_mode', autospec=True)
@mock.patch.object(manager_utils, 'node_power_action', spec_set=True,
autospec=True)
def test_tear_down(self, node_power_action_mock,
update_secure_boot_mode_mock, pxe_tear_down_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
pxe_tear_down_mock.return_value = states.DELETED
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)
pxe_tear_down_mock.assert_called_once_with(mock.ANY, task)
self.assertEqual(states.DELETED, returned_state)
@mock.patch.object(ilo_boot.LOG, 'warning',
spec_set=True, autospec=True)
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'tear_down',
spec_set=True, autospec=True)
@mock.patch.object(ilo_boot, 'exception', spec_set=True, autospec=True)
@mock.patch.object(ilo_common, 'update_secure_boot_mode',
spec_set=True, autospec=True)
@mock.patch.object(manager_utils, 'node_power_action', spec_set=True,
autospec=True)
def test_tear_down_handle_exception(self, node_power_action_mock,
update_secure_boot_mode_mock,
exception_mock, pxe_tear_down_mock,
mock_log):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
pxe_tear_down_mock.return_value = states.DELETED
exception_mock.IloOperationNotSupported = Exception
update_secure_boot_mode_mock.side_effect = Exception
returned_state = task.driver.deploy.tear_down(task)
update_secure_boot_mode_mock.assert_called_once_with(task, False)
pxe_tear_down_mock.assert_called_once_with(mock.ANY, task)
node_power_action_mock.assert_called_once_with(task,
states.POWER_OFF)
self.assertTrue(mock_log.called)
self.assertEqual(states.DELETED, returned_state)
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare_cleaning',
spec_set=True, autospec=True)
@mock.patch.object(manager_utils, 'node_power_action', spec_set=True,
autospec=True)
def test_prepare_cleaning(self, node_power_action_mock,
iscsi_prep_clean_mock):
iscsi_prep_clean_mock.return_value = states.CLEANWAIT
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
ret = task.driver.deploy.prepare_cleaning(task)
self.assertEqual(states.CLEANWAIT, ret)
node_power_action_mock.assert_called_once_with(task,
states.POWER_OFF)
iscsi_prep_clean_mock.assert_called_once_with(mock.ANY, task)
@mock.patch.object(iscsi_deploy.ISCSIDeploy, 'continue_deploy',
spec_set=True, autospec=True)
@mock.patch.object(ilo_common, 'update_secure_boot_mode', autospec=True)
@mock.patch.object(ilo_common, 'update_boot_mode', autospec=True)
def test_continue_deploy(self,
func_update_boot_mode,
func_update_secure_boot_mode,
pxe_vendorpassthru_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.provision_state = states.DEPLOYWAIT
task.node.target_provision_state = states.ACTIVE
task.driver.deploy.continue_deploy(task)
func_update_boot_mode.assert_called_once_with(task)
func_update_secure_boot_mode.assert_called_once_with(task, True)
pxe_vendorpassthru_mock.assert_called_once_with(
mock.ANY, task)