Merge "Generalize ISO building for virtual media driver"

This commit is contained in:
Zuul 2020-04-17 12:32:55 +00:00 committed by Gerrit Code Review
commit 8a143378cb
13 changed files with 703 additions and 707 deletions

View File

@ -16,7 +16,9 @@
import os
import re
import shutil
import time
from urllib import parse as urlparse
from ironic_lib import metrics_utils
from ironic_lib import utils as il_utils
@ -824,6 +826,35 @@ def direct_deploy_should_convert_raw_image(node):
return CONF.force_raw_images and CONF.agent.stream_raw_images
def copy_image_to_web_server(source_file_path, destination):
"""Copies the given image to the http web server.
This method copies the given image to the http_root location.
It enables read-write access to the image else the deploy fails
as the image file at the web_server url is inaccessible.
:param source_file_path: The absolute path of the image file
which needs to be copied to the
web server root.
:param destination: The name of the file that
will contain the copied image.
:raises: ImageUploadFailed exception if copying the source
file to the web server fails.
:returns: image url after the source image is uploaded.
"""
image_url = urlparse.urljoin(CONF.deploy.http_url, destination)
image_path = os.path.join(CONF.deploy.http_root, destination)
try:
shutil.copyfile(source_file_path, image_path)
except IOError as exc:
raise exception.ImageUploadFailed(image_name=destination,
web_server=CONF.deploy.http_url,
reason=exc)
return image_url
@image_cache.cleanup(priority=50)
class InstanceImageCache(image_cache.ImageCache):

View File

@ -16,8 +16,6 @@ Boot Interface for iLO drivers and its supporting methods.
"""
import os
import tempfile
from urllib import parse as urlparse
from ironic_lib import metrics_utils
from ironic_lib import utils as ironic_utils
@ -40,6 +38,7 @@ from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers.modules import ipxe
from ironic.drivers.modules import pxe
from ironic.drivers.modules import virtual_media_base
LOG = logging.getLogger(__name__)
@ -47,16 +46,45 @@ METRICS = metrics_utils.get_metrics_logger(__name__)
CONF = cfg.CONF
REQUIRED_PROPERTIES = {
DEPLOY_ISO_PROPERTIES = {
'ilo_deploy_iso': _("UUID (from Glance) of the deployment ISO. "
"Required.")
}
RESCUE_PROPERTIES = {
DEPLOY_RAMDISK_PROPERTIES = {
'deploy_kernel': _("URL or Glance UUID of the deployment kernel. "
"Required."),
'deploy_ramdisk': _("URL or Glance UUID of the ramdisk that is "
"mounted at boot time. Required.")
}
OPTIONAL_PROPERTIES = {
'bootloader': _("URL or Glance UUID of the EFI system partition "
"image containing EFI boot loader. This image will be "
"used by ironic when building UEFI-bootable ISO "
"out of kernel and ramdisk. Required for UEFI "
"boot from partition images.")
}
RESCUE_ISO_PROPERTIES = {
'ilo_rescue_iso': _("UUID (from Glance) of the rescue ISO. Only "
"required if rescue mode is being used and ironic is "
"managing booting the rescue ramdisk.")
}
COMMON_PROPERTIES = REQUIRED_PROPERTIES
RESCUE_RAMDISK_PROPERTIES = {
'rescue_kernel': _("URL or Glance UUID of the rescue kernel. "
"Required."),
'rescue_ramdisk': _("URL or Glance UUID of the ramdisk that is "
"mounted at boot time. Required.")
}
COMMON_PROPERTIES = DEPLOY_ISO_PROPERTIES
KERNEL_RAMDISK_LABELS = {
'deploy': DEPLOY_RAMDISK_PROPERTIES,
'rescue': RESCUE_RAMDISK_PROPERTIES
}
def parse_driver_info(node, mode='deploy'):
@ -77,26 +105,37 @@ def parse_driver_info(node, mode='deploy'):
"""
info = node.driver_info
d_info = {}
if mode == 'rescue':
if mode == 'rescue' and info.get('ilo_rescue_iso'):
d_info['ilo_rescue_iso'] = info.get('ilo_rescue_iso')
else:
elif mode == 'deploy' and info.get('ilo_deploy_iso'):
d_info['ilo_deploy_iso'] = info.get('ilo_deploy_iso')
else:
params_to_check = KERNEL_RAMDISK_LABELS[mode]
d_info = {option: info.get(option)
for option in params_to_check}
if not any(d_info.values()):
# NOTE(dtantsur): avoid situation when e.g. deploy_kernel comes
# from driver_info but deploy_ramdisk comes from configuration,
# since it's a sign of a potential operator's mistake.
d_info = {k: getattr(CONF.conductor, k)
for k in params_to_check}
error_msg = (_("Error validating iLO virtual media for %(mode)s. "
"Either 'ilo_%(mode)s_iso' is missing or "
"DEPLOY_RAMDISK_PROPERTIES or RESCUE_RAMDISK_PROPERTIES "
"were missing in node's driver_info.") % {'mode': mode})
error_msg = (_("Error validating iLO virtual media for %s. Some "
"parameters were missing in node's driver_info") % mode)
deploy_utils.check_for_missing_params(d_info, error_msg)
d_info.update(
{option: info.get(option, getattr(CONF.conductor, option, None))
for option in OPTIONAL_PROPERTIES})
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.
@ -171,12 +210,6 @@ def _get_boot_iso(task, root_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("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
@ -186,43 +219,15 @@ def _get_boot_iso(task, root_uuid):
# 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 = boot_mode_utils.get_boot_mode(task.node)
boot_iso_object_name = _get_boot_iso_object_name(task.node)
kernel_params = ""
if deploy_utils.get_boot_option(task.node) == "ramdisk":
i_info = task.node.instance_info
kernel_params = "root=/dev/ram0 text "
kernel_params += i_info.get("ramdisk_kernel_arguments", "")
else:
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_href=deploy_iso_uuid,
root_uuid=root_uuid,
kernel_params=kernel_params,
boot_mode=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)
use_web_server = CONF.ilo.use_web_server_for_images
container = CONF.ilo.swift_ilo_container
LOG.debug("Created boot_iso %s in Swift", boot_iso_object_name)
return 'swift:%s' % boot_iso_object_name
return virtual_media_base.prepare_iso_image(
task, kernel_href, ramdisk_href,
deploy_iso_href=deploy_iso_uuid,
root_uuid=root_uuid, use_web_server=use_web_server,
container=container)
def _clean_up_boot_iso_for_instance(node):
@ -230,25 +235,21 @@ def _clean_up_boot_iso_for_instance(node):
: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'):
boot_iso_object_name = virtual_media_base.get_iso_image_name(node)
if CONF.ilo.use_web_server_for_images:
boot_iso_path = os.path.join(
CONF.deploy.http_root, boot_iso_object_name)
ironic_utils.unlink_without_raise(boot_iso_path)
else:
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("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)
ironic_utils.unlink_without_raise(boot_iso_path)
def _parse_deploy_info(node):
@ -284,19 +285,7 @@ def _validate_driver_info(task):
"""
node = task.node
ilo_common.parse_driver_info(node)
if 'ilo_deploy_iso' not in node.driver_info:
raise exception.MissingParameterValue(_(
"Missing 'ilo_deploy_iso' parameter in node's 'driver_info'."))
deploy_iso = node.driver_info['ilo_deploy_iso']
if not service_utils.is_glance_image(deploy_iso):
try:
image_service.HttpImageService().validate_href(deploy_iso)
except exception.ImageRefValidationFailed:
raise exception.InvalidParameterValue(_(
"Virtual media boot accepts only Glance images or "
"HTTP(S) as driver_info['ilo_deploy_iso']. Either '%s' "
"is not a glance UUID or not a valid HTTP(S) URL or "
"the given URL is not reachable.") % deploy_iso)
parse_driver_info(node)
def _validate_instance_image_info(task):
@ -518,11 +507,19 @@ class IloVirtualMediaBoot(base.BootInterface):
deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task)
ramdisk_params['BOOTIF'] = deploy_nic_mac
if node.provision_state == states.RESCUING:
if (node.driver_info.get('ilo_rescue_iso')
and node.provision_state == states.RESCUING):
iso = node.driver_info['ilo_rescue_iso']
else:
elif node.driver_info.get('ilo_deploy_iso'):
iso = node.driver_info['ilo_deploy_iso']
else:
mode = deploy_utils.rescue_or_deploy_mode(node)
d_info = parse_driver_info(node, mode)
use_web_server = CONF.ilo.use_web_server_for_images
container = CONF.ilo.swift_ilo_container
iso = virtual_media_base.prepare_deploy_iso(
task, ramdisk_params, mode, d_info,
use_web_server=use_web_server, container=container)
ilo_common.setup_vmedia(task, iso, ramdisk_params)
@METRICS.timer('IloVirtualMediaBoot.prepare_instance')
@ -636,6 +633,10 @@ class IloVirtualMediaBoot(base.BootInterface):
:returns: None
:raises: IloOperationError, if some operation on iLO failed.
"""
info = task.node.driver_info
if not info.get('ilo_deploy_iso') and not info.get('ilo_rescue_iso'):
_clean_up_boot_iso_for_instance(task.node)
ilo_common.cleanup_vmedia_boot(task)
def _configure_vmedia_boot(self, task, root_uuid):

View File

@ -17,7 +17,6 @@ Common functionalities shared between different iLO modules.
"""
import os
import shutil
import tempfile
from urllib import parse as urlparse
@ -134,17 +133,12 @@ def copy_image_to_web_server(source_file_path, destination):
:returns: image url after the source image is uploaded.
"""
LOG.warning(
'This method is obsolete and will be removed in next release. '
'Please use deploy_utils.copy_image_to_web_server() instead.')
image_url = urlparse.urljoin(CONF.deploy.http_url, destination)
image_path = os.path.join(CONF.deploy.http_root, destination)
try:
shutil.copyfile(source_file_path, image_path)
except IOError as exc:
raise exception.ImageUploadFailed(image_name=destination,
web_server=CONF.deploy.http_url,
reason=exc)
os.chmod(image_path, 0o644)
return image_url
return deploy_utils.copy_image_to_web_server(source_file_path,
destination)
def remove_image_from_web_server(object_name):
@ -420,8 +414,8 @@ def _prepare_floppy_image(task, params):
images.create_vfat_image(vfat_image_tmpfile, parameters=params)
object_name = _get_floppy_image_name(task.node)
if CONF.ilo.use_web_server_for_images:
image_url = copy_image_to_web_server(vfat_image_tmpfile,
object_name)
image_url = deploy_utils.copy_image_to_web_server(
vfat_image_tmpfile, object_name)
else:
image_url = copy_image_to_swift(vfat_image_tmpfile, object_name)

View File

@ -32,6 +32,7 @@ from ironic.common import exception
from ironic.common.i18n import _
from ironic.common import image_service
from ironic.common import swift
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.ilo import common as ilo_common
# Supported components for firmware update when invoked through manual clean
@ -370,7 +371,7 @@ def _extract_fw_from_file(node, target_file):
"firmware file %(firmware_image)s on web server ...",
{'firmware_image': fw_image_location,
'node': node.uuid})
fw_image_uploaded_url = ilo_common.copy_image_to_web_server(
fw_image_uploaded_url = deploy_utils.copy_image_to_web_server(
fw_image_location, fw_image_filename)
fw_image_location_obj.fw_image_location = fw_image_uploaded_url

View File

@ -20,7 +20,6 @@ from urllib import parse as urlparse
from ironic_lib import utils as ironic_utils
from oslo_log import log
from oslo_serialization import base64
from oslo_utils import importutils
from ironic.common import boot_devices
@ -36,6 +35,7 @@ from ironic.drivers import base
from ironic.drivers.modules import boot_mode_utils
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.redfish import utils as redfish_utils
from ironic.drivers.modules import virtual_media_base
LOG = log.getLogger(__name__)
@ -392,163 +392,16 @@ class RedfishVirtualMediaBoot(base.BootInterface):
return image_url
@staticmethod
def _get_iso_image_name(node):
"""Returns the boot iso image name for a given node.
:param node: the node for which image name is to be provided.
"""
return "boot-%s" % node.uuid
@classmethod
def _cleanup_iso_image(cls, task):
"""Deletes the ISO if it was created for the instance.
:param task: an ironic node object.
"""
iso_object_name = cls._get_iso_image_name(task.node)
iso_object_name = virtual_media_base.get_iso_image_name(task.node)
cls._unpublish_image(iso_object_name)
@classmethod
def _prepare_iso_image(cls, task, kernel_href, ramdisk_href,
bootloader_href=None, configdrive=None,
root_uuid=None, params=None):
"""Prepare an ISO to boot the node.
Build bootable ISO out of `kernel_href` and `ramdisk_href` (and
`bootloader` if it's UEFI boot), then push built image up to Swift and
return a temporary URL.
:param task: a TaskManager instance containing the node to act on.
:param kernel_href: URL or Glance UUID of the kernel to use
:param ramdisk_href: URL or Glance UUID of the ramdisk to use
:param bootloader_href: URL or Glance UUID of the EFI bootloader
image to use when creating UEFI bootbable ISO
:param configdrive: URL to or a compressed blob of a ISO9660 or
FAT-formatted OpenStack config drive image. This image will be
written onto the built ISO image. Optional.
:param root_uuid: optional uuid of the root partition.
:param params: a dictionary containing 'parameter name'->'value'
mapping to be passed to kernel command line.
:returns: bootable ISO HTTP URL.
:raises: MissingParameterValue, if any of the required parameters are
missing.
:raises: InvalidParameterValue, if any of the parameters have invalid
value.
:raises: ImageCreationFailed, if creating ISO image failed.
"""
if not kernel_href or not ramdisk_href:
raise exception.InvalidParameterValue(_(
"Unable to find kernel or ramdisk for "
"building ISO for %(node)s") %
{'node': task.node.uuid})
i_info = task.node.instance_info
if deploy_utils.get_boot_option(task.node) == "ramdisk":
kernel_params = "root=/dev/ram0 text "
kernel_params += i_info.get("ramdisk_kernel_arguments", "")
else:
kernel_params = i_info.get(
'kernel_append_params', CONF.redfish.kernel_append_params)
if params:
kernel_params = ' '.join(
(kernel_params, ' '.join(
'%s=%s' % kv for kv in params.items())))
boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node)
LOG.debug("Trying to create %(boot_mode)s ISO image for node %(node)s "
"with kernel %(kernel_href)s, ramdisk %(ramdisk_href)s, "
"bootloader %(bootloader_href)s and kernel params %(params)s"
"", {'node': task.node.uuid,
'boot_mode': boot_mode,
'kernel_href': kernel_href,
'ramdisk_href': ramdisk_href,
'bootloader_href': bootloader_href,
'params': kernel_params})
with tempfile.NamedTemporaryFile(
dir=CONF.tempdir, suffix='.iso') as boot_fileobj:
with tempfile.NamedTemporaryFile(
dir=CONF.tempdir, suffix='.img') as cfgdrv_fileobj:
configdrive_href = configdrive
if configdrive:
parsed_url = urlparse.urlparse(configdrive)
if not parsed_url.scheme:
cfgdrv_blob = base64.decode_as_bytes(configdrive)
with open(cfgdrv_fileobj.name, 'wb') as f:
f.write(cfgdrv_blob)
configdrive_href = urlparse.urlunparse(
('file', '', cfgdrv_fileobj.name, '', '', ''))
LOG.info("Burning configdrive %(url)s to boot ISO image "
"for node %(node)s", {'url': configdrive_href,
'node': task.node.uuid})
boot_iso_tmp_file = boot_fileobj.name
images.create_boot_iso(
task.context, boot_iso_tmp_file,
kernel_href, ramdisk_href,
esp_image_href=bootloader_href,
configdrive_href=configdrive_href,
root_uuid=root_uuid,
kernel_params=kernel_params,
boot_mode=boot_mode)
iso_object_name = cls._get_iso_image_name(task.node)
image_url = cls._publish_image(
boot_iso_tmp_file, iso_object_name)
LOG.debug("Created ISO %(name)s in object store for node %(node)s, "
"exposed as temporary URL "
"%(url)s", {'node': task.node.uuid,
'name': iso_object_name,
'url': image_url})
return image_url
@classmethod
def _prepare_deploy_iso(cls, task, params, mode):
"""Prepare deploy or rescue ISO image
Build bootable ISO out of
`[driver_info]/deploy_kernel`/`[driver_info]/deploy_ramdisk` or
`[driver_info]/rescue_kernel`/`[driver_info]/rescue_ramdisk`
and `[driver_info]/bootloader`, then push built image up to Glance
and return temporary Swift URL to the image.
:param task: a TaskManager instance containing the node to act on.
:param params: a dictionary containing 'parameter name'->'value'
mapping to be passed to kernel command line.
:param mode: either 'deploy' or 'rescue'.
:returns: bootable ISO HTTP URL.
:raises: MissingParameterValue, if any of the required parameters are
missing.
:raises: InvalidParameterValue, if any of the parameters have invalid
value.
:raises: ImageCreationFailed, if creating ISO image failed.
"""
node = task.node
d_info = cls._parse_driver_info(node)
kernel_href = d_info.get('%s_kernel' % mode)
ramdisk_href = d_info.get('%s_ramdisk' % mode)
bootloader_href = d_info.get('bootloader')
return cls._prepare_iso_image(
task, kernel_href, ramdisk_href, bootloader_href, params=params)
@classmethod
def _prepare_boot_iso(cls, task, root_uuid=None):
"""Prepare boot ISO image
@ -598,9 +451,19 @@ class RedfishVirtualMediaBoot(base.BootInterface):
bootloader_href = d_info.get('bootloader')
return cls._prepare_iso_image(
task, kernel_href, ramdisk_href, bootloader_href,
root_uuid=root_uuid)
if CONF.redfish.use_swift:
use_web_server = False
container = CONF.redfish.swift_container
timeout = CONF.redfish.swift_object_expiry_timeout
else:
use_web_server = True
container = None
timeout = None
return virtual_media_base.prepare_iso_image(
task, kernel_href, ramdisk_href, bootloader_href=bootloader_href,
root_uuid=root_uuid, timeout=timeout,
use_web_server=use_web_server, container=container)
def get_properties(self):
"""Return the properties of the interface.
@ -753,7 +616,16 @@ class RedfishVirtualMediaBoot(base.BootInterface):
mode = deploy_utils.rescue_or_deploy_mode(node)
iso_ref = self._prepare_deploy_iso(task, ramdisk_params, mode)
if CONF.redfish.use_swift:
use_web_server = False
container = CONF.redfish.swift_container
else:
use_web_server = True
container = None
iso_ref = virtual_media_base.prepare_deploy_iso(
task, ramdisk_params, mode, d_info,
use_web_server=use_web_server, container=container)
self._eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
self._insert_vmedia(task, iso_ref, sushy.VIRTUAL_MEDIA_CD)

View File

@ -0,0 +1,173 @@
# 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.
import tempfile
from oslo_log import log
from ironic.common import exception
from ironic.common.i18n import _
from ironic.common import images
from ironic.common import swift
from ironic.conf import CONF
from ironic.drivers.modules import boot_mode_utils
from ironic.drivers.modules import deploy_utils
LOG = log.getLogger(__name__)
def get_iso_image_name(node):
"""Returns the boot iso image name for a given node.
:param node: the node for which image name is to be provided.
"""
return "boot-%s" % node.uuid
def prepare_iso_image(task, kernel_href, ramdisk_href, deploy_iso_href=None,
bootloader_href=None, root_uuid=None,
kernel_params=None, timeout=None,
use_web_server=False, container=None):
"""Prepare an ISO to boot the node.
Build bootable ISO out of `kernel_href` and `ramdisk_href` (and
`bootloader` if it's UEFI boot), then push built image up to Swift and
return a temporary URL.
:param task: a TaskManager instance containing the node to act on.
:param kernel_href: URL or Glance UUID of the kernel to use
:param ramdisk_href: URL or Glance UUID of the ramdisk to use
:param bootloader_href: URL or Glance UUID of the EFI bootloader
image to use when creating UEFI bootbable ISO
:param root_uuid: optional uuid of the root partition.
:param kernel_params: a dictionary containing 'parameter name'->'value'
mapping to be passed to kernel command line.
:param timeout: swift object expiry timeout
:returns: bootable ISO HTTP URL.
:raises: MissingParameterValue, if any of the required parameters are
missing.
:raises: InvalidParameterValue, if any of the parameters have invalid
value.
:raises: ImageCreationFailed, if creating ISO image failed.
"""
if not kernel_href or not ramdisk_href:
raise exception.InvalidParameterValue(_(
"Unable to find kernel or ramdisk for "
"building ISO for %(node)s") %
{'node': task.node.uuid})
boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node)
LOG.debug("Trying to create %(boot_mode)s ISO image for node %(node)s "
"with kernel %(kernel_href)s, ramdisk %(ramdisk_href)s, "
"bootloader %(bootloader_href)s and kernel params %(params)s"
"", {'node': task.node.uuid,
'boot_mode': boot_mode,
'kernel_href': kernel_href,
'ramdisk_href': ramdisk_href,
'bootloader_href': bootloader_href,
'params': kernel_params})
with tempfile.NamedTemporaryFile(
dir=CONF.tempdir, suffix='.iso') as fileobj:
boot_iso_tmp_file = fileobj.name
images.create_boot_iso(task.context, boot_iso_tmp_file,
kernel_href, ramdisk_href,
deploy_iso_href=deploy_iso_href,
esp_image_href=bootloader_href,
root_uuid=root_uuid,
kernel_params=kernel_params,
boot_mode=boot_mode)
iso_object_name = get_iso_image_name(task.node)
if use_web_server:
boot_iso_url = (
deploy_utils.copy_image_to_web_server(boot_iso_tmp_file,
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:
swift_api = swift.SwiftAPI()
object_headers = None
if task.node.driver == 'redfish':
object_headers = {'X-Delete-After': str(timeout)}
swift_api.create_object(container, iso_object_name,
boot_iso_tmp_file,
object_headers=object_headers)
LOG.debug("Created ISO %(name)s in Swift for node %(node)s",
{'node': task.node.uuid, 'name': iso_object_name})
if task.node.driver == 'redfish':
boot_iso_url = swift_api.get_temp_url(
container, iso_object_name, timeout)
return boot_iso_url
else:
return 'swift:%s' % iso_object_name
def prepare_deploy_iso(task, params, mode, driver_info,
use_web_server=False, container=None):
"""Prepare deploy or rescue ISO image
Build bootable ISO out of
`[driver_info]/deploy_kernel`/`[driver_info]/deploy_ramdisk` or
`[driver_info]/rescue_kernel`/`[driver_info]/rescue_ramdisk`
and `[driver_info]/bootloader`, then push built image up to Glance
and return temporary Swift URL to the image.
:param task: a TaskManager instance containing the node to act on.
:param params: a dictionary containing 'parameter name'->'value'
mapping to be passed to kernel command line.
:param mode: either 'deploy' or 'rescue'.
:param driver_info: a dictionary containing driver_info values.
:returns: bootable ISO HTTP URL.
:raises: MissingParameterValue, if any of the required parameters are
missing.
:raises: InvalidParameterValue, if any of the parameters have invalid
value.
:raises: ImageCreationFailed, if creating ISO image failed.
"""
kernel_href = driver_info.get('%s_kernel' % mode)
ramdisk_href = driver_info.get('%s_ramdisk' % mode)
bootloader_href = driver_info.get('bootloader')
timeout = None
if deploy_utils.get_boot_option(task.node) == "ramdisk":
i_info = task.node.instance_info
kernel_params = "root=/dev/ram0 text "
kernel_params += i_info.get("ramdisk_kernel_arguments", "")
elif task.node.driver == 'redfish':
kernel_params = CONF.redfish.kernel_append_params
timeout = CONF.redfish.swift_object_expiry_timeout
else:
kernel_params = CONF.pxe.pxe_append_params
if params:
kernel_params = ' '.join(
(kernel_params, ' '.join(
'%s=%s' % kv for kv in params.items())))
return prepare_iso_image(task, kernel_href, ramdisk_href,
bootloader_href=bootloader_href,
kernel_params=kernel_params, timeout=timeout,
use_web_server=use_web_server,
container=container)

View File

@ -15,9 +15,6 @@
"""Test class for boot methods used by iLO modules."""
import io
import tempfile
from ironic_lib import utils as ironic_utils
import mock
from oslo_config import cfg
@ -39,6 +36,7 @@ from ironic.drivers.modules.ilo import management as ilo_management
from ironic.drivers.modules import ipxe
from ironic.drivers.modules import pxe
from ironic.drivers.modules.storage import noop as noop_storage
from ironic.drivers.modules import virtual_media_base
from ironic.drivers import utils as driver_utils
from ironic.tests.unit.drivers.modules.ilo import test_common
@ -50,13 +48,44 @@ class IloBootCommonMethodsTestCase(test_common.BaseIloTest):
boot_interface = 'ilo-virtual-media'
def test_parse_driver_info(self):
def test_parse_driver_info_deploy_iso(self):
self.node.driver_info['ilo_deploy_iso'] = 'deploy-iso'
expected_driver_info = {'ilo_deploy_iso': 'deploy-iso'}
expected_driver_info = {'bootloader': None,
'ilo_deploy_iso': 'deploy-iso'}
actual_driver_info = ilo_boot.parse_driver_info(self.node)
self.assertEqual(expected_driver_info, actual_driver_info)
def test_parse_driver_info_rescue_iso(self):
self.node.driver_info['ilo_rescue_iso'] = 'rescue-iso'
expected_driver_info = {'bootloader': None,
'ilo_rescue_iso': 'rescue-iso'}
actual_driver_info = ilo_boot.parse_driver_info(self.node, 'rescue')
self.assertEqual(expected_driver_info, actual_driver_info)
def test_parse_driver_info_deploy(self):
self.node.driver_info['deploy_kernel'] = 'kernel'
self.node.driver_info['deploy_ramdisk'] = 'ramdisk'
self.node.driver_info['bootloader'] = 'bootloader'
expected_driver_info = {'deploy_kernel': 'kernel',
'deploy_ramdisk': 'ramdisk',
'bootloader': 'bootloader'}
actual_driver_info = ilo_boot.parse_driver_info(self.node)
self.assertEqual(expected_driver_info, actual_driver_info)
def test_parse_driver_info_rescue(self):
self.node.driver_info['rescue_kernel'] = 'kernel'
self.node.driver_info['rescue_ramdisk'] = 'ramdisk'
self.node.driver_info['bootloader'] = 'bootloader'
expected_driver_info = {'rescue_kernel': 'kernel',
'rescue_ramdisk': 'ramdisk',
'bootloader': 'bootloader'}
actual_driver_info = ilo_boot.parse_driver_info(self.node, 'rescue')
self.assertEqual(expected_driver_info, actual_driver_info)
def test_parse_driver_info_exc(self):
self.assertRaises(exception.MissingParameterValue,
ilo_boot.parse_driver_info, self.node)
@ -66,11 +95,6 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest):
boot_interface = 'ilo-virtual-media'
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):
@ -128,71 +152,21 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest):
boot_iso_expected = u'glance://uui\u0111'
self.assertEqual(boot_iso_expected, boot_iso_actual)
@mock.patch.object(boot_mode_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,
@mock.patch.object(virtual_media_base, 'prepare_iso_image', 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=io.BytesIO)
fileobj_mock.name = 'tmpfile'
mock_file_handle = mock.MagicMock(spec=io.BytesIO)
mock_file_handle.__enter__.return_value = fileobj_mock
tempfile_mock.return_value = mock_file_handle
def test__get_boot_iso_create(self, deploy_info_mock,
image_props_mock, prepare_iso_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': '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'
prepare_iso_mock.return_value = 'swift:boot-iso'
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
boot_iso_actual = ilo_boot._get_boot_iso(task, 'root-uuid')
@ -200,166 +174,30 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest):
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',
prepare_iso_mock.assert_called_once_with(
task, 'kernel_uuid', 'ramdisk_uuid',
deploy_iso_href='deploy_iso_uuid',
root_uuid='root-uuid',
kernel_params='kernel-params',
boot_mode='uefi')
swift_obj_mock.create_object.assert_called_once_with('ilo-cont',
'abcdef',
'tmpfile')
boot_iso_expected = 'swift:abcdef'
root_uuid='root-uuid', use_web_server=False,
container='ironic_ilo_container')
boot_iso_expected = 'swift:boot-iso'
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_recreate_boot_iso_use_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=io.BytesIO)
fileobj_mock.name = 'tmpfile'
mock_file_handle = mock.MagicMock(spec=io.BytesIO)
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 = 'new_boot_iso'
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/new_boot_iso"
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
instance_info = task.node.instance_info
old_boot_iso = 'http://10.10.1.30/httpboot/old_boot_iso'
instance_info['ilo_boot_iso'] = old_boot_iso
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_href='deploy_iso_uuid',
root_uuid='root-uuid',
kernel_params='kernel-params',
boot_mode='uefi')
boot_iso_expected = 'http://10.10.1.30/httpboot/new_boot_iso'
self.assertEqual(boot_iso_expected, boot_iso_actual)
copy_file_mock.assert_called_once_with(fileobj_mock.name,
'new_boot_iso')
@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=io.BytesIO)
fileobj_mock.name = 'tmpfile'
mock_file_handle = mock.MagicMock(spec=io.BytesIO)
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_href='deploy_iso_uuid',
root_uuid='root-uuid',
kernel_params='kernel-params',
boot_mode='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(virtual_media_base, 'get_iso_image_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(virtual_media_base, 'get_iso_image_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,
@ -369,10 +207,6 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest):
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')
@ -380,25 +214,17 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest):
@mock.patch.object(ironic_utils, 'unlink_without_raise', spec_set=True,
autospec=True)
def test__clean_up_boot_iso_for_instance_on_webserver(self, unlink_mock):
@mock.patch.object(virtual_media_base, 'get_iso_image_name',
spec_set=True, autospec=True)
def test__clean_up_boot_iso_for_instance_on_webserver(
self, boot_object_name_mock, unlink_mock):
boot_object_name_mock.return_value = 'boot-object'
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',
@ -412,68 +238,15 @@ class IloBootPrivateMethodsTestCase(test_common.BaseIloTest):
@mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test__validate_driver_info_MissingParam(self, mock_parse_driver_info):
@mock.patch.object(ilo_boot, 'parse_driver_info', spec_set=True,
autospec=True)
def test__validate_driver_info(self, mock_driver_info,
mock_parse_driver_info):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
self.assertRaisesRegex(exception.MissingParameterValue,
"Missing 'ilo_deploy_iso'",
ilo_boot._validate_driver_info, task)
mock_parse_driver_info.assert_called_once_with(task.node)
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
autospec=True)
@mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test__validate_driver_info_valid_uuid(self, mock_parse_driver_info,
mock_is_glance_image):
mock_is_glance_image.return_value = True
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
deploy_iso = '8a81759a-f29b-454b-8ab3-161c6ca1882c'
task.node.driver_info['ilo_deploy_iso'] = deploy_iso
ilo_boot._validate_driver_info(task)
mock_parse_driver_info.assert_called_once_with(task.node)
mock_is_glance_image.assert_called_once_with(deploy_iso)
@mock.patch.object(image_service.HttpImageService, 'validate_href',
spec_set=True, autospec=True)
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
autospec=True)
@mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test__validate_driver_info_InvalidParam(self, mock_parse_driver_info,
mock_is_glance_image,
mock_validate_href):
deploy_iso = 'http://abc.org/image/qcow2'
mock_validate_href.side_effect = exception.ImageRefValidationFailed(
image_href='http://abc.org/image/qcow2', reason='fail')
mock_is_glance_image.return_value = False
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.driver_info['ilo_deploy_iso'] = deploy_iso
self.assertRaisesRegex(exception.InvalidParameterValue,
"Virtual media boot accepts",
ilo_boot._validate_driver_info, task)
mock_parse_driver_info.assert_called_once_with(task.node)
mock_validate_href.assert_called_once_with(mock.ANY, deploy_iso)
@mock.patch.object(image_service.HttpImageService, 'validate_href',
spec_set=True, autospec=True)
@mock.patch.object(service_utils, 'is_glance_image', spec_set=True,
autospec=True)
@mock.patch.object(ilo_common, 'parse_driver_info', spec_set=True,
autospec=True)
def test__validate_driver_info_valid_url(self, mock_parse_driver_info,
mock_is_glance_image,
mock_validate_href):
deploy_iso = 'http://abc.org/image/deploy.iso'
mock_is_glance_image.return_value = False
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.driver_info['ilo_deploy_iso'] = deploy_iso
ilo_boot._validate_driver_info(task)
mock_parse_driver_info.assert_called_once_with(task.node)
mock_validate_href.assert_called_once_with(mock.ANY, deploy_iso)
mock_driver_info.assert_called_once_with(task.node)
@mock.patch.object(deploy_utils, 'validate_image_properties',
spec_set=True, autospec=True)
@ -1044,10 +817,13 @@ class IloVirtualMediaBootTestCase(test_common.BaseIloTest):
@mock.patch.object(ilo_common, 'cleanup_vmedia_boot', spec_set=True,
autospec=True)
def test_clean_up_ramdisk(self, cleanup_vmedia_mock):
@mock.patch.object(ilo_boot, '_clean_up_boot_iso_for_instance',
spec_set=True, autospec=True)
def test_clean_up_ramdisk(self, cleanup_iso_mock, cleanup_vmedia_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.driver.boot.clean_up_ramdisk(task)
cleanup_iso_mock.assert_called_once_with(task.node)
cleanup_vmedia_mock.assert_called_once_with(task)
@mock.patch.object(deploy_utils, 'is_iscsi_boot',
@ -1228,7 +1004,7 @@ class IloVirtualMediaBootTestCase(test_common.BaseIloTest):
def test_validate_rescue_no_rescue_ramdisk(self):
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaisesRegex(exception.MissingParameterValue,
'Missing.*ilo_rescue_iso',
'Either.*ilo_rescue_iso*',
task.driver.boot.validate_rescue, task)

View File

@ -19,7 +19,6 @@ import builtins
import hashlib
import io
import os
import shutil
import tempfile
from ironic_lib import utils as ironic_utils
@ -341,7 +340,7 @@ class IloCommonMethodsTestCase(BaseIloTest):
'ilo_cont', object_name, timeout)
self.assertEqual('temp-url', temp_url)
@mock.patch.object(ilo_common, 'copy_image_to_web_server',
@mock.patch.object(deploy_utils, 'copy_image_to_web_server',
spec_set=True, autospec=True)
@mock.patch.object(images, 'create_vfat_image', spec_set=True,
autospec=True)
@ -808,42 +807,6 @@ class IloCommonMethodsTestCase(BaseIloTest):
task, False)
ilo_mock_object.set_secure_boot_mode.assert_called_once_with(False)
@mock.patch.object(os, 'chmod', spec_set=True,
autospec=True)
@mock.patch.object(shutil, 'copyfile', spec_set=True,
autospec=True)
def test_copy_image_to_web_server(self, copy_mock,
chmod_mock):
CONF.deploy.http_url = "http://x.y.z.a/webserver/"
CONF.deploy.http_root = "/webserver"
expected_url = "http://x.y.z.a/webserver/image-UUID"
source = 'tmp_image_file'
destination = "image-UUID"
image_path = "/webserver/image-UUID"
actual_url = ilo_common.copy_image_to_web_server(source, destination)
self.assertEqual(expected_url, actual_url)
copy_mock.assert_called_once_with(source, image_path)
chmod_mock.assert_called_once_with(image_path, 0o644)
@mock.patch.object(os, 'chmod', spec_set=True,
autospec=True)
@mock.patch.object(shutil, 'copyfile', spec_set=True,
autospec=True)
def test_copy_image_to_web_server_fails(self, copy_mock,
chmod_mock):
CONF.deploy.http_url = "http://x.y.z.a/webserver/"
CONF.deploy.http_root = "/webserver"
source = 'tmp_image_file'
destination = "image-UUID"
image_path = "/webserver/image-UUID"
exc = exception.ImageUploadFailed('reason')
copy_mock.side_effect = exc
self.assertRaises(exception.ImageUploadFailed,
ilo_common.copy_image_to_web_server,
source, destination)
copy_mock.assert_called_once_with(source, image_path)
self.assertFalse(chmod_mock.called)
@mock.patch.object(ilo_common, 'ironic_utils', autospec=True)
def test_remove_image_from_web_server(self, utils_mock):
# | GIVEN |

View File

@ -22,6 +22,7 @@ import mock
from oslo_utils import importutils
from ironic.common import exception
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers.modules.ilo import firmware_processor as ilo_fw_processor
from ironic.tests import base
@ -481,10 +482,12 @@ class FirmwareProcessorTestCase(base.TestCase):
utils_mock.process_firmware_image.assert_called_once_with(
any_target_file, ilo_object_mock)
@mock.patch.object(deploy_utils, 'copy_image_to_web_server',
autospec=True)
@mock.patch.object(ilo_fw_processor, 'ilo_common', autospec=True)
@mock.patch.object(ilo_fw_processor, 'proliantutils_utils', autospec=True)
def test__extract_fw_from_file_doesnt_upload_firmware(
self, utils_mock, ilo_common_mock):
self, utils_mock, ilo_common_mock, copy_mock):
# | GIVEN |
node_mock = mock.MagicMock(uuid='fake_node_uuid')
any_target_file = 'any_target_file'
@ -493,7 +496,7 @@ class FirmwareProcessorTestCase(base.TestCase):
# | WHEN |
ilo_fw_processor._extract_fw_from_file(node_mock, any_target_file)
# | THEN |
ilo_common_mock.copy_image_to_web_server.assert_not_called()
copy_mock.assert_not_called()
@mock.patch.object(ilo_fw_processor, 'ilo_common', autospec=True)
@mock.patch.object(ilo_fw_processor, 'proliantutils_utils', autospec=True)
@ -513,10 +516,12 @@ class FirmwareProcessorTestCase(base.TestCase):
# | THEN |
_remove_mock.assert_called_once_with(location_obj)
@mock.patch.object(deploy_utils, 'copy_image_to_web_server',
autospec=True)
@mock.patch.object(ilo_fw_processor, 'ilo_common', autospec=True)
@mock.patch.object(ilo_fw_processor, 'proliantutils_utils', autospec=True)
def test__extract_fw_from_file_uploads_firmware_to_webserver(
self, utils_mock, ilo_common_mock):
self, utils_mock, ilo_common_mock, copy_mock):
# | GIVEN |
node_mock = mock.MagicMock(uuid='fake_node_uuid')
any_target_file = 'any_target_file'
@ -526,15 +531,17 @@ class FirmwareProcessorTestCase(base.TestCase):
# | WHEN |
ilo_fw_processor._extract_fw_from_file(node_mock, any_target_file)
# | THEN |
ilo_common_mock.copy_image_to_web_server.assert_called_once_with(
copy_mock.assert_called_once_with(
'some_location/some_fw_file', 'some_fw_file')
@mock.patch.object(deploy_utils, 'copy_image_to_web_server',
autospec=True)
@mock.patch.object(ilo_fw_processor, 'ilo_common', autospec=True)
@mock.patch.object(ilo_fw_processor, 'proliantutils_utils', autospec=True)
@mock.patch.object(ilo_fw_processor, '_remove_webserver_based_me',
autospec=True)
def test__extract_fw_from_file_sets_loc_obj_remove_to_webserver(
self, _remove_mock, utils_mock, ilo_common_mock):
self, _remove_mock, utils_mock, ilo_common_mock, copy_mock):
# | GIVEN |
node_mock = mock.MagicMock(uuid='fake_node_uuid')
any_target_file = 'any_target_file'

View File

@ -27,6 +27,7 @@ from ironic.drivers.modules import boot_mode_utils
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.redfish import boot as redfish_boot
from ironic.drivers.modules.redfish import utils as redfish_utils
from ironic.drivers.modules import virtual_media_base
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
@ -339,113 +340,10 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock_unpublish.assert_called_once_with(object_name)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_publish_image', autospec=True)
@mock.patch.object(images, 'create_boot_iso', autospec=True)
def test__prepare_iso_image_uefi(
self, mock_create_boot_iso, mock__publish_image):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.instance_info.update(deploy_boot_mode='uefi')
expected_url = 'https://a.b/c.f?e=f'
mock__publish_image.return_value = expected_url
url = task.driver.boot._prepare_iso_image(
task, 'http://kernel/img', 'http://ramdisk/img',
'http://bootloader/img', root_uuid=task.node.uuid)
object_name = 'boot-%s' % task.node.uuid
mock__publish_image.assert_called_once_with(
mock.ANY, object_name)
mock_create_boot_iso.assert_called_once_with(
mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img',
boot_mode='uefi', esp_image_href='http://bootloader/img',
configdrive_href=mock.ANY,
kernel_params='nofb nomodeset vga=normal',
root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123')
self.assertEqual(expected_url, url)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_publish_image', autospec=True)
@mock.patch.object(images, 'create_boot_iso', autospec=True)
def test__prepare_iso_image_bios(
self, mock_create_boot_iso, mock__publish_image):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
expected_url = 'https://a.b/c.f?e=f'
mock__publish_image.return_value = expected_url
url = task.driver.boot._prepare_iso_image(
task, 'http://kernel/img', 'http://ramdisk/img',
bootloader_href=None, root_uuid=task.node.uuid)
object_name = 'boot-%s' % task.node.uuid
mock__publish_image.assert_called_once_with(
mock.ANY, object_name)
mock_create_boot_iso.assert_called_once_with(
mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img',
boot_mode=None, esp_image_href=None,
configdrive_href=mock.ANY,
kernel_params='nofb nomodeset vga=normal',
root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123')
self.assertEqual(expected_url, url)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_publish_image', autospec=True)
@mock.patch.object(images, 'create_boot_iso', autospec=True)
def test__prepare_iso_image_kernel_params(
self, mock_create_boot_iso, mock__publish_image):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
kernel_params = 'network-config=base64-cloudinit-blob'
task.node.instance_info.update(kernel_append_params=kernel_params)
task.driver.boot._prepare_iso_image(
task, 'http://kernel/img', 'http://ramdisk/img',
bootloader_href=None, root_uuid=task.node.uuid)
mock_create_boot_iso.assert_called_once_with(
mock.ANY, mock.ANY, 'http://kernel/img', 'http://ramdisk/img',
boot_mode=None, esp_image_href=None,
configdrive_href=mock.ANY,
kernel_params=kernel_params,
root_uuid='1be26c0b-03f2-4d2e-ae87-c02d7f33c123')
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_prepare_iso_image', autospec=True)
def test__prepare_deploy_iso(self, mock__prepare_iso_image):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.driver_info.update(
{'deploy_kernel': 'kernel',
'deploy_ramdisk': 'ramdisk',
'bootloader': 'bootloader'}
)
task.node.instance_info.update(deploy_boot_mode='uefi')
task.driver.boot._prepare_deploy_iso(task, {}, 'deploy')
mock__prepare_iso_image.assert_called_once_with(
mock.ANY, 'kernel', 'ramdisk', 'bootloader', params={})
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_prepare_iso_image', autospec=True)
@mock.patch.object(virtual_media_base, 'prepare_iso_image', autospec=True)
@mock.patch.object(images, 'create_boot_iso', autospec=True)
def test__prepare_boot_iso(self, mock_create_boot_iso,
mock__prepare_iso_image):
mock_prepare_iso_image):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.driver_info.update(
@ -462,9 +360,11 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
task.driver.boot._prepare_boot_iso(
task, root_uuid=task.node.uuid)
mock__prepare_iso_image.assert_called_once_with(
mock_prepare_iso_image.assert_called_once_with(
mock.ANY, 'http://kernel/img', 'http://ramdisk/img',
'bootloader', root_uuid=task.node.uuid)
bootloader_href='bootloader', root_uuid=task.node.uuid,
timeout=900, use_web_server=False,
container='ironic_redfish_container')
@mock.patch.object(redfish_utils, 'parse_driver_info', autospec=True)
@mock.patch.object(deploy_utils, 'validate_image_properties',
@ -557,8 +457,8 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(redfish_boot.manager_utils, 'node_set_boot_device',
autospec=True)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_prepare_deploy_iso', autospec=True)
@mock.patch.object(virtual_media_base, 'prepare_deploy_iso',
autospec=True)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_eject_vmedia', autospec=True)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
@ -571,14 +471,14 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
def test_prepare_ramdisk_with_params(
self, mock_boot_mode_utils, mock_node_power_action,
mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia,
mock__prepare_deploy_iso, mock_node_set_boot_device):
mock_prepare_deploy_iso, mock_node_set_boot_device):
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
task.node.provision_state = states.DEPLOYING
mock__parse_driver_info.return_value = {}
mock__prepare_deploy_iso.return_value = 'image-url'
mock_prepare_deploy_iso.return_value = 'image-url'
task.driver.boot.prepare_ramdisk(task, {})
@ -597,8 +497,9 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
'ipa-debug': '1',
}
mock__prepare_deploy_iso.assert_called_once_with(
task, expected_params, 'deploy')
mock_prepare_deploy_iso.assert_called_once_with(
task, expected_params, 'deploy', {},
use_web_server=False, container='ironic_redfish_container')
mock_node_set_boot_device.assert_called_once_with(
task, boot_devices.CDROM, False)
@ -607,8 +508,8 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
@mock.patch.object(redfish_boot.manager_utils, 'node_set_boot_device',
autospec=True)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_prepare_deploy_iso', autospec=True)
@mock.patch.object(virtual_media_base, 'prepare_deploy_iso',
autospec=True)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_eject_vmedia', autospec=True)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
@ -621,14 +522,14 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
def test_prepare_ramdisk_no_debug(
self, mock_boot_mode_utils, mock_node_power_action,
mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia,
mock__prepare_deploy_iso, mock_node_set_boot_device):
mock_prepare_deploy_iso, mock_node_set_boot_device):
self.config(debug=False)
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
task.node.provision_state = states.DEPLOYING
mock__parse_driver_info.return_value = {}
mock__prepare_deploy_iso.return_value = 'image-url'
mock_prepare_deploy_iso.return_value = 'image-url'
task.driver.boot.prepare_ramdisk(task, {})
@ -646,8 +547,9 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
'ipa-agent-token': mock.ANY,
}
mock__prepare_deploy_iso.assert_called_once_with(
task, expected_params, 'deploy')
mock_prepare_deploy_iso.assert_called_once_with(
task, expected_params, 'deploy', {},
use_web_server=False, container='ironic_redfish_container')
mock_node_set_boot_device.assert_called_once_with(
task, boot_devices.CDROM, False)
@ -658,8 +560,8 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
autospec=True)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_prepare_floppy_image', autospec=True)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_prepare_deploy_iso', autospec=True)
@mock.patch.object(virtual_media_base, 'prepare_deploy_iso',
autospec=True)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
'_has_vmedia_device', autospec=True)
@mock.patch.object(redfish_boot.RedfishVirtualMediaBoot,
@ -674,7 +576,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
def test_prepare_ramdisk_with_floppy(
self, mock_boot_mode_utils, mock_node_power_action,
mock__parse_driver_info, mock__insert_vmedia, mock__eject_vmedia,
mock__has_vmedia_device, mock__prepare_deploy_iso,
mock__has_vmedia_device, mock_prepare_deploy_iso,
mock__prepare_floppy_image, mock_node_set_boot_device):
with task_manager.acquire(self.context, self.node.uuid,
@ -687,7 +589,7 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
mock__has_vmedia_device.return_value = True
mock__prepare_floppy_image.return_value = 'floppy-image-url'
mock__prepare_deploy_iso.return_value = 'cd-image-url'
mock_prepare_deploy_iso.return_value = 'cd-image-url'
task.driver.boot.prepare_ramdisk(task, {})
@ -720,8 +622,11 @@ class RedfishVirtualMediaBootTestCase(db_base.DbTestCase):
'ipa-agent-token': mock.ANY,
}
mock__prepare_deploy_iso.assert_called_once_with(
task, expected_params, 'deploy')
expected_d_info = {'config_via_floppy': True}
mock_prepare_deploy_iso.assert_called_once_with(
task, expected_params, 'deploy', expected_d_info,
use_web_server=False, container='ironic_redfish_container')
mock_node_set_boot_device.assert_called_once_with(
task, boot_devices.CDROM, False)

View File

@ -15,6 +15,7 @@
# under the License.
import os
import shutil
import tempfile
import fixtures
@ -1241,6 +1242,38 @@ class AgentMethodsTestCase(db_base.DbTestCase):
self.assertTrue(
utils.direct_deploy_should_convert_raw_image(self.node))
@mock.patch.object(shutil, 'copyfile', spec_set=True,
autospec=True)
def test_copy_image_to_web_server(self, copy_mock):
cfg.CONF.deploy.http_url = "http://x.y.z.a/webserver/"
cfg.CONF.deploy.http_root = "/webserver"
expected_url = "http://x.y.z.a/webserver/image-UUID"
source = 'tmp_image_file'
destination = "image-UUID"
image_path = "/webserver/image-UUID"
actual_url = utils.copy_image_to_web_server(source, destination)
self.assertEqual(expected_url, actual_url)
copy_mock.assert_called_once_with(source, image_path)
@mock.patch.object(os, 'chmod', spec_set=True,
autospec=True)
@mock.patch.object(shutil, 'copyfile', spec_set=True,
autospec=True)
def test_copy_image_to_web_server_fails(self, copy_mock,
chmod_mock):
cfg.CONF.deploy.http_url = "http://x.y.z.a/webserver/"
cfg.CONF.deploy.http_root = "/webserver"
source = 'tmp_image_file'
destination = "image-UUID"
image_path = "/webserver/image-UUID"
exc = exception.ImageUploadFailed('reason')
copy_mock.side_effect = exc
self.assertRaises(exception.ImageUploadFailed,
utils.copy_image_to_web_server,
source, destination)
copy_mock.assert_called_once_with(source, image_path)
self.assertFalse(chmod_mock.called)
class ValidateImagePropertiesTestCase(db_base.DbTestCase):

View File

@ -0,0 +1,232 @@
# 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.
import tempfile
import mock
from oslo_config import cfg
import six
from ironic.common import images
from ironic.common import swift
from ironic.conductor import task_manager
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules import virtual_media_base
from ironic.drivers import utils as driver_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 object_utils
if six.PY3:
import io
file = io.BytesIO
INFO_DICT = db_utils.get_test_redfish_info()
CONF = cfg.CONF
class VirtualMediaCommonMethodsTestCase(db_base.DbTestCase):
def setUp(self):
super(VirtualMediaCommonMethodsTestCase, self).setUp()
self.config(enabled_hardware_types=['ilo', 'fake-hardware'],
enabled_boot_interfaces=['ilo-pxe', 'ilo-virtual-media',
'fake'],
enabled_bios_interfaces=['ilo', 'no-bios'],
enabled_power_interfaces=['ilo', 'fake'],
enabled_management_interfaces=['ilo', 'fake'],
enabled_inspect_interfaces=['ilo', 'fake', 'no-inspect'],
enabled_console_interfaces=['ilo', 'fake', 'no-console'],
enabled_vendor_interfaces=['ilo', 'fake', 'no-vendor'])
self.node = object_utils.create_test_node(
self.context, boot_interface='ilo-virtual-media',
deploy_interface='direct')
def test_get_iso_image_name(self):
boot_iso_actual = virtual_media_base.get_iso_image_name(self.node)
boot_iso_expected = "boot-%s" % self.node.uuid
self.assertEqual(boot_iso_expected, boot_iso_actual)
@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(virtual_media_base, 'get_iso_image_name',
spec_set=True, autospec=True)
@mock.patch.object(driver_utils, 'get_node_capability', spec_set=True,
autospec=True)
def test__prepare_iso_image_uefi(self, capability_mock,
iso_image_name_mock, swift_api_mock,
create_boot_iso_mock, tempfile_mock):
CONF.ilo.swift_ilo_container = 'ilo-cont'
CONF.ilo.use_web_server_for_images = False
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
iso_image_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 = virtual_media_base.prepare_iso_image(
task, 'kernel_uuid', 'ramdisk_uuid',
deploy_iso_href='deploy_iso_uuid',
bootloader_href='bootloader_uuid',
root_uuid='root-uuid',
kernel_params='kernel-params',
timeout=None,
container=CONF.ilo.swift_ilo_container,
use_web_server=CONF.ilo.use_web_server_for_images)
iso_image_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_href='deploy_iso_uuid',
esp_image_href='bootloader_uuid',
root_uuid='root-uuid',
kernel_params='kernel-params',
boot_mode='uefi')
swift_obj_mock.create_object.assert_called_once_with(
'ilo-cont', 'abcdef', 'tmpfile', None)
boot_iso_expected = 'swift:abcdef'
self.assertEqual(boot_iso_expected, boot_iso_actual)
@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(virtual_media_base, 'get_iso_image_name',
spec_set=True, autospec=True)
@mock.patch.object(driver_utils, 'get_node_capability', spec_set=True,
autospec=True)
def test__prepare_iso_image_bios(self, capability_mock,
iso_image_name_mock, swift_api_mock,
create_boot_iso_mock, tempfile_mock):
CONF.ilo.swift_ilo_container = 'ilo-cont'
CONF.ilo.use_web_server_for_images = False
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
iso_image_name_mock.return_value = 'abcdef'
create_boot_iso_mock.return_value = '/path/to/boot-iso'
capability_mock.return_value = 'bios'
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
boot_iso_actual = virtual_media_base.prepare_iso_image(
task, 'kernel_uuid', 'ramdisk_uuid',
deploy_iso_href='deploy_iso_uuid',
root_uuid='root-uuid',
kernel_params='kernel-params',
timeout=None,
container=CONF.ilo.swift_ilo_container,
use_web_server=CONF.ilo.use_web_server_for_images)
iso_image_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_href='deploy_iso_uuid',
esp_image_href=None,
root_uuid='root-uuid',
kernel_params='kernel-params',
boot_mode='bios')
swift_obj_mock.create_object.assert_called_once_with(
'ilo-cont', 'abcdef', 'tmpfile', None)
boot_iso_expected = 'swift:abcdef'
self.assertEqual(boot_iso_expected, boot_iso_actual)
@mock.patch.object(deploy_utils, '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(virtual_media_base, 'get_iso_image_name',
spec_set=True, autospec=True)
@mock.patch.object(driver_utils, 'get_node_capability', spec_set=True,
autospec=True)
def test__prepare_iso_image_use_webserver(self, capability_mock,
iso_image_name_mock,
create_boot_iso_mock,
tempfile_mock, copy_file_mock):
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"
iso_image_name_mock.return_value = 'new_boot_iso'
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/new_boot_iso"
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
boot_iso_actual = virtual_media_base.prepare_iso_image(
task, kernel_href, ramdisk_href,
deploy_iso_href='deploy_iso_uuid',
bootloader_href='bootloader_uuid',
root_uuid='root-uuid',
kernel_params='kernel-params',
use_web_server=CONF.ilo.use_web_server_for_images)
iso_image_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_href='deploy_iso_uuid',
esp_image_href='bootloader_uuid',
root_uuid='root-uuid',
kernel_params='kernel-params',
boot_mode='uefi')
boot_iso_expected = 'http://10.10.1.30/httpboot/new_boot_iso'
self.assertEqual(boot_iso_expected, boot_iso_actual)
copy_file_mock.assert_called_once_with(fileobj_mock.name,
'new_boot_iso')
@mock.patch.object(virtual_media_base, 'prepare_iso_image', spec_set=True,
autospec=True)
def test_prepare_deploy_iso(self, prepare_iso_mock):
driver_info = {'deploy_kernel': 'kernel', 'deploy_ramdisk': 'ramdisk',
'bootloader': 'bootloader'}
CONF.pxe.pxe_append_params = 'kernel-params'
timeout = None
container = 'container'
prepare_iso_mock.return_value = (
'swift:boot-b5451849-e088-4a4c-aa5f-4d97b3371dec')
with task_manager.acquire(self.context, self.node.uuid,
shared=False) as task:
deploy_iso_actual = virtual_media_base.prepare_deploy_iso(
task, {}, 'deploy', driver_info, use_web_server=False,
container=container)
prepare_iso_mock.assert_called_once_with(
task, 'kernel', 'ramdisk', bootloader_href='bootloader',
kernel_params=CONF.pxe.pxe_append_params, timeout=timeout,
use_web_server=False, container='container')
deploy_iso_expected = (
'swift:boot-b5451849-e088-4a4c-aa5f-4d97b3371dec')
self.assertEqual(deploy_iso_expected, deploy_iso_actual)

View File

@ -0,0 +1,8 @@
features:
- |
Add functionality to perform virtual media boot without
user-built deploy/rescue/boot ISO images for hardware type ilo.
Instead, ironic will build necessary images out of common
kernel/ramdisk pair (though user needs to provide ESP image).
User provided deploy/rescue/boot ISO images are
also supported.