Beispiel #1
0
def prepare_node_for_deploy(task):
    """Common preparatory steps for all iLO drivers.

    This method performs common preparatory steps required for all drivers.
    1. Power off node
    2. Disables secure boot, if it is in enabled state.
    3. Updates boot_mode capability to 'uefi' if secure boot is requested.
    4. Changes boot mode of the node if secure boot is disabled currently.

    :param task: a TaskManager instance containing the node to act on.
    :raises: IloOperationError, if some operation on iLO failed.
    """
    manager_utils.node_power_action(task, states.POWER_OFF)

    # Boot mode can be changed only if secure boot is in disabled state.
    # secure boot and boot mode cannot be changed together.
    change_boot_mode = True

    # Disable secure boot on the node if it is in enabled state.
    if _disable_secure_boot(task):
        change_boot_mode = False

    if change_boot_mode:
        ilo_common.update_boot_mode(task)
    else:
        # Need to update boot mode that will be used during deploy, if one is
        # not provided.
        # Since secure boot was disabled, we are in 'uefi' boot mode.
        if boot_mode_utils.get_boot_mode_for_deploy(task.node) is None:
            instance_info = task.node.instance_info
            instance_info['deploy_boot_mode'] = 'uefi'
            task.node.instance_info = instance_info
            task.node.save()
Beispiel #2
0
def build_service_pxe_config(task,
                             instance_image_info,
                             root_uuid_or_disk_id,
                             ramdisk_boot=False,
                             ipxe_enabled=False):
    node = task.node
    pxe_config_path = get_pxe_config_file_path(node.uuid)
    # NOTE(pas-ha) if it is takeover of ACTIVE node or node performing
    # unrescue operation, first ensure that basic PXE configs and links
    # are in place before switching pxe config
    if (node.provision_state in [states.ACTIVE, states.UNRESCUING]
            and not os.path.isfile(pxe_config_path)):
        pxe_options = build_pxe_config_options(task,
                                               instance_image_info,
                                               service=True,
                                               ipxe_enabled=ipxe_enabled)
        pxe_config_template = deploy_utils.get_pxe_config_template(node)
        create_pxe_config(task,
                          pxe_options,
                          pxe_config_template,
                          ipxe_enabled=ipxe_enabled)
    iwdi = node.driver_internal_info.get('is_whole_disk_image')
    deploy_utils.switch_pxe_config(
        pxe_config_path,
        root_uuid_or_disk_id,
        boot_mode_utils.get_boot_mode_for_deploy(node),
        iwdi,
        deploy_utils.is_trusted_boot_requested(node),
        deploy_utils.is_iscsi_boot(task),
        ramdisk_boot,
        ipxe_enabled=ipxe_enabled)
Beispiel #3
0
    def _prepare_instance_pxe_config(self,
                                     task,
                                     image_info,
                                     iscsi_boot=False,
                                     ramdisk_boot=False):
        """Prepares the config file for PXE boot

        :param task: a task from TaskManager.
        :param image_info: a dict of values of instance image
            metadata to set on the configuration file.
        :param iscsi_boot: if boot is from an iSCSI volume or not.
        :param ramdisk_boot: if the boot is to a ramdisk configuration.
        :returns: None
        """

        node = task.node
        dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
        provider = dhcp_factory.DHCPFactory()
        provider.update_dhcp(task, dhcp_opts)
        pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
        if not os.path.isfile(pxe_config_path):
            pxe_options = _build_pxe_config_options(task,
                                                    image_info,
                                                    service=ramdisk_boot)
            pxe_config_template = (deploy_utils.get_pxe_config_template(node))
            pxe_utils.create_pxe_config(task, pxe_options, pxe_config_template)
        deploy_utils.switch_pxe_config(
            pxe_config_path,
            None,
            boot_mode_utils.get_boot_mode_for_deploy(node),
            False,
            iscsi_boot=iscsi_boot,
            ramdisk_boot=ramdisk_boot)
Beispiel #4
0
def prepare_node_for_deploy(task):
    """Common preparatory steps for all iLO drivers.

    This method performs common preparatory steps required for all drivers.
    1. Power off node
    2. Disables secure boot, if it is in enabled state.
    3. Updates boot_mode capability to 'uefi' if secure boot is requested.
    4. Changes boot mode of the node if secure boot is disabled currently.

    :param task: a TaskManager instance containing the node to act on.
    :raises: IloOperationError, if some operation on iLO failed.
    """
    manager_utils.node_power_action(task, states.POWER_OFF)

    # Boot mode can be changed only if secure boot is in disabled state.
    # secure boot and boot mode cannot be changed together.
    change_boot_mode = True

    # Disable secure boot on the node if it is in enabled state.
    if _disable_secure_boot(task):
        change_boot_mode = False

    if change_boot_mode:
        ilo_common.update_boot_mode(task)
    else:
        # Need to update boot mode that will be used during deploy, if one is
        # not provided.
        # Since secure boot was disabled, we are in 'uefi' boot mode.
        if boot_mode_utils.get_boot_mode_for_deploy(task.node) is None:
            instance_info = task.node.instance_info
            instance_info['deploy_boot_mode'] = 'uefi'
            task.node.instance_info = instance_info
            task.node.save()
Beispiel #5
0
def _prepare_boot_iso(task, root_uuid):
    """Prepare a boot ISO to boot the node.

    :param task: a TaskManager instance containing the node to act on.
    :param root_uuid: the uuid of the root partition.
    :raises: MissingParameterValue, if any of the required parameters are
        missing.
    :raises: InvalidParameterValue, if any of the parameters have invalid
        value.
    :raises: ImageCreationFailed, if creating boot ISO
       for BIOS boot_mode failed.
    """
    deploy_info = _parse_deploy_info(task.node)
    driver_internal_info = task.node.driver_internal_info

    # fetch boot iso
    if deploy_info.get('irmc_boot_iso'):
        boot_iso_href = deploy_info['irmc_boot_iso']
        if service_utils.is_image_href_ordinary_file_name(boot_iso_href):
            driver_internal_info['irmc_boot_iso'] = boot_iso_href
        else:
            boot_iso_filename = _get_iso_name(task.node, label='boot')
            boot_iso_fullpathname = os.path.join(
                CONF.irmc.remote_image_share_root, boot_iso_filename)
            images.fetch(task.context, boot_iso_href, boot_iso_fullpathname)

            driver_internal_info['irmc_boot_iso'] = boot_iso_filename

    # create boot iso
    else:
        image_href = deploy_info['image_source']
        image_props = ['kernel_id', 'ramdisk_id']
        image_properties = images.get_image_properties(
            task.context, image_href, image_props)
        kernel_href = (task.node.instance_info.get('kernel')
                       or image_properties['kernel_id'])
        ramdisk_href = (task.node.instance_info.get('ramdisk')
                        or image_properties['ramdisk_id'])

        deploy_iso_filename = _get_iso_name(task.node, label='deploy')
        deploy_iso = ('file://' + os.path.join(
            CONF.irmc.remote_image_share_root, deploy_iso_filename))
        boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node)
        kernel_params = CONF.pxe.pxe_append_params

        boot_iso_filename = _get_iso_name(task.node, label='boot')
        boot_iso_fullpathname = os.path.join(
            CONF.irmc.remote_image_share_root, boot_iso_filename)

        images.create_boot_iso(task.context, boot_iso_fullpathname,
                               kernel_href, ramdisk_href,
                               deploy_iso, root_uuid,
                               kernel_params, boot_mode)

        driver_internal_info['irmc_boot_iso'] = boot_iso_filename

    # save driver_internal_info['irmc_boot_iso']
    task.node.driver_internal_info = driver_internal_info
    task.node.save()
Beispiel #6
0
def _get_boot_mode(node):
    """Gets the boot mode.

    :param node: A single Node.
    :returns: A string representing the boot mode type. Defaults to 'bios'.
    """
    boot_mode = boot_mode_utils.get_boot_mode_for_deploy(node)
    if boot_mode:
        return boot_mode
    return "bios"
Beispiel #7
0
    def continue_deploy(self, task):
        task.process_event('resume')
        node = task.node
        image_source = node.instance_info.get('image_source')
        LOG.debug('Continuing deploy for node %(node)s with image %(img)s', {
            'node': node.uuid,
            'img': image_source
        })

        image_info = {
            'id': image_source.split('/')[-1],
            'urls': [node.instance_info['image_url']],
            'checksum': node.instance_info['image_checksum'],
            # NOTE(comstud): Older versions of ironic do not set
            # 'disk_format' nor 'container_format', so we use .get()
            # to maintain backwards compatibility in case code was
            # upgraded in the middle of a build request.
            'disk_format': node.instance_info.get('image_disk_format'),
            'container_format':
            node.instance_info.get('image_container_format'),
            'stream_raw_images': CONF.agent.stream_raw_images,
        }

        proxies = {}
        for scheme in ('http', 'https'):
            proxy_param = 'image_%s_proxy' % scheme
            proxy = node.driver_info.get(proxy_param)
            if proxy:
                proxies[scheme] = proxy
        if proxies:
            image_info['proxies'] = proxies
            no_proxy = node.driver_info.get('image_no_proxy')
            if no_proxy is not None:
                image_info['no_proxy'] = no_proxy

        image_info['node_uuid'] = node.uuid
        iwdi = node.driver_internal_info.get('is_whole_disk_image')
        if not iwdi:
            for label in PARTITION_IMAGE_LABELS:
                image_info[label] = node.instance_info.get(label)
            boot_option = deploy_utils.get_boot_option(node)
            boot_mode = boot_mode_utils.get_boot_mode_for_deploy(node)
            if boot_mode:
                image_info['deploy_boot_mode'] = boot_mode
            else:
                image_info['deploy_boot_mode'] = 'bios'
            image_info['boot_option'] = boot_option
            disk_label = deploy_utils.get_disk_label(node)
            if disk_label is not None:
                image_info['disk_label'] = disk_label

        # Tell the client to download and write the image with the given args
        self._client.prepare_image(node, image_info)

        task.process_event('wait')
Beispiel #8
0
def clean_up_pxe_config(task, ipxe_enabled=False):
    """Clean up the TFTP environment for the task's node.

    :param task: A TaskManager instance.

    """
    LOG.debug("Cleaning up PXE config for node %s", task.node.uuid)

    is_uefi_boot_mode = (boot_mode_utils.get_boot_mode_for_deploy(
        task.node) == 'uefi')

    if is_uefi_boot_mode and not ipxe_enabled:
        api = dhcp_factory.DHCPFactory().provider
        ip_addresses = api.get_ip_addresses(task)
        if not ip_addresses:
            return

        for port_ip_address in ip_addresses:
            try:
                # Get xx.xx.xx.xx based grub config file
                ip_address_path = _get_pxe_ip_address_path(
                    port_ip_address, False)
                # NOTE(TheJulia): Remove elilo support after the deprecation
                # period, in the Queens release.
                # Get 0AOAOAOA based elilo config file
                hex_ip_path = _get_pxe_ip_address_path(port_ip_address, True)
            except exception.InvalidIPv4Address:
                continue
            except exception.FailedToGetIPAddressOnPort:
                continue
            # Cleaning up config files created for grub2.
            ironic_utils.unlink_without_raise(ip_address_path)
            # Cleaning up config files created for elilo.
            ironic_utils.unlink_without_raise(hex_ip_path)

    for port in task.ports:
        client_id = port.extra.get('client-id')
        # syslinux, ipxe, etc.
        ironic_utils.unlink_without_raise(
            _get_pxe_mac_path(port.address,
                              client_id=client_id,
                              ipxe_enabled=ipxe_enabled))
        # Grub2 MAC address based confiuration
        ironic_utils.unlink_without_raise(_get_pxe_grub_mac_path(port.address))
    if ipxe_enabled:
        utils.rmtree_without_raise(
            os.path.join(get_ipxe_root_dir(), task.node.uuid))
    else:
        utils.rmtree_without_raise(os.path.join(get_root_dir(),
                                                task.node.uuid))
Beispiel #9
0
def validate_boot_parameters_for_trusted_boot(node):
    """Check if boot parameters are valid for trusted boot."""
    boot_mode = boot_mode_utils.get_boot_mode_for_deploy(node)
    boot_option = deploy_utils.get_boot_option(node)
    is_whole_disk_image = node.driver_internal_info.get('is_whole_disk_image')
    # 'is_whole_disk_image' is not supported by trusted boot, because there is
    # no Kernel/Ramdisk to measure at all.
    if (boot_mode != 'bios'
        or is_whole_disk_image
        or boot_option != 'netboot'):
        msg = (_("Trusted boot is only supported in BIOS boot mode with "
                 "netboot and without whole_disk_image, but Node "
                 "%(node_uuid)s was configured with boot_mode: %(boot_mode)s, "
                 "boot_option: %(boot_option)s, is_whole_disk_image: "
                 "%(is_whole_disk_image)s: at least one of them is wrong, and "
                 "this can be caused by enable secure boot.") %
               {'node_uuid': node.uuid, 'boot_mode': boot_mode,
                'boot_option': boot_option,
                'is_whole_disk_image': is_whole_disk_image})
        LOG.error(msg)
        raise exception.InvalidParameterValue(msg)
Beispiel #10
0
    def prepare_instance(self, task):
        """Prepares the boot of instance.

        This method prepares the boot of the instance after reading
        relevant information from the node's instance_info. In case of netboot,
        it updates the dhcp entries and switches the PXE config. In case of
        localboot, it cleans up the PXE config.
        In case of 'boot from volume', it updates the iSCSI info onto iLO and
        sets the node to boot from 'UefiTarget' boot device.

        :param task: a task from TaskManager.
        :returns: None
        :raises: IloOperationError, if some operation on iLO failed.
        """

        # Set boot mode
        ilo_common.update_boot_mode(task)
        # Need to enable secure boot, if being requested
        ilo_common.update_secure_boot_mode(task, True)

        boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node)

        if deploy_utils.is_iscsi_boot(task) and boot_mode == 'uefi':
            # Need to set 'ilo_uefi_iscsi_boot' param for clean up
            driver_internal_info = task.node.driver_internal_info
            driver_internal_info['ilo_uefi_iscsi_boot'] = True
            task.node.driver_internal_info = driver_internal_info
            task.node.save()
            # It will set iSCSI info onto iLO
            task.driver.management.set_iscsi_boot_target(task)
            manager_utils.node_set_boot_device(task, boot_devices.ISCSIBOOT,
                                               persistent=True)
        else:
            # Volume boot in BIOS boot mode is handled using
            # PXE boot interface
            super(IloPXEBoot, self).prepare_instance(task)
Beispiel #11
0
def _prepare_iso_image(task,
                       kernel_href,
                       ramdisk_href,
                       bootloader_href=None,
                       configdrive=None,
                       root_uuid=None,
                       params=None,
                       base_iso=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.

    If `configdrive` is specified it will be eventually written onto
    the boot ISO image.

    :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) and not base_iso:
        raise exception.InvalidParameterValue(
            _("Unable to find kernel, ramdisk for "
              "building ISO, or explicit ISO for %(node)s") %
            {'node': task.node.uuid})

    img_handler = ImageHandler(task.node.driver)
    k_param = img_handler.kernel_params

    i_info = task.node.instance_info

    # NOTE(TheJulia): Until we support modifying a base iso, most of
    # this logic actually does nothing in the end. But it should!
    if deploy_utils.get_boot_option(task.node) == "ramdisk":
        if not base_iso:
            kernel_params = "root=/dev/ram0 text "
            kernel_params += i_info.get("ramdisk_kernel_arguments", "")
        else:
            kernel_params = None

    else:
        kernel_params = i_info.get('kernel_append_params', k_param)

    if params and not base_iso:
        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

            # FIXME(TheJulia): This is treated as conditional with
            # a base_iso as the intent, eventually, is to support
            # injection into the supplied image.

            if configdrive and not base_iso:
                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.debug(
                    "Built configdrive out of configdrive blob "
                    "for node %(node)s", {'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,
                                   base_iso=base_iso)

            iso_object_name = _get_iso_image_name(task.node)

            image_url = img_handler.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
Beispiel #12
0
    def _prepare_iso_image(cls,
                           task,
                           kernel_href,
                           ramdisk_href,
                           bootloader_href=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 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 fileobj:
            boot_iso_tmp_file = fileobj.name
            images.create_boot_iso(task.context,
                                   boot_iso_tmp_file,
                                   kernel_href,
                                   ramdisk_href,
                                   esp_image_href=bootloader_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 Swift for node %(node)s, exposed "
            "as temporary URL %(url)s", {
                'node': task.node.uuid,
                'name': iso_object_name,
                'url': image_url
            })

        return image_url
Beispiel #13
0
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
Beispiel #14
0
def update_boot_mode(task):
    """Update instance_info with boot mode to be used for deploy.

    This method updates instance_info with boot mode to be used for
    deploy if node properties['capabilities'] do not have boot_mode.
    It sets the boot mode on the node.

    :param task: Task object.
    :raises: IloOperationError if setting boot mode failed.
    """

    node = task.node
    boot_mode = boot_mode_utils.get_boot_mode_for_deploy(node)

    # No boot mode found. Check if default_boot_mode is defined
    if not boot_mode and (CONF.ilo.default_boot_mode in ['bios', 'uefi']):
        boot_mode = CONF.ilo.default_boot_mode
        instance_info = node.instance_info
        instance_info['deploy_boot_mode'] = boot_mode
        node.instance_info = instance_info
        node.save()

    # Boot mode is computed, setting it for the deploy
    if boot_mode:
        LOG.debug("Node %(uuid)s boot mode is being set to %(boot_mode)s", {
            'uuid': node.uuid,
            'boot_mode': boot_mode
        })
        set_boot_mode(node, boot_mode)
        return

    # Computing boot mode based on boot mode settings on bare metal
    LOG.debug("Check pending boot mode for node %s.", node.uuid)
    ilo_object = get_ilo_object(node)

    try:
        boot_mode = ilo_object.get_pending_boot_mode()
    except ilo_error.IloCommandNotSupportedError:
        boot_mode = 'legacy'

    if boot_mode != 'UNKNOWN':
        boot_mode = BOOT_MODE_ILO_TO_GENERIC[boot_mode.lower()]

    if boot_mode == 'UNKNOWN':
        # NOTE(faizan) ILO will return this in remote cases and mostly on
        # the nodes which supports UEFI. Such nodes mostly comes with UEFI
        # as default boot mode. So we will try setting bootmode to UEFI
        # and if it fails then we fall back to BIOS boot mode.
        try:
            boot_mode = 'uefi'
            ilo_object.set_pending_boot_mode(
                BOOT_MODE_GENERIC_TO_ILO[boot_mode].upper())
        except ilo_error.IloError as ilo_exception:
            operation = _("Setting %s as boot mode") % boot_mode
            raise exception.IloOperationError(operation=operation,
                                              error=ilo_exception)

        LOG.debug(
            "Node %(uuid)s boot mode is being set to %(boot_mode)s "
            "as pending boot mode is unknown.", {
                'uuid': node.uuid,
                'boot_mode': boot_mode
            })

    instance_info = node.instance_info
    instance_info['deploy_boot_mode'] = boot_mode
    node.instance_info = instance_info
    node.save()
Beispiel #15
0
def create_pxe_config(task, pxe_options, template=None, ipxe_enabled=False):
    """Generate PXE configuration file and MAC address links for it.

    This method will generate the PXE configuration file for the task's
    node under a directory named with the UUID of that node. For each
    MAC address or DHCP IP address (port) of that node, a symlink for
    the configuration file will be created under the PXE configuration
    directory, so regardless of which port boots first they'll get the
    same PXE configuration.
    If elilo is the bootloader in use, then its configuration file will
    be created based on hex form of DHCP IP address.
    If grub2 bootloader is in use, then its configuration will be created
    based on DHCP IP address in the form nn.nn.nn.nn.

    :param task: A TaskManager instance.
    :param pxe_options: A dictionary with the PXE configuration
        parameters.
    :param template: The PXE configuration template. If no template is
        given the node specific template will be used.

    """
    LOG.debug("Building PXE config for node %s", task.node.uuid)
    if template is None:
        template = deploy_utils.get_pxe_config_template(task.node)

    _ensure_config_dirs_exist(task, ipxe_enabled)

    pxe_config_file_path = get_pxe_config_file_path(task.node.uuid,
                                                    ipxe_enabled=ipxe_enabled)
    is_uefi_boot_mode = (boot_mode_utils.get_boot_mode_for_deploy(
        task.node) == 'uefi')

    # grub bootloader panics with '{}' around any of its tags in its
    # config file. To overcome that 'ROOT' and 'DISK_IDENTIFIER' are enclosed
    # with '(' and ')' in uefi boot mode.
    # These changes do not have any impact on elilo bootloader.
    hex_form = True
    if is_uefi_boot_mode and utils.is_regex_string_in_file(
            template, '^menuentry'):
        hex_form = False
        pxe_config_root_tag = '(( ROOT ))'
        pxe_config_disk_ident = '(( DISK_IDENTIFIER ))'
        LOG.warning("The requested config appears to support elilo. "
                    "Support for elilo has been deprecated and will be "
                    "removed in the Queens release of OpenStack.")
    else:
        # TODO(stendulker): We should use '(' ')' as the delimiters for all our
        # config files so that we do not need special handling for each of the
        # bootloaders. Should be removed once the Mitaka release starts.
        pxe_config_root_tag = '{{ ROOT }}'
        pxe_config_disk_ident = '{{ DISK_IDENTIFIER }}'

    params = {
        'pxe_options': pxe_options,
        'ROOT': pxe_config_root_tag,
        'DISK_IDENTIFIER': pxe_config_disk_ident
    }

    pxe_config = utils.render_template(template, params)
    utils.write_to_file(pxe_config_file_path, pxe_config)

    # Always write the mac addresses
    _link_mac_pxe_configs(task, ipxe_enabled=ipxe_enabled)
    if is_uefi_boot_mode and not ipxe_enabled:
        try:
            _link_ip_address_pxe_configs(task, hex_form, ipxe_enabled)
        # NOTE(TheJulia): The IP address support will fail if the
        # dhcp_provider interface is set to none. This will result
        # in the MAC addresses and DHCP files being written, and
        # we can remove IP address creation for the grub use
        # case, considering that will ease removal of elilo support.
        except exception.FailedToGetIPaddressesOnPort as e:
            if CONF.dhcp.dhcp_provider != 'none':
                with excutils.save_and_reraise_exception():
                    LOG.error(
                        'Unable to create boot config, IP address '
                        'was unable to be retrieved. %(error)s', {'error': e})
Beispiel #16
0
    def prepare_instance(self, task):
        """Prepares the boot of instance.

        This method prepares the boot of the instance after reading
        relevant information from the node's instance_info. In case of netboot,
        it updates the dhcp entries and switches the PXE config. In case of
        localboot, it cleans up the PXE config.

        :param task: a task from TaskManager.
        :returns: None
        """
        boot_mode_utils.sync_boot_mode(task)

        node = task.node
        boot_option = deploy_utils.get_boot_option(node)
        boot_device = None
        if deploy_utils.is_iscsi_boot(task):
            dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
            provider = dhcp_factory.DHCPFactory()
            provider.update_dhcp(task, dhcp_opts)

            # configure iPXE for iscsi boot
            pxe_config_path = pxe_utils.get_pxe_config_file_path(
                task.node.uuid)
            if not os.path.isfile(pxe_config_path):
                pxe_options = _build_pxe_config_options(task, {})
                pxe_config_template = (
                    deploy_utils.get_pxe_config_template(node))
                pxe_utils.create_pxe_config(task, pxe_options,
                                            pxe_config_template)
            deploy_utils.switch_pxe_config(
                pxe_config_path,
                None,
                boot_mode_utils.get_boot_mode_for_deploy(node),
                False,
                iscsi_boot=True)
            boot_device = boot_devices.PXE

        elif boot_option == "ramdisk":
            instance_image_info = _get_instance_image_info(
                task.node, task.context)
            _cache_ramdisk_kernel(task.context, task.node, instance_image_info)
            dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
            provider = dhcp_factory.DHCPFactory()
            provider.update_dhcp(task, dhcp_opts)
            pxe_config_path = pxe_utils.get_pxe_config_file_path(
                task.node.uuid)
            deploy_utils.switch_pxe_config(
                pxe_config_path,
                None,
                boot_mode_utils.get_boot_mode_for_deploy(node),
                False,
                iscsi_boot=False,
                ramdisk_boot=True)
            boot_device = boot_devices.PXE

        elif boot_option != "local":
            if task.driver.storage.should_write_image(task):
                # Make sure that the instance kernel/ramdisk is cached.
                # This is for the takeover scenario for active nodes.
                instance_image_info = _get_instance_image_info(
                    task.node, task.context)
                _cache_ramdisk_kernel(task.context, task.node,
                                      instance_image_info)

            # If it's going to PXE boot we need to update the DHCP server
            dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
            provider = dhcp_factory.DHCPFactory()
            provider.update_dhcp(task, dhcp_opts)

            iwdi = task.node.driver_internal_info.get('is_whole_disk_image')
            try:
                root_uuid_or_disk_id = task.node.driver_internal_info[
                    'root_uuid_or_disk_id']
            except KeyError:
                if not task.driver.storage.should_write_image(task):
                    pass
                elif not iwdi:
                    LOG.warning(
                        "The UUID for the root partition can't be "
                        "found, unable to switch the pxe config from "
                        "deployment mode to service (boot) mode for "
                        "node %(node)s", {"node": task.node.uuid})
                else:
                    LOG.warning(
                        "The disk id for the whole disk image can't "
                        "be found, unable to switch the pxe config "
                        "from deployment mode to service (boot) mode "
                        "for node %(node)s. Booting the instance "
                        "from disk.", {"node": task.node.uuid})
                    pxe_utils.clean_up_pxe_config(task)
                    boot_device = boot_devices.DISK
            else:
                _build_service_pxe_config(task, instance_image_info,
                                          root_uuid_or_disk_id)
                boot_device = boot_devices.PXE
        else:
            # If it's going to boot from the local disk, we don't need
            # PXE config files. They still need to be generated as part
            # of the prepare() because the deployment does PXE boot the
            # deploy ramdisk
            pxe_utils.clean_up_pxe_config(task)
            boot_device = boot_devices.DISK

        # NOTE(pas-ha) do not re-set boot device on ACTIVE nodes
        # during takeover
        if boot_device and task.node.provision_state != states.ACTIVE:
            manager_utils.node_set_boot_device(task,
                                               boot_device,
                                               persistent=True)
Beispiel #17
0
    def prepare_instance(self, task):
        """Prepares the boot of instance.

        This method prepares the boot of the instance after reading
        relevant information from the node's instance_info.
        It does the following depending on boot_option for deploy:

        - If the boot mode is 'uefi' and its booting from volume, then it
          sets the iSCSI target info and node to boot from 'UefiTarget'
          boot device.
        - If not 'boot from volume' and the boot_option requested for
          this deploy is 'local' or image is a whole disk image, then
          it sets the node to boot from disk.
        - Otherwise it finds/creates the boot ISO to boot the instance
          image, attaches the boot ISO to the bare metal and then sets
          the node to boot from CDROM.

        :param task: a task from TaskManager.
        :returns: None
        :raises: IloOperationError, if some operation on iLO failed.
        :raises: InstanceDeployFailure, if its try to boot iSCSI volume in
                 'BIOS' boot mode.
        """
        ilo_common.cleanup_vmedia_boot(task)

        boot_mode = boot_mode_utils.get_boot_mode_for_deploy(task.node)
        boot_option = deploy_utils.get_boot_option(task.node)

        if deploy_utils.is_iscsi_boot(task):
            # It will set iSCSI info onto iLO
            if boot_mode == 'uefi':
                # Need to set 'ilo_uefi_iscsi_boot' param for clean up
                driver_internal_info = task.node.driver_internal_info
                driver_internal_info['ilo_uefi_iscsi_boot'] = True
                task.node.driver_internal_info = driver_internal_info
                task.node.save()
                task.driver.management.set_iscsi_boot_target(task)
                manager_utils.node_set_boot_device(
                    task, boot_devices.ISCSIBOOT, persistent=True)
            else:
                msg = 'Virtual media can not boot volume in BIOS boot mode.'
                raise exception.InstanceDeployFailure(msg)
        elif boot_option == "ramdisk":
            boot_iso = _get_boot_iso(task, None)
            ilo_common.setup_vmedia_for_boot(task, boot_iso)
            manager_utils.node_set_boot_device(task,
                                               boot_devices.CDROM,
                                               persistent=True)
        else:
            # Boot from disk every time if the image deployed is
            # a whole disk image.
            node = task.node
            iwdi = node.driver_internal_info.get('is_whole_disk_image')
            if deploy_utils.get_boot_option(node) == "local" or iwdi:
                manager_utils.node_set_boot_device(task, boot_devices.DISK,
                                                   persistent=True)
            else:
                drv_int_info = node.driver_internal_info
                root_uuid_or_disk_id = drv_int_info.get('root_uuid_or_disk_id')
                if root_uuid_or_disk_id:
                    self._configure_vmedia_boot(task, root_uuid_or_disk_id)
                else:
                    LOG.warning("The UUID for the root partition could not "
                                "be found for node %s", node.uuid)
        # Set boot mode
        ilo_common.update_boot_mode(task)
        # Need to enable secure boot, if being requested
        ilo_common.update_secure_boot_mode(task, True)
Beispiel #18
0
    def reboot_to_instance(self, task):
        task.process_event('resume')
        node = task.node
        iwdi = task.node.driver_internal_info.get('is_whole_disk_image')
        error = self.check_deploy_success(node)
        if error is not None:
            # TODO(jimrollenhagen) power off if using neutron dhcp to
            #                      align with pxe driver?
            msg = (_('node %(node)s command status errored: %(error)s') %
                   {'node': node.uuid, 'error': error})
            LOG.error(msg)
            deploy_utils.set_failed_state(task, msg)
            return

        # If `boot_option` is set to `netboot`, PXEBoot.prepare_instance()
        # would need root_uuid of the whole disk image to add it into the
        # pxe config to perform chain boot.
        # IPA would have returned us the 'root_uuid_or_disk_id' if image
        # being provisioned is a whole disk image. IPA would also provide us
        # 'efi_system_partition_uuid' if the image being provisioned is a
        # partition image.
        # In case of local boot using partition image, we need both
        # 'root_uuid_or_disk_id' and 'efi_system_partition_uuid' to configure
        # bootloader for local boot.
        driver_internal_info = task.node.driver_internal_info
        root_uuid = self._get_uuid_from_result(task, 'root_uuid')
        if root_uuid:
            driver_internal_info['root_uuid_or_disk_id'] = root_uuid
            task.node.driver_internal_info = driver_internal_info
            task.node.save()
        elif iwdi and CONF.agent.manage_agent_boot:
            # IPA version less than 3.1.0 will not return root_uuid for
            # whole disk image. Also IPA version introduced a requirement
            # for hexdump utility that may not be always available. Need to
            # fall back to older behavior for the same.
            LOG.warning("With the deploy ramdisk based on Ironic Python Agent "
                        "version 3.1.0 and beyond, the drivers using "
                        "`direct` deploy interface performs `netboot` or "
                        "`local` boot for whole disk image based on value "
                        "of boot option setting. When you upgrade Ironic "
                        "Python Agent in your deploy ramdisk, ensure that "
                        "boot option is set appropriately for the node %s. "
                        "The boot option can be set using configuration "
                        "`[deploy]/default_boot_option` or as a `boot_option` "
                        "capability in node's `properties['capabilities']`. "
                        "Also please note that this functionality requires "
                        "`hexdump` command in the ramdisk.", node.uuid)

        efi_sys_uuid = None
        if not iwdi:
            if boot_mode_utils.get_boot_mode_for_deploy(node) == 'uefi':
                efi_sys_uuid = (
                    self._get_uuid_from_result(task,
                                               'efi_system_partition_uuid'))
        LOG.info('Image successfully written to node %s', node.uuid)

        if CONF.agent.manage_agent_boot:
            # It is necessary to invoke prepare_instance() of the node's
            # boot interface, so that the any necessary configurations like
            # setting of the boot mode (e.g. UEFI secure boot) which cannot
            # be done on node during deploy stage can be performed.
            LOG.debug('Executing driver specific tasks before booting up the '
                      'instance for node %s', node.uuid)
            self.prepare_instance_to_boot(task, root_uuid, efi_sys_uuid)
        else:
            manager_utils.node_set_boot_device(task, 'disk', persistent=True)

        LOG.debug('Rebooting node %s to instance', node.uuid)
        self.reboot_and_finish_deploy(task)
Beispiel #19
0
def _get_boot_iso(task, root_uuid):
    """This method returns a boot ISO to boot the node.

    It chooses one of the three options in the order as below:
    1. Does nothing if 'ilo_boot_iso' is present in node's instance_info and
       'boot_iso_created_in_web_server' is not set in 'driver_internal_info'.
    2. Image deployed has a meta-property 'boot_iso' in Glance. This should
       refer to the UUID of the boot_iso which exists in Glance.
    3. Generates a boot ISO on the fly using kernel and ramdisk mentioned in
       the image deployed. It uploads the generated boot ISO to Swift.

    :param task: a TaskManager instance containing the node to act on.
    :param root_uuid: the uuid of the root partition.
    :returns: boot ISO URL. Should be either of below:
        * A Swift object - It should be of format 'swift:<object-name>'. It is
          assumed that the image object is present in
          CONF.ilo.swift_ilo_container;
        * A Glance image - It should be format 'glance://<glance-image-uuid>'
          or just <glance-image-uuid>;
        * An HTTP URL.
        On error finding the boot iso, it returns None.
    :raises: MissingParameterValue, if any of the required parameters are
        missing in the node's driver_info or instance_info.
    :raises: InvalidParameterValue, if any of the parameters have invalid
        value in the node's driver_info or instance_info.
    :raises: SwiftOperationError, if operation with Swift fails.
    :raises: ImageCreationFailed, if creation of boot ISO failed.
    :raises: exception.ImageRefValidationFailed if ilo_boot_iso is not
        HTTP(S) URL.
    """
    LOG.debug("Trying to get a boot ISO to boot the baremetal node")

    # Option 1 - Check if user has provided ilo_boot_iso in node's
    # instance_info
    driver_internal_info = task.node.driver_internal_info
    boot_iso_created_in_web_server = (
        driver_internal_info.get('boot_iso_created_in_web_server'))

    if (task.node.instance_info.get('ilo_boot_iso')
            and not boot_iso_created_in_web_server):
        LOG.debug("Using ilo_boot_iso provided in node's instance_info")
        boot_iso = task.node.instance_info['ilo_boot_iso']
        if not service_utils.is_glance_image(boot_iso):
            try:
                image_service.HttpImageService().validate_href(boot_iso)
            except exception.ImageRefValidationFailed:
                with excutils.save_and_reraise_exception():
                    LOG.error("Virtual media deploy accepts only Glance "
                              "images or HTTP(S) URLs as "
                              "instance_info['ilo_boot_iso']. Either %s "
                              "is not a valid HTTP(S) URL or is "
                              "not reachable.", boot_iso)

        return task.node.instance_info['ilo_boot_iso']

    # Option 2 - Check if user has provided a boot_iso in Glance. If boot_iso
    # is a supported non-glance href execution will proceed to option 3.
    deploy_info = _parse_deploy_info(task.node)

    image_href = deploy_info['image_source']
    image_properties = (
        images.get_image_properties(
            task.context, image_href, ['boot_iso', 'kernel_id', 'ramdisk_id']))

    boot_iso_uuid = image_properties.get('boot_iso')
    kernel_href = (task.node.instance_info.get('kernel')
                   or image_properties.get('kernel_id'))
    ramdisk_href = (task.node.instance_info.get('ramdisk')
                    or image_properties.get('ramdisk_id'))

    if boot_iso_uuid:
        LOG.debug("Found boot_iso %s in Glance", boot_iso_uuid)
        return boot_iso_uuid

    if not kernel_href or not ramdisk_href:
        LOG.error("Unable to find kernel or ramdisk for "
                  "image %(image)s to generate boot ISO for %(node)s",
                  {'image': image_href, 'node': task.node.uuid})
        return

    # NOTE(rameshg87): Functionality to share the boot ISOs created for
    # similar instances (instances with same deployed image) is
    # not implemented as of now. Creation/Deletion of such a shared boot ISO
    # will require synchronisation across conductor nodes for the shared boot
    # ISO.  Such a synchronisation mechanism doesn't exist in ironic as of now.

    # Option 3 - Create boot_iso from kernel/ramdisk, upload to Swift
    # or web server and provide its name.
    deploy_iso_uuid = deploy_info['ilo_deploy_iso']
    boot_mode = boot_mode_utils.get_boot_mode_for_deploy(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_uuid, root_uuid,
                               kernel_params, boot_mode)

        if CONF.ilo.use_web_server_for_images:
            boot_iso_url = (
                ilo_common.copy_image_to_web_server(boot_iso_tmp_file,
                                                    boot_iso_object_name))
            driver_internal_info = task.node.driver_internal_info
            driver_internal_info['boot_iso_created_in_web_server'] = True
            task.node.driver_internal_info = driver_internal_info
            task.node.save()
            LOG.debug("Created boot_iso %(boot_iso)s for node %(node)s",
                      {'boot_iso': boot_iso_url, 'node': task.node.uuid})
            return boot_iso_url
        else:
            container = CONF.ilo.swift_ilo_container
            swift_api = swift.SwiftAPI()
            swift_api.create_object(container, boot_iso_object_name,
                                    boot_iso_tmp_file)

            LOG.debug("Created boot_iso %s in Swift", boot_iso_object_name)
            return 'swift:%s' % boot_iso_object_name
Beispiel #20
0
def create_pxe_config(task, pxe_options, template=None):
    """Generate PXE configuration file and MAC address links for it.

    This method will generate the PXE configuration file for the task's
    node under a directory named with the UUID of that node. For each
    MAC address or DHCP IP address (port) of that node, a symlink for
    the configuration file will be created under the PXE configuration
    directory, so regardless of which port boots first they'll get the
    same PXE configuration.
    If elilo is the bootloader in use, then its configuration file will
    be created based on hex form of DHCP IP address.
    If grub2 bootloader is in use, then its configuration will be created
    based on DHCP IP address in the form nn.nn.nn.nn.

    :param task: A TaskManager instance.
    :param pxe_options: A dictionary with the PXE configuration
        parameters.
    :param template: The PXE configuration template. If no template is
        given the node specific template will be used.

    """
    LOG.debug("Building PXE config for node %s", task.node.uuid)

    if template is None:
        template = deploy_utils.get_pxe_config_template(task.node)

    _ensure_config_dirs_exist(task.node.uuid)

    pxe_config_file_path = get_pxe_config_file_path(task.node.uuid)
    is_uefi_boot_mode = (boot_mode_utils.get_boot_mode_for_deploy(
        task.node) == 'uefi')

    # grub bootloader panics with '{}' around any of its tags in its
    # config file. To overcome that 'ROOT' and 'DISK_IDENTIFIER' are enclosed
    # with '(' and ')' in uefi boot mode.
    # These changes do not have any impact on elilo bootloader.
    hex_form = True
    if is_uefi_boot_mode and utils.is_regex_string_in_file(
            template, '^menuentry'):
        hex_form = False
        pxe_config_root_tag = '(( ROOT ))'
        pxe_config_disk_ident = '(( DISK_IDENTIFIER ))'
        LOG.warning("The requested config appears to support elilo. "
                    "Support for elilo has been deprecated and will be "
                    "removed in the Queens release of OpenStack.")
    else:
        # TODO(stendulker): We should use '(' ')' as the delimiters for all our
        # config files so that we do not need special handling for each of the
        # bootloaders. Should be removed once the Mitaka release starts.
        pxe_config_root_tag = '{{ ROOT }}'
        pxe_config_disk_ident = '{{ DISK_IDENTIFIER }}'

    params = {
        'pxe_options': pxe_options,
        'ROOT': pxe_config_root_tag,
        'DISK_IDENTIFIER': pxe_config_disk_ident
    }

    pxe_config = utils.render_template(template, params)
    utils.write_to_file(pxe_config_file_path, pxe_config)

    if is_uefi_boot_mode and not CONF.pxe.ipxe_enabled:
        _link_ip_address_pxe_configs(task, hex_form)
    else:
        _link_mac_pxe_configs(task)