From 39e40ef12b016a1aeb37a3fe755b9978d3f9934f Mon Sep 17 00:00:00 2001 From: Shivanand Tendulker Date: Mon, 24 Aug 2015 21:48:19 -0700 Subject: [PATCH] Refactor iscsi_ilo driver to use new boot interface This commit implements new boot interface IloVirtualMediaBoot and refactors IloVirtualMediaIscsiDeploy to use the same. Implements: blueprint ilo-driver-uses-boot-interface Change-Id: I5481e705892f66cb3184ac16ff56c123f5c69087 --- ironic/drivers/ilo.py | 7 +- ironic/drivers/modules/ilo/boot.py | 413 +++++ ironic/drivers/modules/ilo/common.py | 55 + ironic/drivers/modules/ilo/deploy.py | 716 +-------- ironic/drivers/modules/ilo/vendor.py | 154 ++ ironic/drivers/pxe.py | 3 +- .../unit/drivers/modules/ilo/test_boot.py | 540 +++++++ .../unit/drivers/modules/ilo/test_common.py | 46 + .../unit/drivers/modules/ilo/test_deploy.py | 1362 +---------------- .../unit/drivers/modules/ilo/test_vendor.py | 228 +++ 10 files changed, 1492 insertions(+), 2032 deletions(-) create mode 100644 ironic/drivers/modules/ilo/boot.py create mode 100644 ironic/drivers/modules/ilo/vendor.py create mode 100644 ironic/tests/unit/drivers/modules/ilo/test_boot.py create mode 100644 ironic/tests/unit/drivers/modules/ilo/test_vendor.py diff --git a/ironic/drivers/ilo.py b/ironic/drivers/ilo.py index 5e91fdacdc..ae22dd7add 100644 --- a/ironic/drivers/ilo.py +++ b/ironic/drivers/ilo.py @@ -21,10 +21,12 @@ 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 boot from ironic.drivers.modules.ilo import deploy from ironic.drivers.modules.ilo import inspect from ironic.drivers.modules.ilo import management from ironic.drivers.modules.ilo import power +from ironic.drivers.modules.ilo import vendor class IloVirtualMediaIscsiDriver(base.BaseDriver): @@ -44,10 +46,11 @@ class IloVirtualMediaIscsiDriver(base.BaseDriver): reason=_("Unable to import proliantutils library")) self.power = power.IloPower() + self.boot = boot.IloVirtualMediaBoot() self.deploy = deploy.IloVirtualMediaIscsiDeploy() self.console = deploy.IloConsoleInterface() self.management = management.IloManagement() - self.vendor = deploy.VendorPassthru() + self.vendor = vendor.VendorPassthru() self.inspect = inspect.IloInspect() @@ -71,6 +74,6 @@ class IloVirtualMediaAgentDriver(base.BaseDriver): self.deploy = deploy.IloVirtualMediaAgentDeploy() self.console = deploy.IloConsoleInterface() self.management = management.IloManagement() - self.vendor = deploy.IloVirtualMediaAgentVendorInterface() + self.vendor = vendor.IloVirtualMediaAgentVendorInterface() self.inspect = inspect.IloInspect() self.raid = agent.AgentRAID() diff --git a/ironic/drivers/modules/ilo/boot.py b/ironic/drivers/modules/ilo/boot.py new file mode 100644 index 0000000000..90e29b7287 --- /dev/null +++ b/ironic/drivers/modules/ilo/boot.py @@ -0,0 +1,413 @@ +# Copyright 2015 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. +""" +Boot Interface for iLO drivers and its supporting methods. +""" + +import os +import tempfile + +from oslo_config import cfg +from oslo_log import log as logging +from oslo_utils import excutils +import six.moves.urllib.parse as urlparse + +from ironic.common import boot_devices +from ironic.common import exception +from ironic.common.glance_service import service_utils +from ironic.common.i18n import _ +from ironic.common.i18n import _LE +from ironic.common.i18n import _LW +from ironic.common import image_service +from ironic.common import images +from ironic.common import swift +from ironic.common import utils +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 + +LOG = logging.getLogger(__name__) + +CONF = cfg.CONF + +REQUIRED_PROPERTIES = { + 'ilo_deploy_iso': _("UUID (from Glance) of the deployment ISO. " + "Required.") +} +COMMON_PROPERTIES = REQUIRED_PROPERTIES + + +def parse_driver_info(node): + """Gets the driver specific Node deployment info. + + This method validates whether the 'driver_info' property of the + supplied node contains the required information for this driver to + deploy images to the node. + + :param node: a single Node. + :returns: A dict with the driver_info values. + :raises: MissingParameterValue, if any of the required parameters are + missing. + """ + info = node.driver_info + d_info = {} + d_info['ilo_deploy_iso'] = info.get('ilo_deploy_iso') + + error_msg = _("Error validating iLO virtual media deploy. Some parameters" + " were missing in node's driver_info") + deploy_utils.check_for_missing_params(d_info, error_msg) + + return d_info + + +def _get_boot_iso_object_name(node): + """Returns the boot iso object name for a given node. + + :param node: the node for which object name is to be provided. + """ + return "boot-%s" % node.uuid + + +def _get_boot_iso(task, root_uuid): + """This method returns a boot ISO to boot the node. + + It chooses one of the three options in the order as below: + 1. Does nothing if 'ilo_boot_iso' is present in node's instance_info and + 'boot_iso_created_in_web_server' is not set in 'driver_internal_info'. + 2. Image deployed has a meta-property 'boot_iso' in Glance. This should + refer to the UUID of the boot_iso which exists in Glance. + 3. Generates a boot ISO on the fly using kernel and ramdisk mentioned in + the image deployed. It uploads the generated boot ISO to Swift. + + :param task: a TaskManager instance containing the node to act on. + :param root_uuid: the uuid of the root partition. + :returns: boot ISO URL. Should be either of below: + * A Swift object - It should be of format 'swift:'. It is + assumed that the image object is present in + CONF.ilo.swift_ilo_container; + * A Glance image - It should be format 'glance://' + or just ; + * An HTTP URL. + On error finding the boot iso, it returns None. + :raises: MissingParameterValue, if any of the required parameters are + missing in the node's driver_info or instance_info. + :raises: InvalidParameterValue, if any of the parameters have invalid + value in the node's driver_info or instance_info. + :raises: SwiftOperationError, if operation with Swift fails. + :raises: ImageCreationFailed, if creation of boot ISO failed. + :raises: exception.ImageRefValidationFailed if ilo_boot_iso is not + HTTP(S) URL. + """ + LOG.debug("Trying to get a boot ISO to boot the baremetal node") + + # Option 1 - Check if user has provided ilo_boot_iso in node's + # instance_info + driver_internal_info = task.node.driver_internal_info + boot_iso_created_in_web_server = ( + driver_internal_info.get('boot_iso_created_in_web_server')) + + if (task.node.instance_info.get('ilo_boot_iso') + and not boot_iso_created_in_web_server): + LOG.debug("Using ilo_boot_iso provided in node's instance_info") + boot_iso = task.node.instance_info['ilo_boot_iso'] + if not service_utils.is_glance_image(boot_iso): + try: + image_service.HttpImageService().validate_href(boot_iso) + except exception.ImageRefValidationFailed: + with excutils.save_and_reraise_exception(): + LOG.error(_LE("Virtual media deploy accepts only Glance " + "images or HTTP(S) URLs as " + "instance_info['ilo_boot_iso']. Either %s " + "is not a valid HTTP(S) URL or is " + "not reachable."), boot_iso) + + return task.node.instance_info['ilo_boot_iso'] + + # Option 2 - Check if user has provided a boot_iso in Glance. If boot_iso + # is a supported non-glance href execution will proceed to option 3. + deploy_info = _parse_deploy_info(task.node) + + image_href = deploy_info['image_source'] + image_properties = ( + images.get_image_properties( + task.context, image_href, ['boot_iso', 'kernel_id', 'ramdisk_id'])) + + boot_iso_uuid = image_properties.get('boot_iso') + kernel_href = (task.node.instance_info.get('kernel') or + image_properties.get('kernel_id')) + ramdisk_href = (task.node.instance_info.get('ramdisk') or + image_properties.get('ramdisk_id')) + + if boot_iso_uuid: + LOG.debug("Found boot_iso %s in Glance", boot_iso_uuid) + return boot_iso_uuid + + if not kernel_href or not ramdisk_href: + LOG.error(_LE("Unable to find kernel or ramdisk for " + "image %(image)s to generate boot ISO for %(node)s"), + {'image': image_href, 'node': task.node.uuid}) + return + + # NOTE(rameshg87): Functionality to share the boot ISOs created for + # similar instances (instances with same deployed image) is + # not implemented as of now. Creation/Deletion of such a shared boot ISO + # will require synchronisation across conductor nodes for the shared boot + # ISO. Such a synchronisation mechanism doesn't exist in ironic as of now. + + # Option 3 - Create boot_iso from kernel/ramdisk, upload to Swift + # or web server and provide its name. + deploy_iso_uuid = deploy_info['ilo_deploy_iso'] + boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node) + boot_iso_object_name = _get_boot_iso_object_name(task.node) + kernel_params = CONF.pxe.pxe_append_params + with tempfile.NamedTemporaryFile(dir=CONF.tempdir) as fileobj: + boot_iso_tmp_file = fileobj.name + images.create_boot_iso(task.context, boot_iso_tmp_file, + kernel_href, ramdisk_href, + deploy_iso_uuid, root_uuid, + kernel_params, boot_mode) + + if CONF.ilo.use_web_server_for_images: + boot_iso_url = ( + ilo_common.copy_image_to_web_server(boot_iso_tmp_file, + boot_iso_object_name)) + driver_internal_info = task.node.driver_internal_info + driver_internal_info['boot_iso_created_in_web_server'] = True + task.node.driver_internal_info = driver_internal_info + task.node.save() + LOG.debug("Created boot_iso %(boot_iso)s for node %(node)s", + {'boot_iso': boot_iso_url, 'node': task.node.uuid}) + return boot_iso_url + else: + container = CONF.ilo.swift_ilo_container + swift_api = swift.SwiftAPI() + swift_api.create_object(container, boot_iso_object_name, + boot_iso_tmp_file) + + LOG.debug("Created boot_iso %s in Swift", boot_iso_object_name) + return 'swift:%s' % boot_iso_object_name + + +def _clean_up_boot_iso_for_instance(node): + """Deletes the boot ISO if it was created for the instance. + + :param node: an ironic node object. + """ + ilo_boot_iso = node.instance_info.get('ilo_boot_iso') + if not ilo_boot_iso: + return + if ilo_boot_iso.startswith('swift'): + swift_api = swift.SwiftAPI() + container = CONF.ilo.swift_ilo_container + boot_iso_object_name = _get_boot_iso_object_name(node) + try: + swift_api.delete_object(container, boot_iso_object_name) + except exception.SwiftOperationError as e: + LOG.exception(_LE("Failed to clean up boot ISO for node " + "%(node)s. Error: %(error)s."), + {'node': node.uuid, 'error': e}) + elif CONF.ilo.use_web_server_for_images: + result = urlparse.urlparse(ilo_boot_iso) + ilo_boot_iso_name = os.path.basename(result.path) + boot_iso_path = os.path.join( + CONF.deploy.http_root, ilo_boot_iso_name) + utils.unlink_without_raise(boot_iso_path) + + +def _parse_deploy_info(node): + """Gets the instance and driver specific Node deployment info. + + This method validates whether the 'instance_info' and 'driver_info' + property of the supplied node contains the required information for + this driver to deploy images to the node. + + :param node: a single Node. + :returns: A dict with the instance_info and driver_info values. + :raises: MissingParameterValue, if any of the required parameters are + missing. + :raises: InvalidParameterValue, if any of the parameters have invalid + value. + """ + info = {} + info.update(deploy_utils.get_image_instance_info(node)) + info.update(parse_driver_info(node)) + return info + + +class IloVirtualMediaBoot(base.BootInterface): + + def get_properties(self): + return COMMON_PROPERTIES + + def validate(self, task): + """Validate the deployment information for the task's node. + + :param task: a TaskManager instance containing the node to act on. + :raises: InvalidParameterValue, if some information is invalid. + :raises: MissingParameterValue if 'kernel_id' and 'ramdisk_id' are + missing in the Glance image or 'kernel' and 'ramdisk' not provided + in instance_info for non-Glance image. + """ + + node = task.node + + d_info = _parse_deploy_info(node) + + if node.driver_internal_info.get('is_whole_disk_image'): + props = [] + elif service_utils.is_glance_image(d_info['image_source']): + props = ['kernel_id', 'ramdisk_id'] + else: + props = ['kernel', 'ramdisk'] + deploy_utils.validate_image_properties(task.context, d_info, props) + + def prepare_ramdisk(self, task, ramdisk_params): + """Prepares the boot of deploy ramdisk using virtual media. + + 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. + """ + + node = task.node + + # Clear ilo_boot_iso if it's a glance image to force recreate + # another one again (or use existing one in glance). + # This is mainly for rebuild scenario. + if service_utils.is_glance_image( + node.instance_info.get('image_source')): + instance_info = node.instance_info + instance_info.pop('ilo_boot_iso', None) + node.instance_info = instance_info + node.save() + + # Eject all virtual media devices, as we are going to use them + # during deploy. + ilo_common.eject_vmedia_devices(task) + + deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task) + ramdisk_params['BOOTIF'] = deploy_nic_mac + deploy_iso = node.driver_info['ilo_deploy_iso'] + + ilo_common.setup_vmedia(task, deploy_iso, ramdisk_params) + + 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. + It does the following depending on boot_option for deploy: + + - If the boot_option requested for this deploy is 'local' or image + is a whole disk image, then it sets the node to boot from disk. + - Otherwise it finds/creates the boot ISO to boot the instance + image, attaches the boot ISO to the bare metal and then sets + the node to boot from CDROM. + + :param task: a task from TaskManager. + :returns: None + :raises: IloOperationError, if some operation on iLO failed. + """ + + ilo_common.cleanup_vmedia_boot(task) + + # For iscsi_ilo driver, we boot from disk every time if the image + # deployed is a whole disk image. + node = task.node + iwdi = node.driver_internal_info.get('is_whole_disk_image') + if deploy_utils.get_boot_option(node) == "local" or iwdi: + manager_utils.node_set_boot_device(task, boot_devices.DISK, + persistent=True) + else: + drv_int_info = node.driver_internal_info + root_uuid_or_disk_id = drv_int_info.get('root_uuid_or_disk_id') + if root_uuid_or_disk_id: + self._configure_vmedia_boot(task, root_uuid_or_disk_id) + else: + LOG.warning(_LW("The UUID for the root partition could not " + "be found for node %s"), node.uuid) + + 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 ejects virtual media + + :param task: a task from TaskManager. + :returns: None + :raises: IloOperationError, if some operation on iLO failed. + """ + + _clean_up_boot_iso_for_instance(task.node) + + driver_internal_info = task.node.driver_internal_info + driver_internal_info.pop('boot_iso_created_in_web_server', None) + driver_internal_info.pop('root_uuid_or_disk_id', None) + task.node.driver_internal_info = driver_internal_info + task.node.save() + + ilo_common.cleanup_vmedia_boot(task) + + def clean_up_ramdisk(self, task): + """Cleans up the boot of ironic ramdisk. + + This method cleans up virtual media devices setup for the deploy + ramdisk. + + :param task: a task from TaskManager. + :returns: None + :raises: IloOperationError, if some operation on iLO failed. + """ + + ilo_common.cleanup_vmedia_boot(task) + + def _configure_vmedia_boot(self, task, root_uuid): + """Configure vmedia boot for the node. + + :param task: a task from TaskManager. + :param root_uuid: uuid of the root partition + :returns: None + :raises: IloOperationError, if some operation on iLO failed. + """ + + node = task.node + boot_iso = _get_boot_iso(task, root_uuid) + if not boot_iso: + LOG.error(_LE("Cannot get boot ISO for node %s"), node.uuid) + return + + # Upon deploy complete, some distros cloud images reboot the system as + # part of its configuration. Hence boot device should be persistent and + # not one-time. + ilo_common.setup_vmedia_for_boot(task, boot_iso) + manager_utils.node_set_boot_device(task, + boot_devices.CDROM, + persistent=True) + + i_info = node.instance_info + i_info['ilo_boot_iso'] = boot_iso + node.instance_info = i_info + node.save() diff --git a/ironic/drivers/modules/ilo/common.py b/ironic/drivers/modules/ilo/common.py index b814315b81..ac816e3066 100644 --- a/ironic/drivers/modules/ilo/common.py +++ b/ironic/drivers/modules/ilo/common.py @@ -26,6 +26,7 @@ from oslo_utils import importutils import six.moves.urllib.parse as urlparse from six.moves.urllib.parse import urljoin +from ironic.common import boot_devices from ironic.common import exception from ironic.common.glance_service import service_utils from ironic.common.i18n import _ @@ -35,6 +36,7 @@ from ironic.common.i18n import _LW from ironic.common import images from ironic.common import swift from ironic.common import utils +from ironic.conductor import utils as manager_utils from ironic.drivers.modules import deploy_utils ilo_client = importutils.try_import('proliantutils.ilo.client') @@ -432,6 +434,37 @@ def update_boot_mode(task): node.save() +def setup_vmedia(task, iso, ramdisk_options): + """Attaches virtual media and sets it as boot device. + + This method attaches the given bootable ISO as virtual media, prepares the + arguments for ramdisk in virtual media floppy. + + :param task: a TaskManager instance containing the node to act on. + :param iso: a bootable ISO image href to attach to. Should be either + of below: + * A Swift object - It should be of format 'swift:'. + It is assumed that the image object is present in + CONF.ilo.swift_ilo_container; + * A Glance image - It should be format 'glance://' + or just ; + * An HTTP URL. + :param ramdisk_options: the options to be passed to the ramdisk in virtual + media floppy. + :raises: ImageCreationFailed, if it failed while creating the floppy image. + :raises: IloOperationError, if some operation on iLO failed. + """ + setup_vmedia_for_boot(task, iso, ramdisk_options) + + # In UEFI boot mode, upon inserting virtual CDROM, one has to reset the + # system to see it as a valid boot device in persistent boot devices. + # But virtual CDROM device is always available for one-time boot. + # During enable/disable of secure boot settings, iLO internally resets + # the server twice. But it retains one time boot settings across internal + # resets. Hence no impact of this change for secure boot deploy. + manager_utils.node_set_boot_device(task, boot_devices.CDROM) + + def setup_vmedia_for_boot(task, boot_iso, parameters=None): """Sets up the node to boot from the given ISO image. @@ -590,3 +623,25 @@ def set_secure_boot_mode(task, flag): except ilo_error.IloError as ilo_exception: raise exception.IloOperationError(operation=operation, error=ilo_exception) + + +def update_secure_boot_mode(task, mode): + """Changes secure boot mode for next boot on the node. + + This method changes secure boot mode on the node for next boot. It changes + the secure boot mode setting on node only if the deploy has requested for + the secure boot. + During deploy, this method is used to enable secure boot on the node by + passing 'mode' as 'True'. + During teardown, this method is used to disable secure boot on the node by + passing 'mode' as 'False'. + + :param task: a TaskManager instance containing the node to act on. + :param mode: Boolean value requesting the next state for secure boot + :raises: IloOperationNotSupported, if operation is not supported on iLO + :raises: IloOperationError, if some operation on iLO failed. + """ + if deploy_utils.is_secure_boot_requested(task.node): + set_secure_boot_mode(task, mode) + LOG.info(_LI('Changed secure boot to %(mode)s for node %(node)s'), + {'mode': mode, 'node': task.node.uuid}) diff --git a/ironic/drivers/modules/ilo/deploy.py b/ironic/drivers/modules/ilo/deploy.py index 9e864675af..562da3551b 100644 --- a/ironic/drivers/modules/ilo/deploy.py +++ b/ironic/drivers/modules/ilo/deploy.py @@ -15,33 +15,21 @@ iLO Deploy Driver(s) and supporting methods. """ -import os -import tempfile - from oslo_config import cfg from oslo_log import log as logging -from oslo_utils import excutils -import six.moves.urllib.parse as urlparse 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 _ -from ironic.common.i18n import _LE -from ironic.common.i18n import _LI from ironic.common.i18n import _LW -from ironic.common import image_service -from ironic.common import images from ironic.common import states -from ironic.common import swift -from ironic.common import utils from ironic.conductor import task_manager from ironic.conductor import utils as manager_utils from ironic.drivers import base from ironic.drivers.modules import agent -from ironic.drivers.modules import agent_base_vendor 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 ipmitool from ironic.drivers.modules import iscsi_deploy @@ -58,12 +46,6 @@ clean_opts = [ 'disabled and will not run during cleaning.')) ] -REQUIRED_PROPERTIES = { - 'ilo_deploy_iso': _("UUID (from Glance) of the deployment ISO. " - "Required.") -} -COMMON_PROPERTIES = REQUIRED_PROPERTIES - CONF.import_opt('pxe_append_params', 'ironic.drivers.modules.iscsi_deploy', group='pxe') CONF.import_opt('swift_ilo_container', 'ironic.drivers.modules.ilo.common', @@ -71,272 +53,6 @@ CONF.import_opt('swift_ilo_container', 'ironic.drivers.modules.ilo.common', CONF.register_opts(clean_opts, group='ilo') -def _recreate_and_populate_ilo_boot_iso(task): - """Recreate the boot iso for the node. - - Recreates the boot iso for the image and host it - on a valid image service and populates the new boot iso - in the instance_info of the node. - - :param task: a TaskManager instance containing the node to act on. - """ - instance_info = task.node.instance_info - root_uuid = task.node.driver_internal_info.get('root_uuid_or_disk_id') - boot_iso = None - if root_uuid: - try: - # Recreate the boot iso - boot_iso = _get_boot_iso(task, root_uuid) - except Exception as e: - LOG.warning(_LW("Boot iso recreation failed during take over. " - "Reason: %(reason)s. The node %(node)s may not " - "come up with current boot_iso %(boot_iso)s. "), - {'boot_iso': instance_info['ilo_boot_iso'], - 'reason': e, 'node': task.node.uuid}) - # populate the new ilo_boot_iso in node.instance_info. - if boot_iso: - instance_info['ilo_boot_iso'] = boot_iso - task.node.instance_info = instance_info - task.node.save() - else: - LOG.warning(_LW("Boot iso recreation failed during take over. " - "The node %(node)s may not come up " - "with current boot_iso %(boot_iso)s. "), - {'boot_iso': instance_info['ilo_boot_iso'], - 'node': task.node.uuid}) - else: - LOG.warning(_LW("There is not enough information to recreate " - "boot iso. The UUID for the root partition " - "could not be found. The boot-iso cannot be " - "created without root_uuid. The node %(node)s may " - "not come up with current boot_iso " - "%(boot_iso)s "), - {'boot_iso': instance_info['ilo_boot_iso'], - 'node': task.node.uuid}) - - -def _get_boot_iso_object_name(node): - """Returns the boot iso object name for a given node. - - :param node: the node for which object name is to be provided. - """ - return "boot-%s" % node.uuid - - -def _get_boot_iso(task, root_uuid): - """This method returns a boot ISO to boot the node. - - It chooses one of the three options in the order as below: - 1. Does nothing if 'ilo_boot_iso' is present in node's instance_info. - 2. Image deployed has a meta-property 'boot_iso' in Glance. This should - refer to the UUID of the boot_iso which exists in Glance. - 3. Generates a boot ISO on the fly using kernel and ramdisk mentioned in - the image deployed. It uploads the generated boot ISO to Swift. - - :param task: a TaskManager instance containing the node to act on. - :param root_uuid: the uuid of the root partition. - :returns: boot ISO URL. Should be either of below: - * A Swift object - It should be of format 'swift:'. It is - assumed that the image object is present in - CONF.ilo.swift_ilo_container; - * A Glance image - It should be format 'glance://' - or just ; - * An HTTP URL. - On error finding the boot iso, it returns None. - :raises: MissingParameterValue, if any of the required parameters are - missing in the node's driver_info or instance_info. - :raises: InvalidParameterValue, if any of the parameters have invalid - value in the node's driver_info or instance_info. - :raises: SwiftOperationError, if operation with Swift fails. - :raises: ImageCreationFailed, if creation of boot ISO failed. - :raises: exception.ImageRefValidationFailed if ilo_boot_iso is not - HTTP(S) URL. - """ - LOG.debug("Trying to get a boot ISO to boot the baremetal node") - - # Option 1 - Check if user has provided ilo_boot_iso in node's - # instance_info - if task.node.instance_info.get('ilo_boot_iso'): - LOG.debug("Using ilo_boot_iso provided in node's instance_info") - boot_iso = task.node.instance_info['ilo_boot_iso'] - if not service_utils.is_glance_image(boot_iso): - try: - image_service.HttpImageService().validate_href(boot_iso) - except exception.ImageRefValidationFailed: - with excutils.save_and_reraise_exception(): - LOG.error(_LE("Virtual media deploy accepts only Glance " - "images or HTTP(S) URLs as " - "instance_info['ilo_boot_iso']. Either %s " - "is not a valid HTTP(S) URL or is " - "not reachable."), boot_iso) - - return task.node.instance_info['ilo_boot_iso'] - - # Option 2 - Check if user has provided a boot_iso in Glance. If boot_iso - # is a supported non-glance href execution will proceed to option 3. - deploy_info = _parse_deploy_info(task.node) - - image_href = deploy_info['image_source'] - image_properties = ( - images.get_image_properties( - task.context, image_href, ['boot_iso', 'kernel_id', 'ramdisk_id'])) - - boot_iso_uuid = image_properties.get('boot_iso') - kernel_href = (task.node.instance_info.get('kernel') or - image_properties.get('kernel_id')) - ramdisk_href = (task.node.instance_info.get('ramdisk') or - image_properties.get('ramdisk_id')) - - if boot_iso_uuid: - LOG.debug("Found boot_iso %s in Glance", boot_iso_uuid) - return boot_iso_uuid - - if not kernel_href or not ramdisk_href: - LOG.error(_LE("Unable to find kernel or ramdisk for " - "image %(image)s to generate boot ISO for %(node)s"), - {'image': image_href, 'node': task.node.uuid}) - return - - # NOTE(rameshg87): Functionality to share the boot ISOs created for - # similar instances (instances with same deployed image) is - # not implemented as of now. Creation/Deletion of such a shared boot ISO - # will require synchronisation across conductor nodes for the shared boot - # ISO. Such a synchronisation mechanism doesn't exist in ironic as of now. - - # Option 3 - Create boot_iso from kernel/ramdisk, upload to Swift - # or web server and provide its name. - deploy_iso_uuid = deploy_info['ilo_deploy_iso'] - boot_mode = deploy_utils.get_boot_mode_for_deploy(task.node) - boot_iso_object_name = _get_boot_iso_object_name(task.node) - kernel_params = CONF.pxe.pxe_append_params - with tempfile.NamedTemporaryFile(dir=CONF.tempdir) as fileobj: - boot_iso_tmp_file = fileobj.name - images.create_boot_iso(task.context, boot_iso_tmp_file, - kernel_href, ramdisk_href, - deploy_iso_uuid, root_uuid, - kernel_params, boot_mode) - if CONF.ilo.use_web_server_for_images: - boot_iso_url = ( - ilo_common.copy_image_to_web_server(boot_iso_tmp_file, - boot_iso_object_name)) - driver_internal_info = task.node.driver_internal_info - driver_internal_info['boot_iso_created_in_web_server'] = True - task.node.driver_internal_info = driver_internal_info - task.node.save() - LOG.debug("Created boot_iso %(boot_iso)s for node %(node)s", - {'boot_iso': boot_iso_url, 'node': task.node.uuid}) - return boot_iso_url - else: - container = CONF.ilo.swift_ilo_container - swift_api = swift.SwiftAPI() - swift_api.create_object(container, boot_iso_object_name, - boot_iso_tmp_file) - - LOG.debug("Created boot_iso %s in Swift", boot_iso_object_name) - return 'swift:%s' % boot_iso_object_name - - -def _clean_up_boot_iso_for_instance(node): - """Deletes the boot ISO if it was created for the instance. - - :param node: an ironic node object. - """ - ilo_boot_iso = node.instance_info.get('ilo_boot_iso') - if not ilo_boot_iso: - return - if ilo_boot_iso.startswith('swift'): - swift_api = swift.SwiftAPI() - container = CONF.ilo.swift_ilo_container - boot_iso_object_name = _get_boot_iso_object_name(node) - try: - swift_api.delete_object(container, boot_iso_object_name) - except exception.SwiftOperationError as e: - LOG.exception(_LE("Failed to clean up boot ISO for node " - "%(node)s. Error: %(error)s."), - {'node': node.uuid, 'error': e}) - elif CONF.ilo.use_web_server_for_images: - result = urlparse.urlparse(ilo_boot_iso) - ilo_boot_iso_name = os.path.basename(result.path) - boot_iso_path = os.path.join( - CONF.deploy.http_root, ilo_boot_iso_name) - utils.unlink_without_raise(boot_iso_path) - - -def _parse_driver_info(node): - """Gets the driver specific Node deployment info. - - This method validates whether the 'driver_info' property of the - supplied node contains the required information for this driver to - deploy images to the node. - - :param node: a single Node. - :returns: A dict with the driver_info values. - :raises: MissingParameterValue, if any of the required parameters are - missing. - """ - info = node.driver_info - d_info = {} - d_info['ilo_deploy_iso'] = info.get('ilo_deploy_iso') - - error_msg = _("Error validating iLO virtual media deploy. Some parameters" - " were missing in node's driver_info") - deploy_utils.check_for_missing_params(d_info, error_msg) - - return d_info - - -def _parse_deploy_info(node): - """Gets the instance and driver specific Node deployment info. - - This method validates whether the 'instance_info' and 'driver_info' - property of the supplied node contains the required information for - this driver to deploy images to the node. - - :param node: a single Node. - :returns: A dict with the instance_info and driver_info values. - :raises: MissingParameterValue, if any of the required parameters are - missing. - :raises: InvalidParameterValue, if any of the parameters have invalid - value. - """ - info = {} - info.update(iscsi_deploy.parse_instance_info(node)) - info.update(_parse_driver_info(node)) - return info - - -def _reboot_into(task, iso, ramdisk_options): - """Reboots the node into a given boot ISO. - - This method attaches the given bootable ISO as virtual media, prepares the - arguments for ramdisk in virtual media floppy, and then reboots the node. - - :param task: a TaskManager instance containing the node to act on. - :param iso: a bootable ISO image href to attach to. Should be either - of below: - * A Swift object - It should be of format 'swift:'. - It is assumed that the image object is present in - CONF.ilo.swift_ilo_container; - * A Glance image - It should be format 'glance://' - or just ; - * An HTTP URL. - :param ramdisk_options: the options to be passed to the ramdisk in virtual - media floppy. - :raises: ImageCreationFailed, if it failed while creating the floppy image. - :raises: IloOperationError, if some operation on iLO failed. - """ - ilo_common.setup_vmedia_for_boot(task, iso, ramdisk_options) - - # In UEFI boot mode, upon inserting virtual CDROM, one has to reset the - # system to see it as a valid boot device in persistent boot devices. - # But virtual CDROM device is always available for one-time boot. - # During enable/disable of secure boot settings, iLO internally resets - # the server twice. But it retains one time boot settings across internal - # resets. Hence no impact of this change for secure boot deploy. - manager_utils.node_set_boot_device(task, boot_devices.CDROM) - manager_utils.node_power_action(task, states.REBOOT) - - def _prepare_agent_vmedia_boot(task): """Ejects virtual media devices and prepares for vmedia boot.""" # Eject all virtual media devices, as we are going to use them @@ -345,7 +61,8 @@ def _prepare_agent_vmedia_boot(task): deploy_ramdisk_opts = deploy_utils.build_agent_options(task.node) deploy_iso = task.node.driver_info['ilo_deploy_iso'] - _reboot_into(task, deploy_iso, deploy_ramdisk_opts) + ilo_common.setup_vmedia(task, deploy_iso, deploy_ramdisk_opts) + manager_utils.node_power_action(task, states.REBOOT) def _disable_secure_boot(task): @@ -411,28 +128,6 @@ def _prepare_node_for_deploy(task): task.node.save() -def _update_secure_boot_mode(task, mode): - """Changes secure boot mode for next boot on the node. - - This method changes secure boot mode on the node for next boot. It changes - the secure boot mode setting on node only if the deploy has requested for - the secure boot. - During deploy, this method is used to enable secure boot on the node by - passing 'mode' as 'True'. - During teardown, this method is used to disable secure boot on the node by - passing 'mode' as 'False'. - - :param task: a TaskManager instance containing the node to act on. - :param mode: Boolean value requesting the next state for secure boot - :raises: IloOperationNotSupported, if operation is not supported on iLO - :raises: IloOperationError, if some operation on iLO failed. - """ - if deploy_utils.is_secure_boot_requested(task.node): - ilo_common.set_secure_boot_mode(task, mode) - LOG.info(_LI('Changed secure boot to %(mode)s for node %(node)s'), - {'mode': mode, 'node': task.node.uuid}) - - def _disable_secure_boot_if_supported(task): """Disables secure boot on node, does not throw if its not supported. @@ -440,7 +135,7 @@ def _disable_secure_boot_if_supported(task): :raises: IloOperationError, if some operation on iLO failed. """ try: - _update_secure_boot_mode(task, False) + ilo_common.update_secure_boot_mode(task, False) # We need to handle IloOperationNotSupported exception so that if # the user has incorrectly specified the Node capability # 'secure_boot' to a node that does not have that capability and @@ -451,79 +146,10 @@ def _disable_secure_boot_if_supported(task): task.node.uuid) -class IloVirtualMediaIscsiDeploy(base.DeployInterface): +class IloVirtualMediaIscsiDeploy(iscsi_deploy.ISCSIDeploy): def get_properties(self): - return COMMON_PROPERTIES - - def validate(self, task): - """Validate the deployment information for the task's node. - - :param task: a TaskManager instance containing the node to act on. - :raises: InvalidParameterValue, if some information is invalid. - :raises: MissingParameterValue if 'kernel_id' and 'ramdisk_id' are - missing in the Glance image or 'kernel' and 'ramdisk' not provided - in instance_info for non-Glance image. - """ - iscsi_deploy.validate(task) - node = task.node - - d_info = _parse_deploy_info(node) - - if node.driver_internal_info.get('is_whole_disk_image'): - props = [] - elif service_utils.is_glance_image(d_info['image_source']): - props = ['kernel_id', 'ramdisk_id'] - else: - props = ['kernel', 'ramdisk'] - deploy_utils.validate_image_properties(task.context, d_info, props) - deploy_utils.validate_capabilities(node) - - @task_manager.require_exclusive_lock - def deploy(self, task): - """Start deployment of the task's node. - - Fetches the instance image, prepares the options for the deployment - ramdisk, sets the node to boot from virtual media cdrom, and reboots - the given node. - - :param task: a TaskManager instance containing the node to act on. - :returns: deploy state DEPLOYWAIT. - :raises: InstanceDeployFailure, if image size if greater than root - partition. - :raises: ImageCreationFailed, if it failed while creating the floppy - image. - :raises: IloOperationError, if some operation on iLO fails. - """ - node = task.node - - # Clear ilo_boot_iso if it's a glance image to force recreate - # another one again (or use existing one in glance). - # This is mainly for rebuild scenario. - if service_utils.is_glance_image( - node.instance_info.get('image_source')): - instance_info = node.instance_info - instance_info.pop('ilo_boot_iso', None) - node.instance_info = instance_info - node.save() - - # Eject all virtual media devices, as we are going to use them - # during deploy. - ilo_common.eject_vmedia_devices(task) - - iscsi_deploy.cache_instance_image(task.context, node) - iscsi_deploy.check_image_size(task) - - deploy_ramdisk_opts = iscsi_deploy.build_deploy_ramdisk_options(node) - agent_opts = deploy_utils.build_agent_options(node) - deploy_ramdisk_opts.update(agent_opts) - deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task) - deploy_ramdisk_opts['BOOTIF'] = deploy_nic_mac - deploy_iso = node.driver_info['ilo_deploy_iso'] - - _reboot_into(task, deploy_iso, deploy_ramdisk_opts) - - return states.DEPLOYWAIT + return {} @task_manager.require_exclusive_lock def tear_down(self, task): @@ -534,15 +160,12 @@ class IloVirtualMediaIscsiDeploy(base.DeployInterface): :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) _disable_secure_boot_if_supported(task) - driver_internal_info = task.node.driver_internal_info - driver_internal_info.pop('boot_iso_created_in_web_server', None) - driver_internal_info.pop('root_uuid_or_disk_id', None) - task.node.driver_internal_info = driver_internal_info - task.node.save() - return states.DELETED + return super(IloVirtualMediaIscsiDeploy, self).tear_down(task) def prepare(self, task): """Prepare the deployment environment for this task's node. @@ -553,33 +176,7 @@ class IloVirtualMediaIscsiDeploy(base.DeployInterface): if task.node.provision_state != states.ACTIVE: _prepare_node_for_deploy(task) - def clean_up(self, task): - """Clean up the deployment environment for the task's node. - - Unlinks instance image and triggers image cache cleanup. - - :param task: a TaskManager instance containing the node to act on. - """ - _clean_up_boot_iso_for_instance(task.node) - if not CONF.ilo.use_web_server_for_images: - iscsi_deploy.destroy_images(task.node.uuid) - else: - ilo_common.destroy_floppy_image_from_web_server(task.node) - - def take_over(self, task): - """Enables boot up of an ACTIVE node. - - It ensures that the ACTIVE node can be booted up successfully - when node is taken over by another conductor. - - :param: task: a TaskManager instance containing the node to act on. - """ - driver_internal_info = task.node.driver_internal_info - boot_iso_created_in_web_server = ( - driver_internal_info.get('boot_iso_created_in_web_server')) - if (CONF.ilo.use_web_server_for_images - and boot_iso_created_in_web_server): - _recreate_and_populate_ilo_boot_iso(task) + super(IloVirtualMediaIscsiDeploy, self).prepare(task) class IloVirtualMediaAgentDeploy(base.DeployInterface): @@ -590,7 +187,7 @@ class IloVirtualMediaAgentDeploy(base.DeployInterface): :returns: dictionary of : entries. """ - return COMMON_PROPERTIES + return ilo_boot.COMMON_PROPERTIES def validate(self, task): """Validate the driver-specific Node deployment info. @@ -600,7 +197,7 @@ class IloVirtualMediaAgentDeploy(base.DeployInterface): """ deploy_utils.validate_capabilities(task.node) - _parse_driver_info(task.node) + ilo_boot.parse_driver_info(task.node) @task_manager.require_exclusive_lock def deploy(self, task): @@ -709,32 +306,6 @@ class IloVirtualMediaAgentDeploy(base.DeployInterface): provider.delete_cleaning_ports(task) -class IloVirtualMediaAgentVendorInterface(agent.AgentVendorInterface): - """Interface for vendor passthru related 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: - # Set boot mode - ilo_common.update_boot_mode(task) - - # Need to enable secure boot, if being requested - _update_secure_boot_mode(task, True) - - super(IloVirtualMediaAgentVendorInterface, - self).reboot_to_instance(task, **kwargs) - - @task_manager.require_exclusive_lock - def continue_deploy(self, task, **kwargs): - ilo_common.cleanup_vmedia_boot(task) - super(IloVirtualMediaAgentVendorInterface, - self).continue_deploy(task, **kwargs) - - class IloPXEDeploy(iscsi_deploy.ISCSIDeploy): def prepare(self, task): @@ -816,266 +387,3 @@ class IloConsoleInterface(ipmitool.IPMIShellinaboxConsole): ilo_common.update_ipmi_properties(task) super(IloConsoleInterface, self).validate(task) - - -class IloPXEVendorPassthru(iscsi_deploy.VendorPassthru): - - @base.passthru(['POST']) - def pass_deploy_info(self, task, **kwargs): - LOG.debug('Pass deploy info for the deployment on node %s', - task.node.uuid) - manager_utils.node_set_boot_device(task, boot_devices.PXE, - persistent=True) - # Set boot mode - ilo_common.update_boot_mode(task) - # Need to enable secure boot, if being requested - _update_secure_boot_mode(task, True) - - super(IloPXEVendorPassthru, self).pass_deploy_info(task, **kwargs) - - @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 deploys the image on - the node and then configures the node to boot according to the - desired boot option (netboot or localboot). - - :param task: a TaskManager object containing the node. - :param kwargs: the kwargs passed from the heartbeat method. - :raises: InstanceDeployFailure, if it encounters some error during - the deploy. - :raises: IloOperationError, if some operation on iLO failed. - """ - LOG.debug('Continuing the deployment on node %s', task.node.uuid) - # Set boot mode - ilo_common.update_boot_mode(task) - # Need to enable secure boot, if being requested - _update_secure_boot_mode(task, True) - - super(IloPXEVendorPassthru, self).continue_deploy(task, **kwargs) - - -class VendorPassthru(agent_base_vendor.BaseAgentVendor): - """Vendor-specific interfaces for iLO deploy drivers.""" - - def get_properties(self): - return COMMON_PROPERTIES - - def validate(self, task, method, **kwargs): - """Validate vendor-specific actions. - - Checks if a valid vendor passthru method was passed and validates - the parameters for the vendor passthru method. - - :param task: a TaskManager instance containing the node to act on. - :param method: method to be validated. - :param kwargs: kwargs containing the vendor passthru method's - parameters. - :raises: MissingParameterValue, if some required parameters were not - passed. - :raises: InvalidParameterValue, if any of the parameters have invalid - value. - """ - if method == 'pass_deploy_info': - iscsi_deploy.get_deploy_info(task.node, **kwargs) - elif method == 'pass_bootloader_install_info': - iscsi_deploy.validate_pass_bootloader_info_input(task, kwargs) - elif method == 'boot_into_iso': - self._validate_boot_into_iso(task, kwargs) - - def _validate_boot_into_iso(self, task, kwargs): - """Validates if attach_iso can be called and if inputs are proper.""" - if not (task.node.provision_state == states.MANAGEABLE or - task.node.maintenance is True): - msg = (_("The requested action 'boot_into_iso' can be performed " - "only when node %(node_uuid)s is in %(state)s state or " - "in 'maintenance' mode") % - {'node_uuid': task.node.uuid, - 'state': states.MANAGEABLE}) - raise exception.InvalidStateRequested(msg) - d_info = {'boot_iso_href': kwargs.get('boot_iso_href')} - error_msg = _("Error validating input for boot_into_iso vendor " - "passthru. Some parameters were not provided: ") - deploy_utils.check_for_missing_params(d_info, error_msg) - deploy_utils.validate_image_properties( - task.context, {'image_source': kwargs.get('boot_iso_href')}, []) - - @base.passthru(['POST']) - @task_manager.require_exclusive_lock - def boot_into_iso(self, task, **kwargs): - """Attaches an ISO image in glance and reboots bare metal. - - This method accepts an ISO image href (a Glance UUID or an HTTP(S) URL) - attaches it as virtual media and then reboots the node. This is - useful for debugging purposes. This can be invoked only when the node - is in manage state. - - :param task: A TaskManager object. - :param kwargs: The arguments sent with vendor passthru. The expected - kwargs are:: - - 'boot_iso_href': href of the image to be booted. This can be - a Glance UUID or an HTTP(S) URL. - """ - _reboot_into(task, kwargs['boot_iso_href'], ramdisk_options=None) - - def _configure_vmedia_boot(self, task, root_uuid): - """Configure vmedia boot for the node.""" - node = task.node - boot_iso = _get_boot_iso(task, root_uuid) - if not boot_iso: - LOG.error(_LE("Cannot get boot ISO for node %s"), node.uuid) - return - - # Upon deploy complete, some distros cloud images reboot the system as - # part of its configuration. Hence boot device should be persistent and - # not one-time. - ilo_common.setup_vmedia_for_boot(task, boot_iso) - manager_utils.node_set_boot_device(task, - boot_devices.CDROM, - persistent=True) - - i_info = node.instance_info - if not i_info.get('ilo_boot_iso'): - i_info['ilo_boot_iso'] = boot_iso - node.instance_info = i_info - - @base.passthru(['POST']) - @task_manager.require_exclusive_lock - def pass_bootloader_install_info(self, task, **kwargs): - """Accepts the results of bootloader installation. - - This method acts as a vendor passthru and accepts the result of - bootloader installation. If the bootloader installation was - successful, then it notifies the baremetal to proceed to reboot - and makes the instance active. If bootloader installation failed, - then it sets provisioning as failed and powers off the node. - - :param task: A TaskManager object. - :param kwargs: The arguments sent with vendor passthru. The expected - kwargs are:: - - 'key': The deploy key for authorization - 'status': 'SUCCEEDED' or 'FAILED' - 'error': The error message if status == 'FAILED' - 'address': The IP address of the ramdisk - """ - LOG.warning(_LW("The node %s is using the bash deploy ramdisk for " - "its deployment. This deploy ramdisk has been " - "deprecated. Please use the ironic-python-agent " - "(IPA) ramdisk instead."), task.node.uuid) - task.process_event('resume') - iscsi_deploy.validate_bootloader_install_status(task, kwargs) - iscsi_deploy.finish_deploy(task, kwargs['address']) - - @base.passthru(['POST']) - @task_manager.require_exclusive_lock - def pass_deploy_info(self, task, **kwargs): - """Continues the iSCSI deployment from where ramdisk left off. - - This method continues the iSCSI deployment from the conductor node - and writes the deploy image to the bare metal's disk. After that, - it does the following depending on boot_option for deploy: - - - If the boot_option requested for this deploy is 'local', then it - sets the node to boot from disk (ramdisk installs the boot loader - present within the image to the bare metal's disk). - - If the boot_option requested is 'netboot' or no boot_option is - requested, it finds/creates the boot ISO to boot the instance - image, attaches the boot ISO to the bare metal and then sets - the node to boot from CDROM. - - :param task: a TaskManager instance containing the node to act on. - :param kwargs: kwargs containing parameters for iSCSI deployment. - :raises: InvalidState - """ - node = task.node - LOG.warning(_LW("The node %s is using the bash deploy ramdisk for " - "its deployment. This deploy ramdisk has been " - "deprecated. Please use the ironic-python-agent " - "(IPA) ramdisk instead."), node.uuid) - task.process_event('resume') - - iwdi = node.driver_internal_info.get('is_whole_disk_image') - ilo_common.cleanup_vmedia_boot(task) - uuid_dict = iscsi_deploy.continue_deploy(task, **kwargs) - root_uuid_or_disk_id = uuid_dict.get( - 'root uuid', uuid_dict.get('disk identifier')) - driver_internal_info = task.node.driver_internal_info - driver_internal_info['root_uuid_or_disk_id'] = root_uuid_or_disk_id - task.node.driver_internal_info = driver_internal_info - task.node.save() - - try: - # Set boot mode - ilo_common.update_boot_mode(task) - - # Need to enable secure boot, if being requested - _update_secure_boot_mode(task, True) - - # For iscsi_ilo driver, we boot from disk every time if the image - # deployed is a whole disk image. - if deploy_utils.get_boot_option(node) == "local" or iwdi: - manager_utils.node_set_boot_device(task, boot_devices.DISK, - persistent=True) - - # Ask the ramdisk to install bootloader and - # wait for the call-back through the vendor passthru - # 'pass_bootloader_install_info', if it's not a whole - # disk image. - if not iwdi: - deploy_utils.notify_ramdisk_to_proceed(kwargs['address']) - task.process_event('wait') - return - else: - self._configure_vmedia_boot(task, root_uuid_or_disk_id) - except Exception as e: - LOG.error(_LE('Deploy failed for instance %(instance)s. ' - 'Error: %(error)s'), - {'instance': node.instance_uuid, 'error': e}) - msg = _('Failed to continue iSCSI deployment.') - deploy_utils.set_failed_state(task, msg) - else: - iscsi_deploy.finish_deploy(task, kwargs.get('address')) - - @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 deploys the image on - the node and then configures the node to boot according to the - desired boot option (netboot or localboot). - - :param task: a TaskManager object containing the node. - :param kwargs: the kwargs passed from the heartbeat method. - :raises: InstanceDeployFailure, if it encounters some error during - the deploy. - """ - task.process_event('resume') - node = task.node - LOG.debug('Continuing the deployment on node %s', node.uuid) - ilo_common.cleanup_vmedia_boot(task) - - iwdi = node.driver_internal_info.get('is_whole_disk_image') - uuid_dict = iscsi_deploy.do_agent_iscsi_deploy(task, self._client) - root_uuid = uuid_dict.get('root uuid') - - if deploy_utils.get_boot_option(node) == "local" or iwdi: - efi_system_part_uuid = uuid_dict.get( - 'efi system partition uuid') - self.configure_local_boot( - task, root_uuid=root_uuid, - efi_system_part_uuid=efi_system_part_uuid) - else: - self._configure_vmedia_boot(task, root_uuid) - - # Set boot mode - ilo_common.update_boot_mode(task) - - # Need to enable secure boot, if being requested - _update_secure_boot_mode(task, True) - - self.reboot_and_finish_deploy(task) diff --git a/ironic/drivers/modules/ilo/vendor.py b/ironic/drivers/modules/ilo/vendor.py new file mode 100644 index 0000000000..9e99ffe9c0 --- /dev/null +++ b/ironic/drivers/modules/ilo/vendor.py @@ -0,0 +1,154 @@ +# Copyright 2015 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. +""" +Vendor Interface for iLO drivers and its supporting methods. +""" + +from oslo_config import cfg +from oslo_log import log as logging + +from ironic.common import exception +from ironic.common.i18n import _ +from ironic.common import states +from ironic.conductor import task_manager +from ironic.conductor import utils as manager_utils +from ironic.drivers import base +from ironic.drivers.modules import agent +from ironic.drivers.modules import deploy_utils +from ironic.drivers.modules.ilo import common as ilo_common +from ironic.drivers.modules import iscsi_deploy + +LOG = logging.getLogger(__name__) + +CONF = cfg.CONF + + +class IloVirtualMediaAgentVendorInterface(agent.AgentVendorInterface): + """Interface for vendor passthru related 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: + # 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(IloVirtualMediaAgentVendorInterface, + self).reboot_to_instance(task, **kwargs) + + @task_manager.require_exclusive_lock + def continue_deploy(self, task, **kwargs): + ilo_common.cleanup_vmedia_boot(task) + super(IloVirtualMediaAgentVendorInterface, + self).continue_deploy(task, **kwargs) + + +class VendorPassthru(iscsi_deploy.VendorPassthru): + """Vendor-specific interfaces for iLO deploy drivers.""" + + def get_properties(self): + return {} + + def validate(self, task, method, **kwargs): + """Validate vendor-specific actions. + + Checks if a valid vendor passthru method was passed and validates + the parameters for the vendor passthru method. + + :param task: a TaskManager instance containing the node to act on. + :param method: method to be validated. + :param kwargs: kwargs containing the vendor passthru method's + parameters. + :raises: MissingParameterValue, if some required parameters were not + passed. + :raises: InvalidParameterValue, if any of the parameters have invalid + value. + """ + if method == 'boot_into_iso': + self._validate_boot_into_iso(task, kwargs) + return + super(VendorPassthru, self).validate(task, method, **kwargs) + + @base.passthru(['POST']) + @task_manager.require_exclusive_lock + def pass_deploy_info(self, task, **kwargs): + """Continues the deployment of baremetal node over iSCSI. + + This method continues the deployment of the baremetal node over iSCSI + from where the deployment ramdisk has left off. + This updates boot mode and secure boot settings, if required. + + :param task: a TaskManager instance containing the node to act on. + :param **kwargs: kwargs for performing iscsi deployment. + :raises: InvalidState + """ + ilo_common.update_boot_mode(task) + ilo_common.update_secure_boot_mode(task, True) + super(VendorPassthru, self).pass_deploy_info(task, **kwargs) + + @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(VendorPassthru, self).continue_deploy(task, **kwargs) + + def _validate_boot_into_iso(self, task, kwargs): + """Validates if attach_iso can be called and if inputs are proper.""" + if not (task.node.provision_state == states.MANAGEABLE or + task.node.maintenance is True): + msg = (_("The requested action 'boot_into_iso' can be performed " + "only when node %(node_uuid)s is in %(state)s state or " + "in 'maintenance' mode") % + {'node_uuid': task.node.uuid, + 'state': states.MANAGEABLE}) + raise exception.InvalidStateRequested(msg) + d_info = {'boot_iso_href': kwargs.get('boot_iso_href')} + error_msg = _("Error validating input for boot_into_iso vendor " + "passthru. Some parameters were not provided: ") + deploy_utils.check_for_missing_params(d_info, error_msg) + deploy_utils.validate_image_properties( + task.context, {'image_source': kwargs.get('boot_iso_href')}, []) + + @base.passthru(['POST']) + @task_manager.require_exclusive_lock + def boot_into_iso(self, task, **kwargs): + """Attaches an ISO image in glance and reboots bare metal. + + This method accepts an ISO image href (a Glance UUID or an HTTP(S) URL) + attaches it as virtual media and then reboots the node. This is + useful for debugging purposes. This can be invoked only when the node + is in manage state. + + :param task: A TaskManager object. + :param kwargs: The arguments sent with vendor passthru. The expected + kwargs are:: + + 'boot_iso_href': href of the image to be booted. This can be + a Glance UUID or an HTTP(S) URL. + """ + ilo_common.setup_vmedia(task, kwargs['boot_iso_href'], + ramdisk_options=None) + manager_utils.node_power_action(task, states.REBOOT) diff --git a/ironic/drivers/pxe.py b/ironic/drivers/pxe.py index 9af175c41e..7f25c946ec 100644 --- a/ironic/drivers/pxe.py +++ b/ironic/drivers/pxe.py @@ -33,6 +33,7 @@ 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 +from ironic.drivers.modules.ilo import vendor as ilo_vendor from ironic.drivers.modules import inspector from ironic.drivers.modules import ipminative from ironic.drivers.modules import ipmitool @@ -210,7 +211,7 @@ class PXEAndIloDriver(base.BaseDriver): self.power = ilo_power.IloPower() self.boot = pxe.PXEBoot() self.deploy = ilo_deploy.IloPXEDeploy() - self.vendor = ilo_deploy.IloPXEVendorPassthru() + self.vendor = ilo_vendor.VendorPassthru() self.console = ilo_deploy.IloConsoleInterface() self.management = ilo_management.IloManagement() self.inspect = ilo_inspect.IloInspect() diff --git a/ironic/tests/unit/drivers/modules/ilo/test_boot.py b/ironic/tests/unit/drivers/modules/ilo/test_boot.py new file mode 100644 index 0000000000..afd2b6bcb6 --- /dev/null +++ b/ironic/tests/unit/drivers/modules/ilo/test_boot.py @@ -0,0 +1,540 @@ +# Copyright 2015 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 common methods used by iLO modules.""" + +import tempfile + +import mock +from oslo_config import cfg +import six + +from ironic.common import boot_devices +from ironic.common import exception +from ironic.common.glance_service import service_utils +from ironic.common import image_service +from ironic.common import images +from ironic.common import swift +from ironic.common import utils +from ironic.conductor import task_manager +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 import utils as driver_utils +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() +CONF = cfg.CONF + + +class IloBootPrivateMethodsTestCase(db_base.DbTestCase): + + def setUp(self): + super(IloBootPrivateMethodsTestCase, self).setUp() + mgr_utils.mock_the_extension_manager(driver="iscsi_ilo") + self.node = obj_utils.create_test_node( + self.context, driver='iscsi_ilo', driver_info=INFO_DICT) + + def test__get_boot_iso_object_name(self): + boot_iso_actual = ilo_boot._get_boot_iso_object_name(self.node) + boot_iso_expected = "boot-%s" % self.node.uuid + self.assertEqual(boot_iso_expected, boot_iso_actual) + + @mock.patch.object(image_service.HttpImageService, 'validate_href', + spec_set=True, autospec=True) + def test__get_boot_iso_http_url(self, service_mock): + url = 'http://abc.org/image/qcow2' + i_info = self.node.instance_info + i_info['ilo_boot_iso'] = url + self.node.instance_info = i_info + self.node.save() + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + boot_iso_actual = ilo_boot._get_boot_iso(task, 'root-uuid') + service_mock.assert_called_once_with(mock.ANY, url) + self.assertEqual(url, boot_iso_actual) + + @mock.patch.object(image_service.HttpImageService, 'validate_href', + spec_set=True, autospec=True) + def test__get_boot_iso_unsupported_url(self, validate_href_mock): + validate_href_mock.side_effect = iter( + [exception.ImageRefValidationFailed( + image_href='file://img.qcow2', reason='fail')]) + url = 'file://img.qcow2' + i_info = self.node.instance_info + i_info['ilo_boot_iso'] = url + self.node.instance_info = i_info + self.node.save() + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + self.assertRaises(exception.ImageRefValidationFailed, + ilo_boot._get_boot_iso, task, 'root-uuid') + + @mock.patch.object(images, 'get_image_properties', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_parse_deploy_info', spec_set=True, + autospec=True) + def test__get_boot_iso_glance_image(self, deploy_info_mock, + image_props_mock): + deploy_info_mock.return_value = {'image_source': 'image-uuid', + 'ilo_deploy_iso': 'deploy_iso_uuid'} + image_props_mock.return_value = {'boot_iso': u'glance://uui\u0111', + 'kernel_id': None, + 'ramdisk_id': None} + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + driver_internal_info = task.node.driver_internal_info + driver_internal_info['boot_iso_created_in_web_server'] = False + task.node.driver_internal_info = driver_internal_info + task.node.save() + boot_iso_actual = ilo_boot._get_boot_iso(task, 'root-uuid') + deploy_info_mock.assert_called_once_with(task.node) + image_props_mock.assert_called_once_with( + task.context, 'image-uuid', + ['boot_iso', 'kernel_id', 'ramdisk_id']) + boot_iso_expected = u'glance://uui\u0111' + self.assertEqual(boot_iso_expected, boot_iso_actual) + + @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy', + spec_set=True, autospec=True) + @mock.patch.object(ilo_boot.LOG, 'error', spec_set=True, autospec=True) + @mock.patch.object(images, 'get_image_properties', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_parse_deploy_info', spec_set=True, + autospec=True) + def test__get_boot_iso_uefi_no_glance_image(self, + deploy_info_mock, + image_props_mock, + log_mock, + boot_mode_mock): + deploy_info_mock.return_value = {'image_source': 'image-uuid', + 'ilo_deploy_iso': 'deploy_iso_uuid'} + image_props_mock.return_value = {'boot_iso': None, + 'kernel_id': None, + 'ramdisk_id': None} + properties = {'capabilities': 'boot_mode:uefi'} + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.node.properties = properties + boot_iso_result = ilo_boot._get_boot_iso(task, 'root-uuid') + deploy_info_mock.assert_called_once_with(task.node) + image_props_mock.assert_called_once_with( + task.context, 'image-uuid', + ['boot_iso', 'kernel_id', 'ramdisk_id']) + self.assertTrue(log_mock.called) + self.assertFalse(boot_mode_mock.called) + self.assertIsNone(boot_iso_result) + + @mock.patch.object(tempfile, 'NamedTemporaryFile', spec_set=True, + autospec=True) + @mock.patch.object(images, 'create_boot_iso', spec_set=True, autospec=True) + @mock.patch.object(swift, 'SwiftAPI', spec_set=True, autospec=True) + @mock.patch.object(ilo_boot, '_get_boot_iso_object_name', spec_set=True, + autospec=True) + @mock.patch.object(driver_utils, 'get_node_capability', spec_set=True, + autospec=True) + @mock.patch.object(images, 'get_image_properties', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_parse_deploy_info', spec_set=True, + autospec=True) + def test__get_boot_iso_create(self, deploy_info_mock, image_props_mock, + capability_mock, boot_object_name_mock, + swift_api_mock, + create_boot_iso_mock, tempfile_mock): + CONF.ilo.swift_ilo_container = 'ilo-cont' + CONF.pxe.pxe_append_params = 'kernel-params' + + swift_obj_mock = swift_api_mock.return_value + fileobj_mock = mock.MagicMock(spec=file) + fileobj_mock.name = 'tmpfile' + mock_file_handle = mock.MagicMock(spec=file) + mock_file_handle.__enter__.return_value = fileobj_mock + tempfile_mock.return_value = mock_file_handle + + deploy_info_mock.return_value = {'image_source': 'image-uuid', + 'ilo_deploy_iso': 'deploy_iso_uuid'} + image_props_mock.return_value = {'boot_iso': None, + 'kernel_id': 'kernel_uuid', + 'ramdisk_id': 'ramdisk_uuid'} + boot_object_name_mock.return_value = 'abcdef' + create_boot_iso_mock.return_value = '/path/to/boot-iso' + capability_mock.return_value = 'uefi' + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + boot_iso_actual = ilo_boot._get_boot_iso(task, 'root-uuid') + deploy_info_mock.assert_called_once_with(task.node) + image_props_mock.assert_called_once_with( + task.context, 'image-uuid', + ['boot_iso', 'kernel_id', 'ramdisk_id']) + boot_object_name_mock.assert_called_once_with(task.node) + create_boot_iso_mock.assert_called_once_with(task.context, + 'tmpfile', + 'kernel_uuid', + 'ramdisk_uuid', + 'deploy_iso_uuid', + 'root-uuid', + 'kernel-params', + 'uefi') + swift_obj_mock.create_object.assert_called_once_with('ilo-cont', + 'abcdef', + 'tmpfile') + boot_iso_expected = 'swift:abcdef' + self.assertEqual(boot_iso_expected, boot_iso_actual) + + @mock.patch.object(ilo_common, 'copy_image_to_web_server', spec_set=True, + autospec=True) + @mock.patch.object(tempfile, 'NamedTemporaryFile', spec_set=True, + autospec=True) + @mock.patch.object(images, 'create_boot_iso', spec_set=True, autospec=True) + @mock.patch.object(ilo_boot, '_get_boot_iso_object_name', spec_set=True, + autospec=True) + @mock.patch.object(driver_utils, 'get_node_capability', spec_set=True, + autospec=True) + @mock.patch.object(images, 'get_image_properties', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_parse_deploy_info', spec_set=True, + autospec=True) + def test__get_boot_iso_create_use_webserver_true_ramdisk_webserver( + self, deploy_info_mock, image_props_mock, + capability_mock, boot_object_name_mock, + create_boot_iso_mock, tempfile_mock, + copy_file_mock): + CONF.ilo.swift_ilo_container = 'ilo-cont' + CONF.ilo.use_web_server_for_images = True + CONF.deploy.http_url = "http://10.10.1.30/httpboot" + CONF.deploy.http_root = "/httpboot" + CONF.pxe.pxe_append_params = 'kernel-params' + + fileobj_mock = mock.MagicMock(spec=file) + fileobj_mock.name = 'tmpfile' + mock_file_handle = mock.MagicMock(spec=file) + mock_file_handle.__enter__.return_value = fileobj_mock + tempfile_mock.return_value = mock_file_handle + + ramdisk_href = "http://10.10.1.30/httpboot/ramdisk" + kernel_href = "http://10.10.1.30/httpboot/kernel" + deploy_info_mock.return_value = {'image_source': 'image-uuid', + 'ilo_deploy_iso': 'deploy_iso_uuid'} + image_props_mock.return_value = {'boot_iso': None, + 'kernel_id': kernel_href, + 'ramdisk_id': ramdisk_href} + boot_object_name_mock.return_value = 'abcdef' + create_boot_iso_mock.return_value = '/path/to/boot-iso' + capability_mock.return_value = 'uefi' + copy_file_mock.return_value = "http://10.10.1.30/httpboot/abcdef" + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + boot_iso_actual = ilo_boot._get_boot_iso(task, 'root-uuid') + deploy_info_mock.assert_called_once_with(task.node) + image_props_mock.assert_called_once_with( + task.context, 'image-uuid', + ['boot_iso', 'kernel_id', 'ramdisk_id']) + boot_object_name_mock.assert_called_once_with(task.node) + create_boot_iso_mock.assert_called_once_with(task.context, + 'tmpfile', + kernel_href, + ramdisk_href, + 'deploy_iso_uuid', + 'root-uuid', + 'kernel-params', + 'uefi') + boot_iso_expected = 'http://10.10.1.30/httpboot/abcdef' + self.assertEqual(boot_iso_expected, boot_iso_actual) + copy_file_mock.assert_called_once_with(fileobj_mock.name, + 'abcdef') + + @mock.patch.object(ilo_boot, '_get_boot_iso_object_name', spec_set=True, + autospec=True) + @mock.patch.object(swift, 'SwiftAPI', spec_set=True, autospec=True) + def test__clean_up_boot_iso_for_instance(self, swift_mock, + boot_object_name_mock): + swift_obj_mock = swift_mock.return_value + CONF.ilo.swift_ilo_container = 'ilo-cont' + boot_object_name_mock.return_value = 'boot-object' + i_info = self.node.instance_info + i_info['ilo_boot_iso'] = 'swift:bootiso' + self.node.instance_info = i_info + self.node.save() + ilo_boot._clean_up_boot_iso_for_instance(self.node) + swift_obj_mock.delete_object.assert_called_once_with('ilo-cont', + 'boot-object') + + @mock.patch.object(ilo_boot.LOG, 'exception', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_get_boot_iso_object_name', spec_set=True, + autospec=True) + @mock.patch.object(swift, 'SwiftAPI', spec_set=True, autospec=True) + def test__clean_up_boot_iso_for_instance_exc(self, swift_mock, + boot_object_name_mock, + log_mock): + swift_obj_mock = swift_mock.return_value + exc = exception.SwiftObjectNotFoundError('error') + swift_obj_mock.delete_object.side_effect = exc + CONF.ilo.swift_ilo_container = 'ilo-cont' + boot_object_name_mock.return_value = 'boot-object' + i_info = self.node.instance_info + i_info['ilo_boot_iso'] = 'swift:bootiso' + self.node.instance_info = i_info + self.node.save() + ilo_boot._clean_up_boot_iso_for_instance(self.node) + swift_obj_mock.delete_object.assert_called_once_with('ilo-cont', + 'boot-object') + self.assertTrue(log_mock.called) + + @mock.patch.object(utils, 'unlink_without_raise', spec_set=True, + autospec=True) + def test__clean_up_boot_iso_for_instance_on_webserver(self, unlink_mock): + + CONF.ilo.use_web_server_for_images = True + CONF.deploy.http_root = "/webserver" + i_info = self.node.instance_info + i_info['ilo_boot_iso'] = 'http://x.y.z.a/webserver/boot-object' + self.node.instance_info = i_info + self.node.save() + boot_iso_path = "/webserver/boot-object" + ilo_boot._clean_up_boot_iso_for_instance(self.node) + unlink_mock.assert_called_once_with(boot_iso_path) + + @mock.patch.object(ilo_boot, '_get_boot_iso_object_name', spec_set=True, + autospec=True) + def test__clean_up_boot_iso_for_instance_no_boot_iso( + self, boot_object_name_mock): + ilo_boot._clean_up_boot_iso_for_instance(self.node) + self.assertFalse(boot_object_name_mock.called) + + @mock.patch.object(ilo_boot, 'parse_driver_info', spec_set=True, + autospec=True) + @mock.patch.object(deploy_utils, 'get_image_instance_info', + spec_set=True, autospec=True) + def test__parse_deploy_info(self, instance_info_mock, driver_info_mock): + instance_info_mock.return_value = {'a': 'b'} + driver_info_mock.return_value = {'c': 'd'} + expected_info = {'a': 'b', 'c': 'd'} + actual_info = ilo_boot._parse_deploy_info(self.node) + self.assertEqual(expected_info, actual_info) + + +class IloVirtualMediaBootTestCase(db_base.DbTestCase): + + def setUp(self): + super(IloVirtualMediaBootTestCase, self).setUp() + mgr_utils.mock_the_extension_manager(driver="iscsi_ilo") + self.node = obj_utils.create_test_node( + self.context, driver='iscsi_ilo', driver_info=INFO_DICT) + + @mock.patch.object(deploy_utils, 'validate_image_properties', + spec_set=True, autospec=True) + @mock.patch.object(ilo_boot, '_parse_deploy_info', spec_set=True, + autospec=True) + def _test_validate(self, + deploy_info_mock, + validate_prop_mock, + props_expected): + d_info = {'image_source': 'uuid'} + deploy_info_mock.return_value = d_info + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.boot.validate(task) + deploy_info_mock.assert_called_once_with(task.node) + validate_prop_mock.assert_called_once_with( + task.context, d_info, props_expected) + + @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, + autospec=True) + def test_validate_glance_partition_image(self, is_glance_image_mock): + is_glance_image_mock.return_value = True + self._test_validate(props_expected=['kernel_id', 'ramdisk_id']) + + def test_validate_whole_disk_image(self): + self.node.driver_internal_info = {'is_whole_disk_image': True} + self.node.save() + self._test_validate(props_expected=[]) + + @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, + autospec=True) + def test_validate_non_glance_partition_image(self, is_glance_image_mock): + is_glance_image_mock.return_value = False + self._test_validate(props_expected=['kernel', 'ramdisk']) + + @mock.patch.object(ilo_common, 'eject_vmedia_devices', + spec_set=True, autospec=True) + @mock.patch.object(ilo_common, 'setup_vmedia', spec_set=True, + autospec=True) + @mock.patch.object(deploy_utils, 'get_single_nic_with_vif_port_id', + spec_set=True, autospec=True) + def _test_prepare_ramdisk(self, get_nic_mock, setup_vmedia_mock, + eject_mock, ilo_boot_iso, image_source, + ramdisk_params={'a': 'b'}): + instance_info = self.node.instance_info + instance_info['ilo_boot_iso'] = ilo_boot_iso + instance_info['image_source'] = image_source + self.node.instance_info = instance_info + self.node.save() + + get_nic_mock.return_value = '12:34:56:78:90:ab' + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + + task.node.driver_info['ilo_deploy_iso'] = 'deploy-iso' + + task.driver.boot.prepare_ramdisk(task, ramdisk_params) + + eject_mock.assert_called_once_with(task) + expected_ramdisk_opts = {'a': 'b', 'BOOTIF': '12:34:56:78:90:ab'} + get_nic_mock.assert_called_once_with(task) + setup_vmedia_mock.assert_called_once_with(task, 'deploy-iso', + expected_ramdisk_opts) + + def test_prepare_ramdisk_glance_image(self): + self._test_prepare_ramdisk( + ilo_boot_iso='swift:abcdef', + image_source='6b2f0c0c-79e8-4db6-842e-43c9764204af') + self.node.refresh() + self.assertNotIn('ilo_boot_iso', self.node.instance_info) + + def test_prepare_ramdisk_not_a_glance_image(self): + self._test_prepare_ramdisk( + ilo_boot_iso='http://mybootiso', + image_source='http://myimage') + self.node.refresh() + self.assertEqual('http://mybootiso', + self.node.instance_info['ilo_boot_iso']) + + @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'setup_vmedia_for_boot', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_get_boot_iso', spec_set=True, + autospec=True) + def test__configure_vmedia_boot_with_boot_iso( + self, get_boot_iso_mock, setup_vmedia_mock, set_boot_device_mock): + root_uuid = {'root uuid': 'root_uuid'} + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + get_boot_iso_mock.return_value = 'boot.iso' + + task.driver.boot._configure_vmedia_boot( + task, root_uuid) + + get_boot_iso_mock.assert_called_once_with( + task, root_uuid) + setup_vmedia_mock.assert_called_once_with( + task, 'boot.iso') + set_boot_device_mock.assert_called_once_with( + task, boot_devices.CDROM, persistent=True) + self.assertEqual('boot.iso', + task.node.instance_info['ilo_boot_iso']) + + @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'setup_vmedia_for_boot', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_get_boot_iso', spec_set=True, + autospec=True) + def test__configure_vmedia_boot_without_boot_iso( + self, get_boot_iso_mock, setup_vmedia_mock, set_boot_device_mock): + root_uuid = {'root uuid': 'root_uuid'} + + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + get_boot_iso_mock.return_value = None + + task.driver.boot._configure_vmedia_boot( + task, root_uuid) + + get_boot_iso_mock.assert_called_once_with( + task, root_uuid) + self.assertFalse(setup_vmedia_mock.called) + self.assertFalse(set_boot_device_mock.called) + + @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, + autospec=True) + @mock.patch.object(ilo_boot, '_clean_up_boot_iso_for_instance', + spec_set=True, autospec=True) + def test_clean_up_instance(self, cleanup_iso_mock, + cleanup_vmedia_mock): + CONF.ilo.use_web_server_for_images = False + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.boot.clean_up_instance(task) + cleanup_iso_mock.assert_called_once_with(task.node) + cleanup_vmedia_mock.assert_called_once_with(task) + + @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, + autospec=True) + def test_clean_up_ramdisk(self, cleanup_vmedia_mock): + CONF.ilo.use_web_server_for_images = False + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.boot.clean_up_ramdisk(task) + cleanup_vmedia_mock.assert_called_once_with(task) + + @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, + autospec=True) + def _test_prepare_instance_whole_disk_image( + self, cleanup_vmedia_boot_mock, set_boot_device_mock): + self.node.driver_internal_info = {'is_whole_disk_image': True} + self.node.save() + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.boot.prepare_instance(task) + + cleanup_vmedia_boot_mock.assert_called_once_with(task) + set_boot_device_mock.assert_called_once_with(task, + boot_devices.DISK, + persistent=True) + + def test_prepare_instance_whole_disk_image_local(self): + self.node.instance_info = {'capabilities': '{"boot_option": "local"}'} + self.node.save() + self._test_prepare_instance_whole_disk_image() + + def test_prepare_instance_whole_disk_image(self): + self._test_prepare_instance_whole_disk_image() + + @mock.patch.object(ilo_boot.IloVirtualMediaBoot, + '_configure_vmedia_boot', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, + autospec=True) + def test_prepare_instance_partition_image( + self, cleanup_vmedia_boot_mock, + configure_vmedia_mock): + self.node.driver_internal_info = {'root_uuid_or_disk_id': "some_uuid"} + self.node.save() + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.boot.prepare_instance(task) + + cleanup_vmedia_boot_mock.assert_called_once_with(task) + configure_vmedia_mock.assert_called_once_with(mock.ANY, task, + "some_uuid") diff --git a/ironic/tests/unit/drivers/modules/ilo/test_common.py b/ironic/tests/unit/drivers/modules/ilo/test_common.py index fcc0ed31eb..f0c803774f 100644 --- a/ironic/tests/unit/drivers/modules/ilo/test_common.py +++ b/ironic/tests/unit/drivers/modules/ilo/test_common.py @@ -24,11 +24,14 @@ from oslo_config import cfg from oslo_utils import importutils import six +from ironic.common import boot_devices from ironic.common import exception from ironic.common import images from ironic.common import swift from ironic.common import utils from ironic.conductor import task_manager +from ironic.conductor import utils as manager_utils +from ironic.drivers.modules import deploy_utils from ironic.drivers.modules.ilo import common as ilo_common from ironic.tests.unit.conductor import mgr_utils from ironic.tests.unit.db import base as db_base @@ -721,3 +724,46 @@ class IloCommonMethodsTestCase(db_base.DbTestCase): ilo_common.destroy_floppy_image_from_web_server(task.node) get_floppy_name_mock.assert_called_once_with(task.node) utils_mock.assert_called_once_with('/webserver/image-uuid') + + @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'setup_vmedia_for_boot', spec_set=True, + autospec=True) + def test_setup_vmedia(self, + func_setup_vmedia_for_boot, + func_set_boot_device): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + parameters = {'a': 'b'} + iso = '733d1c44-a2ea-414b-aca7-69decf20d810' + ilo_common.setup_vmedia(task, iso, parameters) + func_setup_vmedia_for_boot.assert_called_once_with(task, iso, + parameters) + func_set_boot_device.assert_called_once_with(task, + boot_devices.CDROM) + + @mock.patch.object(deploy_utils, 'is_secure_boot_requested', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'set_secure_boot_mode', spec_set=True, + autospec=True) + def test__update_secure_boot_mode_passed_true(self, + func_set_secure_boot_mode, + func_is_secure_boot_req): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + func_is_secure_boot_req.return_value = True + ilo_common.update_secure_boot_mode(task, True) + func_set_secure_boot_mode.assert_called_once_with(task, True) + + @mock.patch.object(deploy_utils, 'is_secure_boot_requested', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'set_secure_boot_mode', spec_set=True, + autospec=True) + def test__update_secure_boot_mode_passed_false(self, + func_set_secure_boot_mode, + func_is_secure_boot_req): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + func_is_secure_boot_req.return_value = False + ilo_common.update_secure_boot_mode(task, False) + self.assertFalse(func_set_secure_boot_mode.called) diff --git a/ironic/tests/unit/drivers/modules/ilo/test_deploy.py b/ironic/tests/unit/drivers/modules/ilo/test_deploy.py index 4480d44e62..59c91c632b 100644 --- a/ironic/tests/unit/drivers/modules/ilo/test_deploy.py +++ b/ironic/tests/unit/drivers/modules/ilo/test_deploy.py @@ -15,25 +15,18 @@ """Test class for common methods used by iLO modules.""" -import tempfile - import mock from oslo_config import cfg import six from ironic.common import boot_devices from ironic.common import exception -from ironic.common.glance_service import service_utils -from ironic.common import image_service -from ironic.common import images from ironic.common import states -from ironic.common import swift -from ironic.common import utils from ironic.conductor import task_manager from ironic.conductor import utils as manager_utils from ironic.drivers.modules import agent -from ironic.drivers.modules import agent_base_vendor 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.ilo import deploy as ilo_deploy from ironic.drivers.modules import iscsi_deploy @@ -60,334 +53,17 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase): self.node = obj_utils.create_test_node( self.context, driver='iscsi_ilo', driver_info=INFO_DICT) - def test__get_boot_iso_object_name(self): - boot_iso_actual = ilo_deploy._get_boot_iso_object_name(self.node) - boot_iso_expected = "boot-%s" % self.node.uuid - self.assertEqual(boot_iso_expected, boot_iso_actual) - - @mock.patch.object(image_service.HttpImageService, 'validate_href', + @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, autospec=True) - def test__get_boot_iso_http_url(self, service_mock): - url = 'http://abc.org/image/qcow2' - i_info = self.node.instance_info - i_info['ilo_boot_iso'] = url - self.node.instance_info = i_info - self.node.save() - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - boot_iso_actual = ilo_deploy._get_boot_iso(task, 'root-uuid') - service_mock.assert_called_once_with(mock.ANY, url) - self.assertEqual(url, boot_iso_actual) - - @mock.patch.object(image_service.HttpImageService, 'validate_href', - spec_set=True, autospec=True) - def test__get_boot_iso_url(self, mock_validate): - url = 'http://aaa/bbb' - i_info = self.node.instance_info - i_info['ilo_boot_iso'] = url - self.node.instance_info = i_info - self.node.save() - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - boot_iso_actual = ilo_deploy._get_boot_iso(task, 'root-uuid') - self.assertEqual(url, boot_iso_actual) - mock_validate.assert_called_once_with(mock.ANY, url) - - @mock.patch.object(image_service.HttpImageService, 'validate_href', - spec_set=True, autospec=True) - def test__get_boot_iso_unsupported_url(self, validate_href_mock): - validate_href_mock.side_effect = iter( - [exception.ImageRefValidationFailed( - image_href='file://img.qcow2', reason='fail')]) - url = 'file://img.qcow2' - i_info = self.node.instance_info - i_info['ilo_boot_iso'] = url - self.node.instance_info = i_info - self.node.save() - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - self.assertRaises(exception.ImageRefValidationFailed, - ilo_deploy._get_boot_iso, task, 'root-uuid') - - @mock.patch.object(images, 'get_image_properties', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_parse_deploy_info', spec_set=True, - autospec=True) - def test__get_boot_iso_glance_image(self, deploy_info_mock, - image_props_mock): - deploy_info_mock.return_value = {'image_source': 'image-uuid', - 'ilo_deploy_iso': 'deploy_iso_uuid'} - image_props_mock.return_value = {'boot_iso': 'boot-iso-uuid', - 'kernel_id': None, - 'ramdisk_id': None} - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - boot_iso_actual = ilo_deploy._get_boot_iso(task, 'root-uuid') - deploy_info_mock.assert_called_once_with(task.node) - image_props_mock.assert_called_once_with( - task.context, 'image-uuid', - ['boot_iso', 'kernel_id', 'ramdisk_id']) - boot_iso_expected = 'boot-iso-uuid' - self.assertEqual(boot_iso_expected, boot_iso_actual) - - @mock.patch.object(deploy_utils, 'get_boot_mode_for_deploy', spec_set=True, - autospec=True) - @mock.patch.object(images, 'get_image_properties', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_parse_deploy_info', spec_set=True, - autospec=True) - def test__get_boot_iso_uefi_no_glance_image(self, - deploy_info_mock, - image_props_mock, - boot_mode_mock): - deploy_info_mock.return_value = {'image_source': 'image-uuid', - 'ilo_deploy_iso': 'deploy_iso_uuid'} - image_props_mock.return_value = {'boot_iso': None, - 'kernel_id': None, - 'ramdisk_id': None} - properties = {'capabilities': 'boot_mode:uefi'} - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.node.properties = properties - boot_iso_result = ilo_deploy._get_boot_iso(task, 'root-uuid') - deploy_info_mock.assert_called_once_with(task.node) - image_props_mock.assert_called_once_with( - task.context, 'image-uuid', - ['boot_iso', 'kernel_id', 'ramdisk_id']) - self.assertFalse(boot_mode_mock.called) - self.assertIsNone(boot_iso_result) - - @mock.patch.object(tempfile, 'NamedTemporaryFile', spec_set=True, - autospec=True) - @mock.patch.object(images, 'create_boot_iso', spec_set=True, autospec=True) - @mock.patch.object(swift, 'SwiftAPI', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_get_boot_iso_object_name', spec_set=True, - autospec=True) - @mock.patch.object(driver_utils, 'get_node_capability', spec_set=True, - autospec=True) - @mock.patch.object(images, 'get_image_properties', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_parse_deploy_info', spec_set=True, - autospec=True) - def test__get_boot_iso_create(self, deploy_info_mock, image_props_mock, - capability_mock, boot_object_name_mock, - swift_api_mock, - create_boot_iso_mock, tempfile_mock): - CONF.ilo.swift_ilo_container = 'ilo-cont' - CONF.pxe.pxe_append_params = 'kernel-params' - - swift_obj_mock = swift_api_mock.return_value - fileobj_mock = mock.MagicMock(spec=file) - fileobj_mock.name = 'tmpfile' - mock_file_handle = mock.MagicMock(spec=file) - mock_file_handle.__enter__.return_value = fileobj_mock - tempfile_mock.return_value = mock_file_handle - - deploy_info_mock.return_value = {'image_source': 'image-uuid', - 'ilo_deploy_iso': 'deploy_iso_uuid'} - image_props_mock.return_value = {'boot_iso': None, - 'kernel_id': 'kernel_uuid', - 'ramdisk_id': 'ramdisk_uuid'} - boot_object_name_mock.return_value = 'abcdef' - create_boot_iso_mock.return_value = '/path/to/boot-iso' - capability_mock.return_value = 'uefi' - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - boot_iso_actual = ilo_deploy._get_boot_iso(task, 'root-uuid') - deploy_info_mock.assert_called_once_with(task.node) - image_props_mock.assert_called_once_with( - task.context, 'image-uuid', - ['boot_iso', 'kernel_id', 'ramdisk_id']) - boot_object_name_mock.assert_called_once_with(task.node) - create_boot_iso_mock.assert_called_once_with(task.context, - 'tmpfile', - 'kernel_uuid', - 'ramdisk_uuid', - 'deploy_iso_uuid', - 'root-uuid', - 'kernel-params', - 'uefi') - swift_obj_mock.create_object.assert_called_once_with('ilo-cont', - 'abcdef', - 'tmpfile') - boot_iso_expected = 'swift:abcdef' - self.assertEqual(boot_iso_expected, boot_iso_actual) - - @mock.patch.object(ilo_common, 'copy_image_to_web_server', spec_set=True, - autospec=True) - @mock.patch.object(tempfile, 'NamedTemporaryFile', spec_set=True, - autospec=True) - @mock.patch.object(images, 'create_boot_iso', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_get_boot_iso_object_name', spec_set=True, - autospec=True) - @mock.patch.object(driver_utils, 'get_node_capability', spec_set=True, - autospec=True) - @mock.patch.object(images, 'get_image_properties', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_parse_deploy_info', spec_set=True, - autospec=True) - def test__get_boot_iso_create_use_webserver_true_ramdisk_webserver( - self, deploy_info_mock, image_props_mock, - capability_mock, boot_object_name_mock, - create_boot_iso_mock, tempfile_mock, - copy_file_mock): - CONF.ilo.swift_ilo_container = 'ilo-cont' - CONF.ilo.use_web_server_for_images = True - CONF.deploy.http_url = "http://10.10.1.30/httpboot" - CONF.deploy.http_root = "/httpboot" - CONF.pxe.pxe_append_params = 'kernel-params' - - fileobj_mock = mock.MagicMock(spec=file) - fileobj_mock.name = 'tmpfile' - mock_file_handle = mock.MagicMock(spec=file) - mock_file_handle.__enter__.return_value = fileobj_mock - tempfile_mock.return_value = mock_file_handle - - ramdisk_href = "http://10.10.1.30/httpboot/ramdisk" - kernel_href = "http://10.10.1.30/httpboot/kernel" - deploy_info_mock.return_value = {'image_source': 'image-uuid', - 'ilo_deploy_iso': 'deploy_iso_uuid'} - image_props_mock.return_value = {'boot_iso': None, - 'kernel_id': kernel_href, - 'ramdisk_id': ramdisk_href} - boot_object_name_mock.return_value = 'abcdef' - create_boot_iso_mock.return_value = '/path/to/boot-iso' - capability_mock.return_value = 'uefi' - copy_file_mock.return_value = "http://10.10.1.30/httpboot/abcdef" - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - boot_iso_actual = ilo_deploy._get_boot_iso(task, 'root-uuid') - deploy_info_mock.assert_called_once_with(task.node) - image_props_mock.assert_called_once_with( - task.context, 'image-uuid', - ['boot_iso', 'kernel_id', 'ramdisk_id']) - boot_object_name_mock.assert_called_once_with(task.node) - create_boot_iso_mock.assert_called_once_with(task.context, - 'tmpfile', - kernel_href, - ramdisk_href, - 'deploy_iso_uuid', - 'root-uuid', - 'kernel-params', - 'uefi') - boot_iso_expected = 'http://10.10.1.30/httpboot/abcdef' - self.assertEqual(boot_iso_expected, boot_iso_actual) - copy_file_mock.assert_called_once_with(fileobj_mock.name, - 'abcdef') - - @mock.patch.object(ilo_deploy, '_get_boot_iso_object_name', spec_set=True, - autospec=True) - @mock.patch.object(swift, 'SwiftAPI', spec_set=True, autospec=True) - def test__clean_up_boot_iso_for_instance(self, swift_mock, - boot_object_name_mock): - swift_obj_mock = swift_mock.return_value - CONF.ilo.swift_ilo_container = 'ilo-cont' - boot_object_name_mock.return_value = 'boot-object' - i_info = self.node.instance_info - i_info['ilo_boot_iso'] = 'swift:bootiso' - self.node.instance_info = i_info - self.node.save() - ilo_deploy._clean_up_boot_iso_for_instance(self.node) - swift_obj_mock.delete_object.assert_called_once_with('ilo-cont', - 'boot-object') - - @mock.patch.object(ilo_deploy.LOG, 'exception', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_get_boot_iso_object_name', spec_set=True, - autospec=True) - @mock.patch.object(swift, 'SwiftAPI', spec_set=True, autospec=True) - def test__clean_up_boot_iso_for_instance_exc(self, swift_mock, - boot_object_name_mock, - log_mock): - swift_obj_mock = swift_mock.return_value - exc = exception.SwiftObjectNotFoundError('error') - swift_obj_mock.delete_object.side_effect = exc - CONF.ilo.swift_ilo_container = 'ilo-cont' - boot_object_name_mock.return_value = 'boot-object' - i_info = self.node.instance_info - i_info['ilo_boot_iso'] = 'swift:bootiso' - self.node.instance_info = i_info - self.node.save() - ilo_deploy._clean_up_boot_iso_for_instance(self.node) - swift_obj_mock.delete_object.assert_called_once_with('ilo-cont', - 'boot-object') - self.assertTrue(log_mock.called) - - @mock.patch.object(utils, 'unlink_without_raise', spec_set=True, - autospec=True) - def test__clean_up_boot_iso_for_instance_on_webserver(self, unlink_mock): - - CONF.ilo.use_web_server_for_images = True - CONF.deploy.http_root = "/webserver" - i_info = self.node.instance_info - i_info['ilo_boot_iso'] = 'http://x.y.z.a/webserver/boot-object' - self.node.instance_info = i_info - self.node.save() - boot_iso_path = "/webserver/boot-object" - ilo_deploy._clean_up_boot_iso_for_instance(self.node) - unlink_mock.assert_called_once_with(boot_iso_path) - - @mock.patch.object(ilo_deploy, '_get_boot_iso_object_name', spec_set=True, - autospec=True) - def test__clean_up_boot_iso_for_instance_no_boot_iso( - self, boot_object_name_mock): - ilo_deploy._clean_up_boot_iso_for_instance(self.node) - self.assertFalse(boot_object_name_mock.called) - - @mock.patch.object(deploy_utils, 'check_for_missing_params', spec_set=True, - autospec=True) - def test__parse_driver_info(self, check_params_mock): - self.node.driver_info['ilo_deploy_iso'] = 'deploy-iso-uuid' - driver_info_expected = {'ilo_deploy_iso': 'deploy-iso-uuid'} - driver_info_actual = ilo_deploy._parse_driver_info(self.node) - error_msg = ("Error validating iLO virtual media deploy. Some" - " parameters were missing in node's driver_info") - check_params_mock.assert_called_once_with(driver_info_expected, - error_msg) - self.assertEqual(driver_info_expected, driver_info_actual) - - @mock.patch.object(ilo_deploy, '_parse_driver_info', spec_set=True, - autospec=True) - @mock.patch.object(iscsi_deploy, 'parse_instance_info', spec_set=True, - autospec=True) - def test__parse_deploy_info(self, instance_info_mock, driver_info_mock): - instance_info_mock.return_value = {'a': 'b'} - driver_info_mock.return_value = {'c': 'd'} - expected_info = {'a': 'b', 'c': 'd'} - actual_info = ilo_deploy._parse_deploy_info(self.node) - self.assertEqual(expected_info, actual_info) - - @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, - autospec=True) - @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'setup_vmedia_for_boot', spec_set=True, - autospec=True) - def test__reboot_into(self, setup_vmedia_mock, set_boot_device_mock, - node_power_action_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - opts = {'a': 'b'} - ilo_deploy._reboot_into(task, 'iso', opts) - setup_vmedia_mock.assert_called_once_with(task, 'iso', opts) - set_boot_device_mock.assert_called_once_with(task, - boot_devices.CDROM) - node_power_action_mock.assert_called_once_with(task, states.REBOOT) - @mock.patch.object(ilo_common, 'eject_vmedia_devices', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_reboot_into', spec_set=True, + @mock.patch.object(ilo_common, 'setup_vmedia', spec_set=True, autospec=True) @mock.patch.object(deploy_utils, 'build_agent_options', spec_set=True, autospec=True) def test__prepare_agent_vmedia_boot(self, build_options_mock, - reboot_into_mock, eject_mock): + setup_media_mock, eject_mock, + power_action_mock): deploy_opts = {'a': 'b'} build_options_mock.return_value = deploy_opts with task_manager.acquire(self.context, self.node.uuid, @@ -398,35 +74,10 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase): eject_mock.assert_called_once_with(task) build_options_mock.assert_called_once_with(task.node) - reboot_into_mock.assert_called_once_with(task, + setup_media_mock.assert_called_once_with(task, 'deploy-iso-uuid', deploy_opts) - - @mock.patch.object(deploy_utils, 'is_secure_boot_requested', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'set_secure_boot_mode', spec_set=True, - autospec=True) - def test__update_secure_boot_mode_passed_true(self, - func_set_secure_boot_mode, - func_is_secure_boot_req): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - func_is_secure_boot_req.return_value = True - ilo_deploy._update_secure_boot_mode(task, True) - func_set_secure_boot_mode.assert_called_once_with(task, True) - - @mock.patch.object(deploy_utils, 'is_secure_boot_requested', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'set_secure_boot_mode', spec_set=True, - autospec=True) - def test__update_secure_boot_mode_passed_False(self, - func_set_secure_boot_mode, - func_is_secure_boot_req): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - func_is_secure_boot_req.return_value = False - ilo_deploy._update_secure_boot_mode(task, False) - self.assertFalse(func_set_secure_boot_mode.called) + power_action_mock.assert_called_once_with(task, states.REBOOT) @mock.patch.object(ilo_common, 'set_secure_boot_mode', spec_set=True, autospec=True) @@ -568,89 +219,6 @@ class IloDeployPrivateMethodsTestCase(db_base.DbTestCase): deploy_boot_mode = task.node.instance_info.get('deploy_boot_mode') self.assertIsNone(deploy_boot_mode) - @mock.patch.object(ilo_deploy.LOG, 'warning', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_get_boot_iso', spec_set=True, - autospec=True) - def test__recreate_and_populate_boot_iso_root_uuid_set(self, - get_boot_iso_mock, - log_mock): - driver_internal_info = {} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - driver_internal_info['root_uuid_or_disk_id'] = 'root-uuid' - task.node.driver_internal_info = driver_internal_info - r_uuid = task.node.driver_internal_info['root_uuid_or_disk_id'] - get_boot_iso_mock.return_value = 'boot-uuid' - ilo_deploy._recreate_and_populate_ilo_boot_iso(task) - self.assertEqual(task.node.instance_info['ilo_boot_iso'], - 'boot-uuid') - get_boot_iso_mock.assert_called_once_with(task, r_uuid) - self.assertFalse(log_mock.called) - - @mock.patch.object(ilo_deploy.LOG, 'warning', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_get_boot_iso', spec_set=True, - autospec=True) - def test__recreate_and_populate_boot_iso_root_not_set(self, - get_boot_iso_mock, - log_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.node.instance_info['ilo_boot_iso'] = 'boot-uuid-old-iso' - ilo_deploy._recreate_and_populate_ilo_boot_iso(task) - self.assertEqual(task.node.instance_info['ilo_boot_iso'], - 'boot-uuid-old-iso') - self.assertFalse(get_boot_iso_mock.called) - self.assertTrue(log_mock.called) - - @mock.patch.object(ilo_deploy.LOG, 'warning', - spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_get_boot_iso', - spec_set=True, autospec=True) - def test__recreate_and_populate_get_boot_iso_fails(self, - get_boot_iso_mock, - log_mock): - driver_internal_info = {} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - - driver_internal_info['boot_iso_created_in_web_server'] = True - driver_internal_info['root_uuid_or_disk_id'] = 'uuid' - task.node.instance_info['ilo_boot_iso'] = 'boot-uuid-old-iso' - task.node.driver_internal_info = driver_internal_info - task.node.save() - r_uuid = task.node.driver_internal_info.get('root_uuid_or_disk_id') - get_boot_iso_mock.side_effect = Exception - ilo_deploy._recreate_and_populate_ilo_boot_iso(task) - self.assertEqual(task.node.instance_info['ilo_boot_iso'], - 'boot-uuid-old-iso') - get_boot_iso_mock.assert_called_once_with(task, r_uuid) - self.assertTrue(log_mock.called) - - @mock.patch.object(ilo_deploy.LOG, 'warning', - spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_get_boot_iso', - spec_set=True, autospec=True) - def test__recreate_and_populate_get_boot_iso_none(self, - boot_iso_mock, - log_mock): - driver_internal_info = {} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - driver_internal_info['boot_iso_created_in_web_server'] = True - driver_internal_info['root_uuid_or_disk_id'] = 'uuid' - task.node.driver_internal_info = driver_internal_info - r_uuid = task.node.driver_internal_info.get('root_uuid_or_disk_id') - task.node.instance_info['ilo_boot_iso'] = 'boot-uuid-old-iso' - task.node.save() - boot_iso_mock.return_value = None - ilo_deploy._recreate_and_populate_ilo_boot_iso(task) - boot_iso_mock.assert_called_once_with(task, r_uuid) - self.assertEqual(task.node.instance_info['ilo_boot_iso'], - 'boot-uuid-old-iso') - self.assertTrue(log_mock.called) - class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase): @@ -660,287 +228,87 @@ class IloVirtualMediaIscsiDeployTestCase(db_base.DbTestCase): self.node = obj_utils.create_test_node( self.context, driver='iscsi_ilo', driver_info=INFO_DICT) - @mock.patch.object(deploy_utils, 'validate_capabilities', - spec_set=True, autospec=True) - @mock.patch.object(deploy_utils, 'validate_image_properties', - spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_parse_deploy_info', spec_set=True, + @mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True, autospec=True) - @mock.patch.object(iscsi_deploy, 'validate', spec_set=True, autospec=True) - def _test_validate(self, validate_mock, - deploy_info_mock, - validate_prop_mock, - validate_capability_mock, - props_expected): - d_info = {'image_source': 'uuid'} - deploy_info_mock.return_value = d_info - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.deploy.validate(task) - validate_mock.assert_called_once_with(task) - deploy_info_mock.assert_called_once_with(task.node) - validate_prop_mock.assert_called_once_with( - task.context, d_info, props_expected) - validate_capability_mock.assert_called_once_with(task.node) - - @mock.patch.object(deploy_utils, 'validate_image_properties', - spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_parse_deploy_info', spec_set=True, - autospec=True) - @mock.patch.object(iscsi_deploy, 'validate', spec_set=True, autospec=True) - def test_validate_invalid_boot_option(self, - validate_mock, - deploy_info_mock, - validate_prop_mock): - d_info = {'image_source': '733d1c44-a2ea-414b-aca7-69decf20d810'} - properties = {'capabilities': 'boot_mode:uefi,boot_option:foo'} - deploy_info_mock.return_value = d_info - props = ['kernel_id', 'ramdisk_id'] - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.node.properties = properties - exc = self.assertRaises(exception.InvalidParameterValue, - task.driver.deploy.validate, - task) - validate_mock.assert_called_once_with(task) - deploy_info_mock.assert_called_once_with(task.node) - validate_prop_mock.assert_called_once_with(task.context, - d_info, props) - self.assertIn('boot_option', str(exc)) - - @mock.patch.object(deploy_utils, 'validate_image_properties', - spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_parse_deploy_info', spec_set=True, - autospec=True) - @mock.patch.object(iscsi_deploy, 'validate', spec_set=True, autospec=True) - def test_validate_invalid_boot_mode(self, - validate_mock, - deploy_info_mock, - validate_prop_mock): - d_info = {'image_source': '733d1c44-a2ea-414b-aca7-69decf20d810'} - properties = {'capabilities': 'boot_mode:foo,boot_option:local'} - deploy_info_mock.return_value = d_info - props = ['kernel_id', 'ramdisk_id'] - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.node.properties = properties - exc = self.assertRaises(exception.InvalidParameterValue, - task.driver.deploy.validate, - task) - validate_mock.assert_called_once_with(task) - deploy_info_mock.assert_called_once_with(task.node) - validate_prop_mock.assert_called_once_with(task.context, - d_info, props) - self.assertIn('boot_mode', str(exc)) - - @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, - autospec=True) - def test_validate_glance_partition_image(self, is_glance_image_mock): - is_glance_image_mock.return_value = True - self._test_validate(props_expected=['kernel_id', 'ramdisk_id']) - - def test_validate_whole_disk_image(self): - self.node.driver_internal_info = {'is_whole_disk_image': True} - self.node.save() - self._test_validate(props_expected=[]) - - @mock.patch.object(service_utils, 'is_glance_image', spec_set=True, - autospec=True) - def test_validate_non_glance_partition_image(self, is_glance_image_mock): - is_glance_image_mock.return_value = False - self._test_validate(props_expected=['kernel', 'ramdisk']) - - @mock.patch.object(ilo_common, 'eject_vmedia_devices', - spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_reboot_into', spec_set=True, - autospec=True) - @mock.patch.object(deploy_utils, 'get_single_nic_with_vif_port_id', - spec_set=True, autospec=True) - @mock.patch.object(deploy_utils, 'build_agent_options', spec_set=True, - autospec=True) - @mock.patch.object(iscsi_deploy, 'build_deploy_ramdisk_options', - spec_set=True, autospec=True) - @mock.patch.object(iscsi_deploy, 'check_image_size', spec_set=True, - autospec=True) - @mock.patch.object(iscsi_deploy, 'cache_instance_image', spec_set=True, - autospec=True) - def _test_deploy(self, - cache_instance_image_mock, - check_image_size_mock, - build_opts_mock, - agent_options_mock, - get_nic_mock, - reboot_into_mock, - eject_mock, - ilo_boot_iso, - image_source - ): - instance_info = self.node.instance_info - instance_info['ilo_boot_iso'] = ilo_boot_iso - instance_info['image_source'] = image_source - self.node.instance_info = instance_info - self.node.save() - - deploy_opts = {'a': 'b'} - agent_options_mock.return_value = { - 'ipa-api-url': 'http://1.2.3.4:6385'} - build_opts_mock.return_value = deploy_opts - get_nic_mock.return_value = '12:34:56:78:90:ab' - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - - task.node.driver_info['ilo_deploy_iso'] = 'deploy-iso' - returned_state = task.driver.deploy.deploy(task) - - eject_mock.assert_called_once_with(task) - cache_instance_image_mock.assert_called_once_with(task.context, - task.node) - check_image_size_mock.assert_called_once_with(task) - expected_ramdisk_opts = {'a': 'b', 'BOOTIF': '12:34:56:78:90:ab', - 'ipa-api-url': 'http://1.2.3.4:6385'} - build_opts_mock.assert_called_once_with(task.node) - get_nic_mock.assert_called_once_with(task) - reboot_into_mock.assert_called_once_with(task, 'deploy-iso', - expected_ramdisk_opts) - - self.assertEqual(states.DEPLOYWAIT, returned_state) - - def test_deploy_glance_image(self): - self._test_deploy( - ilo_boot_iso='swift:abcdef', - image_source='6b2f0c0c-79e8-4db6-842e-43c9764204af') - self.node.refresh() - self.assertNotIn('ilo_boot_iso', self.node.instance_info) - - def test_deploy_not_a_glance_image(self): - self._test_deploy( - ilo_boot_iso='http://mybootiso', - image_source='http://myimage') - self.node.refresh() - self.assertEqual('http://mybootiso', - self.node.instance_info['ilo_boot_iso']) - - @mock.patch.object(ilo_deploy, '_update_secure_boot_mode', spec_set=True, + @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'tear_down', spec_set=True, autospec=True) @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, autospec=True) def test_tear_down(self, node_power_action_mock, + iscsi_tear_down_mock, update_secure_boot_mode_mock): with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: - driver_internal_info = task.node.driver_internal_info - driver_internal_info['boot_iso_created_in_web_server'] = True - driver_internal_info['root_uuid_or_disk_id'] = 'uuid' - task.node.driver_internal_info = driver_internal_info - task.node.save() + iscsi_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) + iscsi_tear_down_mock.assert_called_once_with(mock.ANY, task) self.assertEqual(states.DELETED, returned_state) - dinfo = task.node.driver_internal_info - self.assertNotIn('boot_iso_created_in_web_server', dinfo) - self.assertNotIn('root_uuid_or_disk_id', dinfo) @mock.patch.object(ilo_deploy.LOG, 'warning', spec_set=True, autospec=True) @mock.patch.object(ilo_deploy, 'exception', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_update_secure_boot_mode', spec_set=True, + @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'tear_down', 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, + iscsi_tear_down_mock, exception_mock, mock_log): with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: + iscsi_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) node_power_action_mock.assert_called_once_with(task, states.POWER_OFF) update_secure_boot_mode_mock.assert_called_once_with(task, False) + iscsi_tear_down_mock.assert_called_once_with(mock.ANY, task) self.assertTrue(mock_log.called) self.assertEqual(states.DELETED, returned_state) - @mock.patch.object(ilo_deploy, '_clean_up_boot_iso_for_instance', + @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'deploy', spec_set=True, autospec=True) - @mock.patch.object(iscsi_deploy, 'destroy_images', spec_set=True, - autospec=True) - def test_clean_up(self, destroy_images_mock, clean_up_boot_mock): + def test_deploy(self, iscsi_deploy_mock): with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: - task.driver.deploy.clean_up(task) - destroy_images_mock.assert_called_once_with(task.node.uuid) - clean_up_boot_mock.assert_called_once_with(task.node) + task.driver.deploy.deploy(task) + iscsi_deploy_mock.assert_called_once_with(mock.ANY, task) - @mock.patch.object(ilo_deploy, '_clean_up_boot_iso_for_instance', + @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'prepare', spec_set=True, autospec=True) - @mock.patch.object(ilo_common, 'destroy_floppy_image_from_web_server', - spec_set=True, autospec=True) - def test_clean_up_of_webserver_images(self, destroy_images_mock, - clean_up_boot_mock): - CONF.ilo.use_web_server_for_images = True - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.deploy.clean_up(task) - destroy_images_mock.assert_called_once_with(task.node) - clean_up_boot_mock.assert_called_once_with(task.node) - @mock.patch.object(ilo_deploy, '_prepare_node_for_deploy', spec_set=True, autospec=True) - def test_prepare(self, func_prepare_node_for_deploy): + def test_prepare(self, func_prepare_node_for_deploy, + iscsi_deploy_prepare_mock): with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: task.driver.deploy.prepare(task) func_prepare_node_for_deploy.assert_called_once_with(task) + iscsi_deploy_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_deploy, '_prepare_node_for_deploy', spec_set=True, autospec=True) - def test_prepare_active_node(self, func_prepare_node_for_deploy): + def test_prepare_active_node(self, func_prepare_node_for_deploy, + iscsi_deploy_prepare_mock): self.node.provision_state = states.ACTIVE self.node.save() with task_manager.acquire(self.context, self.node.uuid, shared=False) as task: task.driver.deploy.prepare(task) self.assertFalse(func_prepare_node_for_deploy.called) - - @mock.patch.object(ilo_deploy, '_recreate_and_populate_ilo_boot_iso', - spec_set=True, autospec=True) - def test_take_over_recreate_iso_config_and_dif_set(self, mock_recreate): - driver_internal_info = {} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - CONF.ilo.use_web_server_for_images = True - driver_internal_info['boot_iso_created_in_web_server'] = True - task.node.driver_internal_info = driver_internal_info - task.node.save() - task.driver.deploy.take_over(task) - mock_recreate.assert_called_once_with(task) - - @mock.patch.object(ilo_deploy, '_recreate_and_populate_ilo_boot_iso', - spec_set=True, autospec=True) - def test_take_over_recreate_iso_config_set_and_dif_not_set(self, - mock_recreate): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - CONF.ilo.use_web_server_for_images = True - task.node.save() - task.driver.deploy.take_over(task) - self.assertFalse(mock_recreate.called) - - @mock.patch.object(ilo_deploy, '_recreate_and_populate_ilo_boot_iso', - spec_set=True, autospec=True) - def test_take_over_recreate_iso_config_not_set(self, mock_recreate): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - CONF.ilo.use_web_server_for_images = False - task.node.save() - task.driver.deploy.take_over(task) - self.assertFalse(mock_recreate.called) + iscsi_deploy_prepare_mock.assert_called_once_with(mock.ANY, task) class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase): @@ -953,7 +321,7 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase): @mock.patch.object(deploy_utils, 'validate_capabilities', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_parse_driver_info', spec_set=True, + @mock.patch.object(ilo_boot, 'parse_driver_info', spec_set=True, autospec=True) def test_validate(self, parse_driver_info_mock, @@ -961,8 +329,8 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase): 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_capability_mock.assert_called_once_with(task.node) + parse_driver_info_mock.assert_called_once_with(task.node) @mock.patch.object(ilo_deploy, '_prepare_agent_vmedia_boot', spec_set=True, autospec=True) @@ -973,7 +341,7 @@ 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', spec_set=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) @@ -991,7 +359,7 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase): @mock.patch.object(ilo_deploy.LOG, 'warning', spec_set=True, autospec=True) @mock.patch.object(ilo_deploy, 'exception', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_update_secure_boot_mode', spec_set=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) @@ -1128,520 +496,6 @@ class IloVirtualMediaAgentDeployTestCase(db_base.DbTestCase): override_priorities={'erase_devices': None}) -class VendorPassthruTestCase(db_base.DbTestCase): - - def setUp(self): - super(VendorPassthruTestCase, self).setUp() - mgr_utils.mock_the_extension_manager(driver="iscsi_ilo") - self.node = obj_utils.create_test_node(self.context, - driver='iscsi_ilo', - driver_info=INFO_DICT) - - @mock.patch.object(iscsi_deploy, 'get_deploy_info', spec_set=True, - autospec=True) - def test_validate_pass_deploy_info(self, get_deploy_info_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - vendor = ilo_deploy.VendorPassthru() - vendor.validate(task, method='pass_deploy_info', foo='bar') - get_deploy_info_mock.assert_called_once_with(task.node, - foo='bar') - - @mock.patch.object(iscsi_deploy, 'validate_pass_bootloader_info_input', - spec_set=True, autospec=True) - def test_validate_pass_bootloader_install_info(self, - validate_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - kwargs = {'address': '1.2.3.4', 'key': 'fake-key', - 'status': 'SUCCEEDED', 'error': ''} - task.driver.vendor.validate( - task, method='pass_bootloader_install_info', **kwargs) - validate_mock.assert_called_once_with(task, kwargs) - - @mock.patch.object(iscsi_deploy, 'get_deploy_info', spec_set=True, - autospec=True) - def test_validate_heartbeat(self, get_deploy_info_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - vendor = ilo_deploy.VendorPassthru() - vendor.validate(task, method='heartbeat', foo='bar') - self.assertFalse(get_deploy_info_mock.called) - - @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'setup_vmedia_for_boot', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_get_boot_iso', spec_set=True, - autospec=True) - def test__configure_vmedia_boot_with_boot_iso( - self, get_boot_iso_mock, setup_vmedia_mock, set_boot_device_mock): - root_uuid = {'root uuid': 'root_uuid'} - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - get_boot_iso_mock.return_value = 'boot.iso' - - task.driver.vendor._configure_vmedia_boot( - task, root_uuid) - - get_boot_iso_mock.assert_called_once_with( - task, root_uuid) - setup_vmedia_mock.assert_called_once_with( - task, 'boot.iso') - set_boot_device_mock.assert_called_once_with( - task, boot_devices.CDROM, persistent=True) - self.assertEqual('boot.iso', - task.node.instance_info['ilo_boot_iso']) - - @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'setup_vmedia_for_boot', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_get_boot_iso', spec_set=True, - autospec=True) - def test__configure_vmedia_boot_without_boot_iso( - self, get_boot_iso_mock, setup_vmedia_mock, set_boot_device_mock): - root_uuid = {'root uuid': 'root_uuid'} - - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - get_boot_iso_mock.return_value = None - - task.driver.vendor._configure_vmedia_boot( - task, root_uuid) - - get_boot_iso_mock.assert_called_once_with( - task, root_uuid) - self.assertFalse(setup_vmedia_mock.called) - self.assertFalse(set_boot_device_mock.called) - - @mock.patch.object(iscsi_deploy, 'validate_bootloader_install_status', - spec_set=True, autospec=True) - @mock.patch.object(iscsi_deploy, 'finish_deploy', spec_set=True, - autospec=True) - def test_pass_bootloader_install_info(self, finish_deploy_mock, - validate_input_mock): - kwargs = {'method': 'pass_deploy_info', 'address': '123456'} - self.node.provision_state = states.DEPLOYWAIT - 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.vendor.pass_bootloader_install_info(task, **kwargs) - finish_deploy_mock.assert_called_once_with(task, '123456') - validate_input_mock.assert_called_once_with(task, kwargs) - - @mock.patch.object(deploy_utils, 'notify_ramdisk_to_proceed', - spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_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(manager_utils, 'node_set_boot_device', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'setup_vmedia_for_boot', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_get_boot_iso', spec_set=True, - autospec=True) - @mock.patch.object(iscsi_deploy, 'continue_deploy', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, - autospec=True) - def test_pass_deploy_info_good(self, cleanup_vmedia_boot_mock, - continue_deploy_mock, get_boot_iso_mock, - setup_vmedia_mock, set_boot_device_mock, - func_update_boot_mode, - func_update_secure_boot_mode, - notify_ramdisk_to_proceed_mock): - kwargs = {'method': 'pass_deploy_info', 'address': '123456'} - continue_deploy_mock.return_value = {'root uuid': 'root-uuid'} - get_boot_iso_mock.return_value = 'boot-iso' - - self.node.provision_state = states.DEPLOYWAIT - 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.vendor.pass_deploy_info(task, **kwargs) - - cleanup_vmedia_boot_mock.assert_called_once_with(task) - continue_deploy_mock.assert_called_once_with(task, **kwargs) - get_boot_iso_mock.assert_called_once_with(task, 'root-uuid') - setup_vmedia_mock.assert_called_once_with(task, 'boot-iso') - self.assertEqual(states.ACTIVE, task.node.provision_state) - self.assertEqual(states.NOSTATE, task.node.target_provision_state) - set_boot_device_mock.assert_called_once_with(task, - boot_devices.CDROM, - persistent=True) - func_update_boot_mode.assert_called_once_with(task) - func_update_secure_boot_mode.assert_called_once_with(task, True) - - self.assertEqual('boot-iso', - task.node.instance_info['ilo_boot_iso']) - info = task.node.driver_internal_info['root_uuid_or_disk_id'] - self.assertEqual('root-uuid', info) - notify_ramdisk_to_proceed_mock.assert_called_once_with('123456') - - @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, - autospec=True) - def test_pass_deploy_info_bad(self, cleanup_vmedia_boot_mock): - kwargs = {'method': 'pass_deploy_info', 'address': '123456'} - - self.node.provision_state = states.AVAILABLE - self.node.target_provision_state = states.NOSTATE - self.node.save() - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - vendor = task.driver.vendor - self.assertRaises(exception.InvalidState, - vendor.pass_deploy_info, - task, **kwargs) - self.assertEqual(states.AVAILABLE, task.node.provision_state) - self.assertEqual(states.NOSTATE, task.node.target_provision_state) - self.assertFalse(cleanup_vmedia_boot_mock.called) - - @mock.patch.object(ilo_deploy, '_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(manager_utils, 'node_power_action', spec_set=True, - autospec=True) - @mock.patch.object(iscsi_deploy, 'continue_deploy', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_get_boot_iso', spec_set=True, - autospec=True) - def test_pass_deploy_info_create_boot_iso_fail( - self, get_iso_mock, cleanup_vmedia_boot_mock, continue_deploy_mock, - node_power_mock, update_boot_mode_mock, - update_secure_boot_mode_mock): - kwargs = {'address': '123456'} - continue_deploy_mock.return_value = {'root uuid': 'root-uuid'} - get_iso_mock.side_effect = iter([exception.ImageCreationFailed( - image_type='iso', error="error")]) - self.node.provision_state = states.DEPLOYWAIT - 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.vendor.pass_deploy_info(task, **kwargs) - - cleanup_vmedia_boot_mock.assert_called_once_with(task) - update_boot_mode_mock.assert_called_once_with(task) - update_secure_boot_mode_mock.assert_called_once_with(task, True) - continue_deploy_mock.assert_called_once_with(task, **kwargs) - get_iso_mock.assert_called_once_with(task, 'root-uuid') - node_power_mock.assert_called_once_with(task, states.POWER_OFF) - self.assertEqual(states.DEPLOYFAIL, task.node.provision_state) - self.assertEqual(states.ACTIVE, task.node.target_provision_state) - self.assertIsNotNone(task.node.last_error) - - @mock.patch.object(iscsi_deploy, 'finish_deploy', spec_set=True, - autospec=True) - @mock.patch.object(deploy_utils, 'notify_ramdisk_to_proceed', - spec_set=True, autospec=True) - @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_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(iscsi_deploy, 'continue_deploy', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, - autospec=True) - def test_pass_deploy_info_boot_option_local( - self, cleanup_vmedia_boot_mock, continue_deploy_mock, - func_update_boot_mode, func_update_secure_boot_mode, - set_boot_device_mock, notify_ramdisk_to_proceed_mock, - finish_deploy_mock): - kwargs = {'method': 'pass_deploy_info', 'address': '123456'} - continue_deploy_mock.return_value = {'root uuid': ''} - - self.node.instance_info = {'capabilities': '{"boot_option": "local"}'} - self.node.provision_state = states.DEPLOYWAIT - self.node.target_provision_state = states.ACTIVE - self.node.save() - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - vendor = task.driver.vendor - vendor.pass_deploy_info(task, **kwargs) - - cleanup_vmedia_boot_mock.assert_called_once_with(task) - continue_deploy_mock.assert_called_once_with(task, **kwargs) - set_boot_device_mock.assert_called_once_with(task, - boot_devices.DISK, - persistent=True) - func_update_boot_mode.assert_called_once_with(task) - func_update_secure_boot_mode.assert_called_once_with(task, True) - notify_ramdisk_to_proceed_mock.assert_called_once_with('123456') - self.assertEqual(states.DEPLOYWAIT, task.node.provision_state) - self.assertEqual(states.ACTIVE, task.node.target_provision_state) - self.assertFalse(finish_deploy_mock.called) - - @mock.patch.object(iscsi_deploy, 'finish_deploy', spec_set=True, - autospec=True) - @mock.patch.object(manager_utils, 'node_set_boot_device', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_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(iscsi_deploy, 'continue_deploy', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, - autospec=True) - def _test_pass_deploy_info_whole_disk_image( - self, cleanup_vmedia_boot_mock, continue_deploy_mock, - func_update_boot_mode, func_update_secure_boot_mode, - set_boot_device_mock, notify_ramdisk_to_proceed_mock): - kwargs = {'method': 'pass_deploy_info', 'address': '123456'} - continue_deploy_mock.return_value = {'root uuid': ''} - - self.node.driver_internal_info = {'is_whole_disk_image': True} - self.node.provision_state = states.DEPLOYWAIT - self.node.target_provision_state = states.ACTIVE - self.node.save() - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - vendor = task.driver.vendor - vendor.pass_deploy_info(task, **kwargs) - - cleanup_vmedia_boot_mock.assert_called_once_with(task) - continue_deploy_mock.assert_called_once_with(task, **kwargs) - set_boot_device_mock.assert_called_once_with(task, - boot_devices.DISK, - persistent=True) - func_update_boot_mode.assert_called_once_with(task) - func_update_secure_boot_mode.assert_called_once_with(task, True) - iscsi_deploy.finish_deploy.assert_called_once_with(task, '123456') - - def test_pass_deploy_info_whole_disk_image_local(self): - self.node.instance_info = {'capabilities': '{"boot_option": "local"}'} - self.node.save() - self._test_pass_deploy_info_whole_disk_image() - - def test_pass_deploy_info_whole_disk_image(self): - self._test_pass_deploy_info_whole_disk_image() - - @mock.patch.object(ilo_deploy, '_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(agent_base_vendor.BaseAgentVendor, - 'reboot_and_finish_deploy', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy.VendorPassthru, '_configure_vmedia_boot', - spec_set=True, autospec=True) - @mock.patch.object(iscsi_deploy, 'do_agent_iscsi_deploy', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, - autospec=True) - def test_continue_deploy_netboot(self, cleanup_vmedia_boot_mock, - do_agent_iscsi_deploy_mock, - configure_vmedia_boot_mock, - reboot_and_finish_deploy_mock, - boot_mode_cap_mock, - update_secure_boot_mock): - self.node.provision_state = states.DEPLOYWAIT - self.node.target_provision_state = states.DEPLOYING - self.node.save() - do_agent_iscsi_deploy_mock.return_value = { - 'root uuid': 'some-root-uuid'} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.vendor.continue_deploy(task) - cleanup_vmedia_boot_mock.assert_called_once_with(task) - do_agent_iscsi_deploy_mock.assert_called_once_with(task, - mock.ANY) - configure_vmedia_boot_mock.assert_called_once_with( - mock.ANY, task, 'some-root-uuid') - boot_mode_cap_mock.assert_called_once_with(task) - update_secure_boot_mock.assert_called_once_with(task, True) - reboot_and_finish_deploy_mock.assert_called_once_with( - mock.ANY, task) - - @mock.patch.object(ilo_deploy, '_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(agent_base_vendor.BaseAgentVendor, - 'reboot_and_finish_deploy', spec_set=True, - autospec=True) - @mock.patch.object(agent_base_vendor.BaseAgentVendor, - 'configure_local_boot', spec_set=True, autospec=True) - @mock.patch.object(iscsi_deploy, 'do_agent_iscsi_deploy', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, - autospec=True) - def test_continue_deploy_localboot(self, cleanup_vmedia_boot_mock, - do_agent_iscsi_deploy_mock, - configure_local_boot_mock, - reboot_and_finish_deploy_mock, - boot_mode_cap_mock, - update_secure_boot_mock): - self.node.provision_state = states.DEPLOYWAIT - self.node.target_provision_state = states.DEPLOYING - self.node.instance_info = { - 'capabilities': {'boot_option': 'local'}} - self.node.save() - do_agent_iscsi_deploy_mock.return_value = { - 'root uuid': 'some-root-uuid'} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.vendor.continue_deploy(task) - cleanup_vmedia_boot_mock.assert_called_once_with(task) - do_agent_iscsi_deploy_mock.assert_called_once_with(task, - mock.ANY) - configure_local_boot_mock.assert_called_once_with( - mock.ANY, task, root_uuid='some-root-uuid', - efi_system_part_uuid=None) - boot_mode_cap_mock.assert_called_once_with(task) - update_secure_boot_mock.assert_called_once_with(task, True) - reboot_and_finish_deploy_mock.assert_called_once_with( - mock.ANY, task) - - @mock.patch.object(ilo_deploy, '_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(agent_base_vendor.BaseAgentVendor, - 'reboot_and_finish_deploy', spec_set=True, - autospec=True) - @mock.patch.object(agent_base_vendor.BaseAgentVendor, - 'configure_local_boot', spec_set=True, autospec=True) - @mock.patch.object(iscsi_deploy, 'do_agent_iscsi_deploy', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, - autospec=True) - def test_continue_deploy_whole_disk_image( - self, cleanup_vmedia_boot_mock, do_agent_iscsi_deploy_mock, - configure_local_boot_mock, reboot_and_finish_deploy_mock, - boot_mode_cap_mock, update_secure_boot_mock): - self.node.provision_state = states.DEPLOYWAIT - self.node.target_provision_state = states.DEPLOYING - self.node.driver_internal_info = {'is_whole_disk_image': True} - self.node.save() - do_agent_iscsi_deploy_mock.return_value = { - 'disk identifier': 'some-disk-id'} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.vendor.continue_deploy(task) - cleanup_vmedia_boot_mock.assert_called_once_with(task) - do_agent_iscsi_deploy_mock.assert_called_once_with(task, - mock.ANY) - configure_local_boot_mock.assert_called_once_with( - mock.ANY, task, root_uuid=None, efi_system_part_uuid=None) - reboot_and_finish_deploy_mock.assert_called_once_with( - mock.ANY, task) - - @mock.patch.object(ilo_deploy, '_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(agent_base_vendor.BaseAgentVendor, - 'reboot_and_finish_deploy', spec_set=True, - autospec=True) - @mock.patch.object(agent_base_vendor.BaseAgentVendor, - 'configure_local_boot', spec_set=True, autospec=True) - @mock.patch.object(iscsi_deploy, 'do_agent_iscsi_deploy', spec_set=True, - autospec=True) - @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True, - autospec=True) - def test_continue_deploy_localboot_uefi(self, cleanup_vmedia_boot_mock, - do_agent_iscsi_deploy_mock, - configure_local_boot_mock, - reboot_and_finish_deploy_mock, - boot_mode_cap_mock, - update_secure_boot_mock): - self.node.provision_state = states.DEPLOYWAIT - self.node.target_provision_state = states.DEPLOYING - self.node.instance_info = { - 'capabilities': {'boot_option': 'local'}} - self.node.save() - do_agent_iscsi_deploy_mock.return_value = { - 'root uuid': 'some-root-uuid', - 'efi system partition uuid': 'efi-system-part-uuid'} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.vendor.continue_deploy(task) - cleanup_vmedia_boot_mock.assert_called_once_with(task) - do_agent_iscsi_deploy_mock.assert_called_once_with(task, - mock.ANY) - configure_local_boot_mock.assert_called_once_with( - mock.ANY, task, root_uuid='some-root-uuid', - efi_system_part_uuid='efi-system-part-uuid') - boot_mode_cap_mock.assert_called_once_with(task) - update_secure_boot_mock.assert_called_once_with(task, True) - reboot_and_finish_deploy_mock.assert_called_once_with( - mock.ANY, task) - - @mock.patch.object(ilo_deploy, '_reboot_into', spec_set=True, - autospec=True) - def test_boot_into_iso(self, reboot_into_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.vendor.boot_into_iso(task, boot_iso_href='foo') - reboot_into_mock.assert_called_once_with(task, 'foo', - ramdisk_options=None) - - @mock.patch.object(ilo_deploy.VendorPassthru, '_validate_boot_into_iso', - spec_set=True, autospec=True) - def test_validate_boot_into_iso(self, validate_boot_into_iso_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - vendor = ilo_deploy.VendorPassthru() - vendor.validate(task, method='boot_into_iso', foo='bar') - validate_boot_into_iso_mock.assert_called_once_with( - vendor, task, {'foo': 'bar'}) - - def test__validate_boot_into_iso_invalid_state(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.node.provision_state = states.AVAILABLE - self.assertRaises( - exception.InvalidStateRequested, - task.driver.vendor._validate_boot_into_iso, - task, {}) - - def test__validate_boot_into_iso_missing_boot_iso_href(self): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.node.provision_state = states.MANAGEABLE - self.assertRaises( - exception.MissingParameterValue, - task.driver.vendor._validate_boot_into_iso, - task, {}) - - @mock.patch.object(deploy_utils, 'validate_image_properties', - spec_set=True, autospec=True) - def test__validate_boot_into_iso_manage(self, validate_image_prop_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - info = {'boot_iso_href': 'foo'} - task.node.provision_state = states.MANAGEABLE - task.driver.vendor._validate_boot_into_iso( - task, info) - validate_image_prop_mock.assert_called_once_with( - task.context, {'image_source': 'foo'}, []) - - @mock.patch.object(deploy_utils, 'validate_image_properties', - spec_set=True, autospec=True) - def test__validate_boot_into_iso_maintenance( - self, validate_image_prop_mock): - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - info = {'boot_iso_href': 'foo'} - task.node.maintenance = True - task.driver.vendor._validate_boot_into_iso( - task, info) - validate_image_prop_mock.assert_called_once_with( - task.context, {'image_source': 'foo'}, []) - - class IloPXEDeployTestCase(db_base.DbTestCase): def setUp(self): @@ -1717,7 +571,7 @@ class IloPXEDeployTestCase(db_base.DbTestCase): @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'tear_down', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_update_secure_boot_mode', 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, @@ -1737,7 +591,7 @@ class IloPXEDeployTestCase(db_base.DbTestCase): @mock.patch.object(iscsi_deploy.ISCSIDeploy, 'tear_down', spec_set=True, autospec=True) @mock.patch.object(ilo_deploy, 'exception', spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_update_secure_boot_mode', + @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) @@ -1757,145 +611,3 @@ class IloPXEDeployTestCase(db_base.DbTestCase): states.POWER_OFF) self.assertTrue(mock_log.called) self.assertEqual(states.DELETED, returned_state) - - -class IloPXEVendorPassthruTestCase(db_base.DbTestCase): - - def setUp(self): - super(IloPXEVendorPassthruTestCase, 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) - - def test_vendor_routes(self): - expected = ['heartbeat', 'pass_deploy_info', - 'pass_bootloader_install_info'] - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - vendor_routes = task.driver.vendor.vendor_routes - self.assertIsInstance(vendor_routes, dict) - self.assertEqual(sorted(expected), sorted(list(vendor_routes))) - - def test_driver_routes(self): - expected = ['lookup'] - with task_manager.acquire(self.context, self.node.uuid, - shared=True) as task: - driver_routes = task.driver.vendor.driver_routes - self.assertIsInstance(driver_routes, dict) - self.assertEqual(sorted(expected), sorted(list(driver_routes))) - - @mock.patch.object(iscsi_deploy.VendorPassthru, 'pass_deploy_info', - spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_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(manager_utils, 'node_set_boot_device', spec_set=True, - autospec=True) - def test_vendorpassthru_pass_deploy_info(self, set_boot_device_mock, - func_update_boot_mode, - func_update_secure_boot_mode, - pxe_vendorpassthru_mock): - kwargs = {'address': '123456'} - 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.vendor.pass_deploy_info(task, **kwargs) - set_boot_device_mock.assert_called_with(task, boot_devices.PXE, - persistent=True) - 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, **kwargs) - - @mock.patch.object(iscsi_deploy.VendorPassthru, 'continue_deploy', - spec_set=True, autospec=True) - @mock.patch.object(ilo_deploy, '_update_secure_boot_mode', autospec=True) - @mock.patch.object(ilo_common, 'update_boot_mode', autospec=True) - def test_vendorpassthru_continue_deploy(self, - func_update_boot_mode, - func_update_secure_boot_mode, - pxe_vendorpassthru_mock): - kwargs = {'address': '123456'} - 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.vendor.continue_deploy(task, **kwargs) - 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, **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', - spec_set=True, autospec=True) - @mock.patch.object(agent.AgentVendorInterface, 'check_deploy_success', - spec_set=True, autospec=True) - @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_update_secure_boot_mode', spec_set=True, - autospec=True) - def test_reboot_to_instance(self, func_update_secure_boot_mode, - func_update_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.assert_called_once_with( - mock.ANY, task.node) - func_update_boot_mode.assert_called_once_with(task) - func_update_secure_boot_mode.assert_called_once_with(task, True) - agent_reboot_to_instance_mock.assert_called_once_with( - mock.ANY, task, **kwargs) - - @mock.patch.object(agent.AgentVendorInterface, 'reboot_to_instance', - spec_set=True, autospec=True) - @mock.patch.object(agent.AgentVendorInterface, 'check_deploy_success', - spec_set=True, autospec=True) - @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, - autospec=True) - @mock.patch.object(ilo_deploy, '_update_secure_boot_mode', spec_set=True, - autospec=True) - def test_reboot_to_instance_deploy_fail(self, func_update_secure_boot_mode, - func_update_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.assert_called_once_with( - mock.ANY, task.node) - self.assertFalse(func_update_boot_mode.called) - self.assertFalse(func_update_secure_boot_mode.called) - agent_reboot_to_instance_mock.assert_called_once_with( - mock.ANY, task, **kwargs) - - @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', - spec_set=True, autospec=True) - @mock.patch.object(agent.AgentVendorInterface, 'continue_deploy', - spec_set=True, autospec=True) - def test_continue_deploy(self, agent_continue_deploy_mock, - cleanup_mock): - CONF.ilo.use_web_server_for_images = True - kwargs = {'address': '123456'} - with task_manager.acquire(self.context, self.node.uuid, - shared=False) as task: - task.driver.vendor.continue_deploy(task, **kwargs) - cleanup_mock.assert_called_once_with(task) - agent_continue_deploy_mock.assert_called_once_with( - mock.ANY, task, **kwargs) diff --git a/ironic/tests/unit/drivers/modules/ilo/test_vendor.py b/ironic/tests/unit/drivers/modules/ilo/test_vendor.py new file mode 100644 index 0000000000..a96fa84d3c --- /dev/null +++ b/ironic/tests/unit/drivers/modules/ilo/test_vendor.py @@ -0,0 +1,228 @@ +# Copyright 2015 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 common methods used by iLO modules.""" + +import mock +from oslo_config import cfg +import six + +from ironic.common import exception +from ironic.common import states +from ironic.conductor import task_manager +from ironic.conductor import utils as manager_utils +from ironic.drivers.modules import agent +from ironic.drivers.modules import deploy_utils +from ironic.drivers.modules.ilo import common as ilo_common +from ironic.drivers.modules.ilo import vendor as ilo_vendor +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() +CONF = cfg.CONF + + +class VendorPassthruTestCase(db_base.DbTestCase): + + def setUp(self): + super(VendorPassthruTestCase, self).setUp() + mgr_utils.mock_the_extension_manager(driver="iscsi_ilo") + self.node = obj_utils.create_test_node(self.context, + driver='iscsi_ilo', + driver_info=INFO_DICT) + + @mock.patch.object(manager_utils, 'node_power_action', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'setup_vmedia', spec_set=True, + autospec=True) + def test_boot_into_iso(self, setup_vmedia_mock, power_action_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.vendor.boot_into_iso(task, boot_iso_href='foo') + setup_vmedia_mock.assert_called_once_with(task, 'foo', + ramdisk_options=None) + power_action_mock.assert_called_once_with(task, states.REBOOT) + + @mock.patch.object(ilo_vendor.VendorPassthru, '_validate_boot_into_iso', + spec_set=True, autospec=True) + def test_validate_boot_into_iso(self, validate_boot_into_iso_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + vendor = ilo_vendor.VendorPassthru() + vendor.validate(task, method='boot_into_iso', foo='bar') + validate_boot_into_iso_mock.assert_called_once_with( + vendor, task, {'foo': 'bar'}) + + def test__validate_boot_into_iso_invalid_state(self): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.node.provision_state = states.AVAILABLE + self.assertRaises( + exception.InvalidStateRequested, + task.driver.vendor._validate_boot_into_iso, + task, {}) + + def test__validate_boot_into_iso_missing_boot_iso_href(self): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.node.provision_state = states.MANAGEABLE + self.assertRaises( + exception.MissingParameterValue, + task.driver.vendor._validate_boot_into_iso, + task, {}) + + @mock.patch.object(deploy_utils, 'validate_image_properties', + spec_set=True, autospec=True) + def test__validate_boot_into_iso_manage(self, validate_image_prop_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + info = {'boot_iso_href': 'foo'} + task.node.provision_state = states.MANAGEABLE + task.driver.vendor._validate_boot_into_iso( + task, info) + validate_image_prop_mock.assert_called_once_with( + task.context, {'image_source': 'foo'}, []) + + @mock.patch.object(deploy_utils, 'validate_image_properties', + spec_set=True, autospec=True) + def test__validate_boot_into_iso_maintenance( + self, validate_image_prop_mock): + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + info = {'boot_iso_href': 'foo'} + task.node.maintenance = True + task.driver.vendor._validate_boot_into_iso( + task, info) + validate_image_prop_mock.assert_called_once_with( + task.context, {'image_source': 'foo'}, []) + + @mock.patch.object(iscsi_deploy.VendorPassthru, 'pass_deploy_info', + spec_set=True, autospec=True) + @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) + def test_pass_deploy_info(self, func_update_boot_mode, + func_update_secure_boot_mode, + vendorpassthru_mock): + kwargs = {'address': '123456'} + 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.vendor.pass_deploy_info(task, **kwargs) + func_update_boot_mode.assert_called_once_with(task) + func_update_secure_boot_mode.assert_called_once_with(task, True) + vendorpassthru_mock.assert_called_once_with( + mock.ANY, task, **kwargs) + + @mock.patch.object(iscsi_deploy.VendorPassthru, '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): + kwargs = {'address': '123456'} + 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.vendor.continue_deploy(task, **kwargs) + 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, **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', + spec_set=True, autospec=True) + @mock.patch.object(agent.AgentVendorInterface, 'check_deploy_success', + spec_set=True, autospec=True) + @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True, + autospec=True) + def test_reboot_to_instance(self, func_update_secure_boot_mode, + func_update_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.assert_called_once_with( + mock.ANY, task.node) + func_update_boot_mode.assert_called_once_with(task) + func_update_secure_boot_mode.assert_called_once_with(task, True) + agent_reboot_to_instance_mock.assert_called_once_with( + mock.ANY, task, **kwargs) + + @mock.patch.object(agent.AgentVendorInterface, 'reboot_to_instance', + spec_set=True, autospec=True) + @mock.patch.object(agent.AgentVendorInterface, 'check_deploy_success', + spec_set=True, autospec=True) + @mock.patch.object(ilo_common, 'update_boot_mode', spec_set=True, + autospec=True) + @mock.patch.object(ilo_common, 'update_secure_boot_mode', spec_set=True, + autospec=True) + def test_reboot_to_instance_deploy_fail(self, func_update_secure_boot_mode, + func_update_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.assert_called_once_with( + mock.ANY, task.node) + self.assertFalse(func_update_boot_mode.called) + self.assertFalse(func_update_secure_boot_mode.called) + agent_reboot_to_instance_mock.assert_called_once_with( + mock.ANY, task, **kwargs) + + @mock.patch.object(ilo_common, 'cleanup_vmedia_boot', + spec_set=True, autospec=True) + @mock.patch.object(agent.AgentVendorInterface, 'continue_deploy', + spec_set=True, autospec=True) + def test_continue_deploy(self, agent_continue_deploy_mock, + cleanup_mock): + CONF.ilo.use_web_server_for_images = True + kwargs = {'address': '123456'} + with task_manager.acquire(self.context, self.node.uuid, + shared=False) as task: + task.driver.vendor.continue_deploy(task, **kwargs) + cleanup_mock.assert_called_once_with(task) + agent_continue_deploy_mock.assert_called_once_with( + mock.ANY, task, **kwargs)