"Fossies" - the Fresh Open Source Software Archive

Member "ironic-16.0.3/ironic/common/images.py" (18 Jan 2021, 26269 Bytes) of package /linux/misc/openstack/ironic-16.0.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "images.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 16.0.2_vs_16.0.3.

    1 # Copyright 2010 United States Government as represented by the
    2 # Administrator of the National Aeronautics and Space Administration.
    3 # All Rights Reserved.
    4 # Copyright (c) 2010 Citrix Systems, Inc.
    5 #
    6 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    7 #    not use this file except in compliance with the License. You may obtain
    8 #    a copy of the License at
    9 #
   10 #         http://www.apache.org/licenses/LICENSE-2.0
   11 #
   12 #    Unless required by applicable law or agreed to in writing, software
   13 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   14 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   15 #    License for the specific language governing permissions and limitations
   16 #    under the License.
   17 
   18 """
   19 Handling of VM disk images.
   20 """
   21 
   22 import os
   23 import shutil
   24 
   25 from ironic_lib import disk_utils
   26 from ironic_lib import utils as ironic_utils
   27 from oslo_concurrency import processutils
   28 from oslo_log import log as logging
   29 from oslo_utils import fileutils
   30 
   31 from ironic.common import exception
   32 from ironic.common.glance_service import service_utils as glance_utils
   33 from ironic.common.i18n import _
   34 from ironic.common import image_service as service
   35 from ironic.common import utils
   36 from ironic.conf import CONF
   37 
   38 LOG = logging.getLogger(__name__)
   39 
   40 
   41 def _create_root_fs(root_directory, files_info):
   42     """Creates a filesystem root in given directory.
   43 
   44     Given a mapping of absolute path of files to their relative paths
   45     within the filesystem, this method copies the files to their
   46     destination.
   47 
   48     :param root_directory: the filesystem root directory.
   49     :param files_info: A dict containing absolute path of file to be copied
   50                        or its content as bytes -> relative path within
   51                        the vfat image. For example::
   52                         {
   53                         '/absolute/path/to/file': 'relative/path/within/root',
   54                         b'{"some": "json"}': 'another/relative/path'
   55                         ...
   56                         }
   57     :raises: OSError, if creation of any directory failed.
   58     :raises: IOError, if copying any of the files failed.
   59     """
   60     for src_file, path in files_info.items():
   61         target_file = os.path.join(root_directory, path)
   62         dirname = os.path.dirname(target_file)
   63         if dirname:
   64             os.makedirs(dirname, exist_ok=True)
   65 
   66         if isinstance(src_file, bytes):
   67             with open(target_file, 'wb') as fp:
   68                 fp.write(src_file)
   69         else:
   70             shutil.copyfile(src_file, target_file)
   71 
   72 
   73 def _umount_without_raise(mount_dir):
   74     """Helper method to umount without raise."""
   75     try:
   76         utils.umount(mount_dir)
   77     except processutils.ProcessExecutionError:
   78         pass
   79 
   80 
   81 def create_vfat_image(output_file, files_info=None, parameters=None,
   82                       parameters_file='parameters.txt', fs_size_kib=100):
   83     """Creates the fat fs image on the desired file.
   84 
   85     This method copies the given files to a root directory (optional),
   86     writes the parameters specified to the parameters file within the
   87     root directory (optional), and then creates a vfat image of the root
   88     directory.
   89 
   90     :param output_file: The path to the file where the fat fs image needs
   91                         to be created.
   92     :param files_info: A dict containing absolute path of file to be copied
   93                        -> relative path within the vfat image. For example::
   94 
   95                         {
   96                         '/absolute/path/to/file' -> 'relative/path/within/root'
   97                         ...
   98                         }
   99     :param parameters: A dict containing key-value pairs of parameters.
  100     :param parameters_file: The filename for the parameters file.
  101     :param fs_size_kib: size of the vfat filesystem in KiB.
  102     :raises: ImageCreationFailed, if image creation failed while doing any
  103              of filesystem manipulation activities like creating dirs,
  104              mounting, creating filesystem, copying files, etc.
  105     """
  106     try:
  107         ironic_utils.dd('/dev/zero', output_file, 'count=1',
  108                         "bs=%dKiB" % fs_size_kib)
  109     except processutils.ProcessExecutionError as e:
  110         raise exception.ImageCreationFailed(image_type='vfat', error=e)
  111 
  112     with utils.tempdir() as tmpdir:
  113 
  114         try:
  115             # The label helps ramdisks to find the partition containing
  116             # the parameters (by using /dev/disk/by-label/ir-vfd-dev).
  117             # NOTE: FAT filesystem label can be up to 11 characters long.
  118             ironic_utils.mkfs('vfat', output_file, label="ir-vfd-dev")
  119             utils.mount(output_file, tmpdir, '-o', 'umask=0')
  120         except processutils.ProcessExecutionError as e:
  121             raise exception.ImageCreationFailed(image_type='vfat', error=e)
  122 
  123         try:
  124             if files_info:
  125                 _create_root_fs(tmpdir, files_info)
  126 
  127             if parameters:
  128                 parameters_file = os.path.join(tmpdir, parameters_file)
  129                 params_list = ['%(key)s=%(val)s' % {'key': k, 'val': v}
  130                                for k, v in parameters.items()]
  131                 file_contents = '\n'.join(params_list)
  132                 utils.write_to_file(parameters_file, file_contents)
  133 
  134         except Exception as e:
  135             LOG.exception("vfat image creation failed. Error: %s", e)
  136             raise exception.ImageCreationFailed(image_type='vfat', error=e)
  137 
  138         finally:
  139             try:
  140                 utils.umount(tmpdir)
  141             except processutils.ProcessExecutionError as e:
  142                 raise exception.ImageCreationFailed(image_type='vfat', error=e)
  143 
  144 
  145 def _generate_cfg(kernel_params, template, options):
  146     """Generates a isolinux or grub configuration file.
  147 
  148     Given a given a list of strings containing kernel parameters, this method
  149     returns the kernel cmdline string.
  150     :param kernel_params: a list of strings(each element being a string like
  151         'K=V' or 'K' or combination of them like 'K1=V1 K2 K3=V3') to be added
  152         as the kernel cmdline.
  153     :param template: the path of the config template file.
  154     :param options: a dictionary of keywords which need to be replaced in
  155                     template file to generate a proper config file.
  156     :returns: a string containing the contents of the isolinux configuration
  157         file.
  158     """
  159     options.update({'kernel_params': ' '.join(kernel_params or [])})
  160     return utils.render_template(template, options)
  161 
  162 
  163 def _label(files_info):
  164     """Get a suitable label for the files.
  165 
  166     Returns "config-2" if the openstack metadata is present.
  167     """
  168     if any(x.startswith('openstack/') for x in files_info.values()):
  169         return 'config-2'
  170     else:
  171         return 'VMEDIA_BOOT_ISO'
  172 
  173 
  174 def create_isolinux_image_for_bios(
  175         output_file, kernel, ramdisk, kernel_params=None, inject_files=None):
  176     """Creates an isolinux image on the specified file.
  177 
  178     Copies the provided kernel, ramdisk to a directory, generates the isolinux
  179     configuration file using the kernel parameters provided, and then generates
  180     a bootable ISO image.
  181 
  182     :param output_file: the path to the file where the iso image needs to be
  183         created.
  184     :param kernel: the kernel to use.
  185     :param ramdisk: the ramdisk to use.
  186     :param kernel_params: a list of strings(each element being a string like
  187         'K=V' or 'K' or combination of them like 'K1=V1,K2,...') to be added
  188         as the kernel cmdline.
  189     :param inject_files: Mapping of local source file paths to their location
  190         on the final ISO image.
  191     :raises: ImageCreationFailed, if image creation failed while copying files
  192         or while running command to generate iso.
  193     """
  194     ISOLINUX_BIN = 'isolinux/isolinux.bin'
  195     ISOLINUX_CFG = 'isolinux/isolinux.cfg'
  196     LDLINUX_SRC_DIRS = ['/usr/lib/syslinux/modules/bios',
  197                         '/usr/share/syslinux']
  198     LDLINUX_BIN = 'isolinux/ldlinux.c32'
  199 
  200     options = {'kernel': '/vmlinuz', 'ramdisk': '/initrd'}
  201 
  202     with utils.tempdir() as tmpdir:
  203         files_info = {
  204             kernel: 'vmlinuz',
  205             ramdisk: 'initrd',
  206             CONF.isolinux_bin: ISOLINUX_BIN,
  207         }
  208         if inject_files:
  209             files_info.update(inject_files)
  210 
  211         # ldlinux.c32 is required for syslinux 5.0 or later.
  212         if CONF.ldlinux_c32:
  213             ldlinux_src = CONF.ldlinux_c32
  214         else:
  215             for directory in LDLINUX_SRC_DIRS:
  216                 ldlinux_src = os.path.join(directory, 'ldlinux.c32')
  217                 if os.path.isfile(ldlinux_src):
  218                     break
  219             else:
  220                 ldlinux_src = None
  221         if ldlinux_src:
  222             files_info[ldlinux_src] = LDLINUX_BIN
  223 
  224         try:
  225             _create_root_fs(tmpdir, files_info)
  226 
  227         except EnvironmentError as e:
  228             LOG.exception("Creating the filesystem root failed.")
  229             raise exception.ImageCreationFailed(image_type='iso', error=e)
  230 
  231         cfg = _generate_cfg(kernel_params,
  232                             CONF.isolinux_config_template, options)
  233 
  234         isolinux_cfg = os.path.join(tmpdir, ISOLINUX_CFG)
  235         utils.write_to_file(isolinux_cfg, cfg)
  236 
  237         try:
  238             utils.execute('mkisofs', '-r', '-V', _label(files_info),
  239                           '-cache-inodes', '-J', '-l', '-no-emul-boot',
  240                           '-boot-load-size', '4', '-boot-info-table',
  241                           '-b', ISOLINUX_BIN, '-o', output_file, tmpdir)
  242         except processutils.ProcessExecutionError as e:
  243             LOG.exception("Creating ISO image failed.")
  244             raise exception.ImageCreationFailed(image_type='iso', error=e)
  245 
  246 
  247 def create_esp_image_for_uefi(
  248         output_file, kernel, ramdisk, deploy_iso=None, esp_image=None,
  249         kernel_params=None, inject_files=None):
  250     """Creates an ESP image on the specified file.
  251 
  252     Copies the provided kernel, ramdisk and EFI system partition image (ESP) to
  253     a directory, generates the grub configuration file using kernel parameters
  254     and then generates a bootable ISO image for UEFI.
  255 
  256     :param output_file: the path to the file where the iso image needs to be
  257         created.
  258     :param kernel: the kernel to use.
  259     :param ramdisk: the ramdisk to use.
  260     :param deploy_iso: deploy ISO image to extract EFI system partition image
  261         from. If not specified, the `esp_image` option is required.
  262     :param esp_image: FAT12/16/32-formatted EFI system partition image
  263         containing the EFI boot loader (e.g. GRUB2) for each hardware
  264         architecture to boot. This image will be embedded into the ISO image.
  265         If not specified, the `deploy_iso` option is required.
  266     :param kernel_params: a list of strings(each element being a string like
  267         'K=V' or 'K' or combination of them like 'K1=V1,K2,...') to be added
  268         as the kernel cmdline.
  269     :param inject_files: Mapping of local source file paths to their location
  270         on the final ISO image.
  271     :raises: ImageCreationFailed, if image creation failed while copying files
  272         or while running command to generate iso.
  273     """
  274     EFIBOOT_LOCATION = 'boot/grub/efiboot.img'
  275 
  276     grub_options = {'linux': '/vmlinuz', 'initrd': '/initrd'}
  277 
  278     with utils.tempdir() as tmpdir:
  279         files_info = {
  280             kernel: 'vmlinuz',
  281             ramdisk: 'initrd',
  282         }
  283         if inject_files:
  284             files_info.update(inject_files)
  285 
  286         with utils.tempdir() as mountdir:
  287             # Open the deploy iso used to initiate deploy and copy the
  288             # efiboot.img i.e. boot loader to the current temporary
  289             # directory.
  290             if deploy_iso and not esp_image:
  291                 uefi_path_info, e_img_rel_path, grub_rel_path = (
  292                     _mount_deploy_iso(deploy_iso, mountdir))
  293 
  294                 grub_cfg = os.path.join(tmpdir, grub_rel_path)
  295 
  296             # Use ELF boot loader provided
  297             elif esp_image and not deploy_iso:
  298                 e_img_rel_path = EFIBOOT_LOCATION
  299                 grub_rel_path = CONF.grub_config_path.lstrip(' ' + os.sep)
  300                 grub_cfg = os.path.join(tmpdir, grub_rel_path)
  301 
  302                 # Create an empty grub config file by copying /dev/null.
  303                 # This is to avoid write failures when actual write of
  304                 # config file happens. Write failures are caused if grub
  305                 # config path does not exist on root file system.
  306                 uefi_path_info = {
  307                     esp_image: e_img_rel_path,
  308                     '/dev/null': grub_rel_path
  309                 }
  310 
  311             else:
  312                 msg = _('Neither deploy ISO nor ESP image configured or '
  313                         'both of them configured')
  314                 raise exception.ImageCreationFailed(
  315                     image_type='iso', error=msg)
  316 
  317             files_info.update(uefi_path_info)
  318 
  319             try:
  320                 _create_root_fs(tmpdir, files_info)
  321 
  322             except EnvironmentError as e:
  323                 LOG.exception("Creating the filesystem root failed.")
  324                 raise exception.ImageCreationFailed(
  325                     image_type='iso', error=e)
  326 
  327             finally:
  328                 if deploy_iso:
  329                     _umount_without_raise(mountdir)
  330 
  331         # Generate and copy grub config file.
  332         grub_conf = _generate_cfg(kernel_params,
  333                                   CONF.grub_config_template, grub_options)
  334         utils.write_to_file(grub_cfg, grub_conf)
  335 
  336         # Create the boot_iso.
  337         try:
  338             utils.execute('mkisofs', '-r', '-V', _label(files_info),
  339                           '-l', '-e', e_img_rel_path, '-no-emul-boot',
  340                           '-o', output_file, tmpdir)
  341 
  342         except processutils.ProcessExecutionError as e:
  343             LOG.exception("Creating ISO image failed.")
  344             raise exception.ImageCreationFailed(image_type='iso', error=e)
  345 
  346 
  347 def fetch(context, image_href, path, force_raw=False):
  348     # TODO(vish): Improve context handling and add owner and auth data
  349     #             when it is added to glance.  Right now there is no
  350     #             auth checking in glance, so we assume that access was
  351     #             checked before we got here.
  352     image_service = service.get_image_service(image_href,
  353                                               context=context)
  354     LOG.debug("Using %(image_service)s to download image %(image_href)s.",
  355               {'image_service': image_service.__class__,
  356                'image_href': image_href})
  357 
  358     with fileutils.remove_path_on_error(path):
  359         with open(path, "wb") as image_file:
  360             image_service.download(image_href, image_file)
  361 
  362     if force_raw:
  363         image_to_raw(image_href, path, "%s.part" % path)
  364 
  365 
  366 def get_source_format(image_href, path):
  367     data = disk_utils.qemu_img_info(path)
  368 
  369     fmt = data.file_format
  370     if fmt is None:
  371         raise exception.ImageUnacceptable(
  372             reason=_("'qemu-img info' parsing failed."),
  373             image_id=image_href)
  374 
  375     backing_file = data.backing_file
  376     if backing_file is not None:
  377         raise exception.ImageUnacceptable(
  378             image_id=image_href,
  379             reason=_("fmt=%(fmt)s backed by: %(backing_file)s") %
  380             {'fmt': fmt, 'backing_file': backing_file})
  381 
  382     return fmt
  383 
  384 
  385 def force_raw_will_convert(image_href, path_tmp):
  386     with fileutils.remove_path_on_error(path_tmp):
  387         fmt = get_source_format(image_href, path_tmp)
  388     if fmt != "raw":
  389         return True
  390     return False
  391 
  392 
  393 def image_to_raw(image_href, path, path_tmp):
  394     with fileutils.remove_path_on_error(path_tmp):
  395         fmt = get_source_format(image_href, path_tmp)
  396 
  397         if fmt != "raw":
  398             staged = "%s.converted" % path
  399             LOG.debug("%(image)s was %(format)s, converting to raw",
  400                       {'image': image_href, 'format': fmt})
  401             with fileutils.remove_path_on_error(staged):
  402                 disk_utils.convert_image(path_tmp, staged, 'raw')
  403                 os.unlink(path_tmp)
  404 
  405                 data = disk_utils.qemu_img_info(staged)
  406                 if data.file_format != "raw":
  407                     raise exception.ImageConvertFailed(
  408                         image_id=image_href,
  409                         reason=_("Converted to raw, but format is "
  410                                  "now %s") % data.file_format)
  411 
  412                 os.rename(staged, path)
  413         else:
  414             os.rename(path_tmp, path)
  415 
  416 
  417 def image_show(context, image_href, image_service=None):
  418     if image_service is None:
  419         image_service = service.get_image_service(image_href, context=context)
  420     return image_service.show(image_href)
  421 
  422 
  423 def download_size(context, image_href, image_service=None):
  424     return image_show(context, image_href, image_service)['size']
  425 
  426 
  427 def converted_size(path):
  428     """Get size of converted raw image.
  429 
  430     The size of image converted to raw format can be growing up to the virtual
  431     size of the image.
  432 
  433     :param path: path to the image file.
  434     :returns: virtual size of the image or 0 if conversion not needed.
  435 
  436     """
  437     data = disk_utils.qemu_img_info(path)
  438     return data.virtual_size
  439 
  440 
  441 def get_image_properties(context, image_href, properties="all"):
  442     """Returns the values of several properties of an image
  443 
  444     :param context: context
  445     :param image_href: href of the image
  446     :param properties: the properties whose values are required.
  447         This argument is optional, default value is "all", so if not specified
  448         all properties will be returned.
  449     :returns: a dict of the values of the properties. A property not on the
  450         glance metadata will have a value of None.
  451     """
  452     img_service = service.get_image_service(image_href, context=context)
  453     iproperties = img_service.show(image_href)['properties']
  454 
  455     if properties == "all":
  456         return iproperties
  457 
  458     return {p: iproperties.get(p) for p in properties}
  459 
  460 
  461 def get_temp_url_for_glance_image(context, image_uuid):
  462     """Returns the tmp url for a glance image.
  463 
  464     :param context: context
  465     :param image_uuid: the UUID of the image in glance
  466     :returns: the tmp url for the glance image.
  467     """
  468     glance_service = service.GlanceImageService(context=context)
  469     image_properties = glance_service.show(image_uuid)
  470     LOG.debug('Got image info: %(info)s for image %(image_uuid)s.',
  471               {'info': image_properties, 'image_uuid': image_uuid})
  472     return glance_service.swift_temp_url(image_properties)
  473 
  474 
  475 def create_boot_iso(context, output_filename, kernel_href,
  476                     ramdisk_href, deploy_iso_href=None, esp_image_href=None,
  477                     root_uuid=None, kernel_params=None, boot_mode=None,
  478                     base_iso=None, inject_files=None):
  479     """Creates a bootable ISO image for a node.
  480 
  481     Given the hrefs for kernel, ramdisk, root partition's UUID and
  482     kernel cmdline arguments, this method fetches the kernel and ramdisk,
  483     and builds a bootable ISO image that can be used to boot up the
  484     baremetal node.
  485 
  486     :param context: context
  487     :param output_filename: the absolute path of the output ISO file
  488     :param kernel_href: URL or glance uuid of the kernel to use
  489     :param ramdisk_href: URL or glance uuid of the ramdisk to use
  490     :param deploy_iso_href: URL or glance UUID of the deploy ISO image
  491         to extract EFI system partition image. If not specified,
  492         the `esp_image_href` option must be present if UEFI-bootable
  493         ISO is desired.
  494     :param esp_image_href: URL or glance UUID of FAT12/16/32-formatted EFI
  495         system partition image containing the EFI boot loader (e.g. GRUB2)
  496         for each hardware architecture to boot. This image will be written
  497         onto the ISO image. If not specified, the `deploy_iso_href` option
  498         is only required for building UEFI-bootable ISO.
  499     :param kernel_params: a string containing whitespace separated values
  500         kernel cmdline arguments of the form K=V or K (optional).
  501     :boot_mode: the boot mode in which the deploy is to happen.
  502     :param base_iso: URL or glance UUID of a to be used as an override of
  503         what should be retrieved for to use, instead of building an ISO
  504         bootable ramdisk.
  505     :param inject_files: Mapping of local source file paths to their location
  506         on the final ISO image.
  507     :raises: ImageCreationFailed, if creating boot ISO failed.
  508     """
  509     with utils.tempdir() as tmpdir:
  510         if base_iso:
  511             # NOTE(TheJulia): Eventually we want to use the creation method
  512             # to perform the massaging of the image, because oddly enough
  513             # we need to do all the same basic things, just a little
  514             # differently.
  515             fetch(context, base_iso, output_filename)
  516             # Temporary, return to the caller until we support the combined
  517             # operation.
  518             return
  519         else:
  520             kernel_path = os.path.join(tmpdir, kernel_href.split('/')[-1])
  521             ramdisk_path = os.path.join(tmpdir, ramdisk_href.split('/')[-1])
  522             fetch(context, kernel_href, kernel_path)
  523             fetch(context, ramdisk_href, ramdisk_path)
  524 
  525         params = []
  526         if root_uuid:
  527             params.append('root=UUID=%s' % root_uuid)
  528         if kernel_params:
  529             params.append(kernel_params)
  530 
  531         if boot_mode == 'uefi':
  532 
  533             deploy_iso_path = esp_image_path = None
  534 
  535             if deploy_iso_href:
  536                 deploy_iso_path = os.path.join(
  537                     tmpdir, deploy_iso_href.split('/')[-1])
  538                 fetch(context, deploy_iso_href, deploy_iso_path)
  539 
  540             elif esp_image_href:
  541                 esp_image_path = os.path.join(
  542                     tmpdir, esp_image_href.split('/')[-1])
  543                 fetch(context, esp_image_href, esp_image_path)
  544 
  545             elif CONF.esp_image:
  546                 esp_image_path = CONF.esp_image
  547             # TODO(TheJulia): we should opportunisticly try to make bios
  548             # bootable and UEFI. In other words, collapse a lot of this
  549             # path since they are not mutually exclusive.
  550             # UEFI boot mode, but Network iPXE -> ISO means bios bootable
  551             # contents are still required.
  552             create_esp_image_for_uefi(
  553                 output_filename, kernel_path, ramdisk_path,
  554                 deploy_iso=deploy_iso_path, esp_image=esp_image_path,
  555                 kernel_params=params, inject_files=inject_files)
  556 
  557         else:
  558             create_isolinux_image_for_bios(
  559                 output_filename, kernel_path, ramdisk_path,
  560                 kernel_params=params, inject_files=inject_files)
  561 
  562 
  563 def is_whole_disk_image(ctx, instance_info):
  564     """Find out if the image is a partition image or a whole disk image.
  565 
  566     :param ctx: an admin context
  567     :param instance_info: a node's instance info dict
  568 
  569     :returns: True for whole disk images and False for partition images
  570         and None on no image_source or Error.
  571     """
  572     image_source = instance_info.get('image_source')
  573     if not image_source:
  574         return
  575 
  576     is_whole_disk_image = False
  577     if glance_utils.is_glance_image(image_source):
  578         try:
  579             iproperties = get_image_properties(ctx, image_source)
  580         except Exception:
  581             return
  582         is_whole_disk_image = (not iproperties.get('kernel_id')
  583                                and not iproperties.get('ramdisk_id'))
  584     else:
  585         # Non glance image ref
  586         if (not instance_info.get('kernel')
  587             and not instance_info.get('ramdisk')):
  588             is_whole_disk_image = True
  589 
  590     return is_whole_disk_image
  591 
  592 
  593 def _mount_deploy_iso(deploy_iso, mountdir):
  594     """This function opens up the deploy iso used for deploy.
  595 
  596     :param deploy_iso: path to the deploy iso where its
  597                        contents are fetched to.
  598     :raises: ImageCreationFailed if mount fails.
  599     :returns: a tuple consisting of - 1. a dictionary containing
  600                                          the values as required
  601                                          by create_isolinux_image,
  602                                       2. efiboot.img relative path, and
  603                                       3. grub.cfg relative path.
  604 
  605     """
  606     e_img_rel_path = None
  607     e_img_path = None
  608     grub_rel_path = None
  609     grub_path = None
  610 
  611     try:
  612         utils.mount(deploy_iso, mountdir, '-o', 'loop')
  613     except processutils.ProcessExecutionError as e:
  614         LOG.exception("mounting the deploy iso failed.")
  615         raise exception.ImageCreationFailed(image_type='iso', error=e)
  616 
  617     try:
  618         for (dir, subdir, files) in os.walk(mountdir):
  619             if 'efiboot.img' in files:
  620                 e_img_path = os.path.join(dir, 'efiboot.img')
  621                 e_img_rel_path = os.path.relpath(e_img_path,
  622                                                  mountdir)
  623             if 'grub.cfg' in files:
  624                 grub_path = os.path.join(dir, 'grub.cfg')
  625                 grub_rel_path = os.path.relpath(grub_path,
  626                                                 mountdir)
  627     except (OSError, IOError) as e:
  628         LOG.exception("examining the deploy iso failed.")
  629         _umount_without_raise(mountdir)
  630         raise exception.ImageCreationFailed(image_type='iso', error=e)
  631 
  632     # check if the variables are assigned some values or not during
  633     # walk of the mountdir.
  634     if not (e_img_path and e_img_rel_path and grub_path and grub_rel_path):
  635         error = (_("Deploy iso didn't contain efiboot.img or grub.cfg"))
  636         _umount_without_raise(mountdir)
  637         raise exception.ImageCreationFailed(image_type='iso', error=error)
  638 
  639     uefi_path_info = {e_img_path: e_img_rel_path,
  640                       grub_path: grub_rel_path}
  641 
  642     # Returning a tuple as it makes the code simpler and clean.
  643     # uefi_path_info: is needed by the caller for _create_root_fs to create
  644     # appropriate directory structures for uefi boot iso.
  645     # grub_rel_path: is needed to copy the new grub.cfg generated using
  646     # generate_cfg() to the same directory path structure where it was
  647     # present in deploy iso. This path varies for different OS vendors.
  648     # e_img_rel_path: is required by mkisofs to generate boot iso.
  649     return uefi_path_info, e_img_rel_path, grub_rel_path