Example #1
0
 def test_render_file(self, jinja_fsl_mock):
     path = '/path/to/template.j2'
     jinja_fsl_mock.return_value = jinja2.DictLoader(
         {'template.j2': self.template})
     self.assertEqual(self.expected,
                      utils.render_template(path,
                                            self.params))
     jinja_fsl_mock.assert_called_once_with('/path/to')
Example #2
0
    def test_default_ipxe_boot_script(self):
        rendered_template = utils.render_template(
            CONF.pxe.ipxe_boot_script,
            {'ipxe_for_mac_uri': 'pxelinux.cfg/'})

        with open('ironic/tests/unit/drivers/boot.ipxe') as f:
            expected_template = f.read().rstrip()

        self.assertEqual(six.text_type(expected_template), rendered_template)
Example #3
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 = deploy_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 ))"
    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)
Example #4
0
    def test_default_pxe_config(self):

        rendered_template = utils.render_template(
            CONF.pxe.pxe_config_template,
            {'pxe_options': self.pxe_options,
             'ROOT': '{{ ROOT }}',
             'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'})

        with open('ironic/tests/unit/drivers/pxe_config.template') as f:
            expected_template = f.read().rstrip()

        self.assertEqual(six.text_type(expected_template), rendered_template)
Example #5
0
def create_ipxe_boot_script():
    """Render the iPXE boot script into the HTTP root directory"""
    boot_script = utils.render_template(
        CONF.pxe.ipxe_boot_script,
        {'ipxe_for_mac_uri': PXE_CFG_DIR_NAME + '/'})
    bootfile_path = os.path.join(
        CONF.deploy.http_root,
        os.path.basename(CONF.pxe.ipxe_boot_script))
    # NOTE(pas-ha) to prevent unneeded writes,
    # only write to file if its content is different from required,
    # which should be rather rare
    if (not os.path.isfile(bootfile_path)
            or not utils.file_has_content(bootfile_path, boot_script)):
        utils.write_to_file(bootfile_path, boot_script)
Example #6
0
    def test_default_grub_config(self):
        pxe_opts = self.pxe_options
        pxe_opts['boot_mode'] = 'uefi'
        pxe_opts['tftp_server'] = '192.0.2.1'
        rendered_template = utils.render_template(
            CONF.pxe.uefi_pxe_config_template,
            {'pxe_options': pxe_opts,
             'ROOT': '(( ROOT ))',
             'DISK_IDENTIFIER': '(( DISK_IDENTIFIER ))'})

        templ_file = 'ironic/tests/unit/drivers/pxe_grub_config.template'
        with open(templ_file) as f:
            expected_template = f.read().rstrip()

        self.assertEqual(six.text_type(expected_template), rendered_template)
Example #7
0
def _generate_cfg(kernel_params, template, options):
    """Generates a isolinux or grub configuration file.

    Given a given a list of strings containing kernel parameters, this method
    returns the kernel cmdline string.
    :param kernel_params: a list of strings(each element being a string like
        'K=V' or 'K' or combination of them like 'K1=V1 K2 K3=V3') to be added
        as the kernel cmdline.
    :param template: the path of the config template file.
    :param options: a dictionary of keywords which need to be replaced in
                    template file to generate a proper config file.
    :returns: a string containing the contents of the isolinux configuration
        file.
    """
    options.update({'kernel_params': ' '.join(kernel_params or [])})
    return utils.render_template(template, options)
Example #8
0
    def test_default_ipxe_boot_from_volume_config(self):
        self.config(
            pxe_config_template='ironic/drivers/modules/ipxe_config.template',
            group='pxe'
        )
        self.config(http_url='http://1.2.3.4:1234', group='deploy')
        rendered_template = utils.render_template(
            CONF.pxe.pxe_config_template,
            {'pxe_options': self.ipxe_options_boot_from_volume,
             'ROOT': '{{ ROOT }}',
             'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'})

        templ_file = 'ironic/tests/unit/drivers/' \
                     'ipxe_config_boot_from_volume.template'
        with open(templ_file) as f:
            expected_template = f.read().rstrip()

        self.assertEqual(six.text_type(expected_template), rendered_template)
Example #9
0
    def test_default_elilo_config(self):
        pxe_opts = self.pxe_options
        pxe_opts['boot_mode'] = 'uefi'
        self.config(
            uefi_pxe_config_template=('ironic/drivers/modules/'
                                      'elilo_efi_pxe_config.template'),
            group='pxe'
        )
        rendered_template = utils.render_template(
            CONF.pxe.uefi_pxe_config_template,
            {'pxe_options': pxe_opts,
             'ROOT': '{{ ROOT }}',
             'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'})

        templ_file = 'ironic/tests/unit/drivers/elilo_efi_pxe_config.template'
        with open(templ_file) as f:
            expected_template = f.read().rstrip()

        self.assertEqual(six.text_type(expected_template), rendered_template)
Example #10
0
    def test_default_ipxe_timeout_config(self):
        # NOTE(lucasagomes): iPXE is just an extension of the PXE driver,
        # it doesn't have it's own configuration option for template.
        # More info:
        # http://docs.openstack.org/developer/ironic/deploy/install-guide.html
        self.config(
            pxe_config_template='ironic/drivers/modules/ipxe_config.template',
            group='pxe'
        )
        self.config(http_url='http://1.2.3.4:1234', group='deploy')
        rendered_template = utils.render_template(
            CONF.pxe.pxe_config_template,
            {'pxe_options': self.ipxe_options_timeout,
             'ROOT': '{{ ROOT }}',
             'DISK_IDENTIFIER': '{{ DISK_IDENTIFIER }}'})

        templ_file = 'ironic/tests/unit/drivers/ipxe_config_timeout.template'
        with open(templ_file) as f:
            expected_template = f.read().rstrip()

        self.assertEqual(six.text_type(expected_template), rendered_template)
Example #11
0
    def prepare_ramdisk(self, task, ramdisk_params):
        """Prepares the boot of Ironic ramdisk using PXE.

        This method prepares the boot of the deploy kernel/ramdisk after
        reading relevant information from the node's driver_info and
        instance_info.

        :param task: a task from TaskManager.
        :param ramdisk_params: the parameters to be passed to the ramdisk.
            pxe driver passes these parameters as kernel command-line
            arguments.
        :returns: None
        :raises: MissingParameterValue, if some information is missing in
            node's driver_info or instance_info.
        :raises: InvalidParameterValue, if some information provided is
            invalid.
        :raises: IronicException, if some power or set boot boot device
            operation failed on the node.
        """
        node = task.node

        if CONF.pxe.ipxe_enabled:
            # Render the iPXE boot script template and save it
            # to HTTP root directory
            boot_script = utils.render_template(
                CONF.pxe.ipxe_boot_script,
                {'ipxe_for_mac_uri': pxe_utils.PXE_CFG_DIR_NAME + '/'})
            bootfile_path = os.path.join(
                CONF.deploy.http_root,
                os.path.basename(CONF.pxe.ipxe_boot_script))
            # NOTE(pas-ha) to prevent unneeded writes,
            # only write to file if its content is different from required,
            # which should be rather rare
            if (not os.path.isfile(bootfile_path) or
                not utils.file_has_content(bootfile_path, boot_script)):
                    utils.write_to_file(bootfile_path, boot_script)

        dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
        provider = dhcp_factory.DHCPFactory()
        provider.update_dhcp(task, dhcp_opts)

        pxe_info = _get_deploy_image_info(node)

        # NODE: Try to validate and fetch instance images only
        # if we are in DEPLOYING state.
        if node.provision_state == states.DEPLOYING:
            pxe_info.update(_get_instance_image_info(node, task.context))

        pxe_options = _build_pxe_config_options(task, pxe_info)
        pxe_options.update(ramdisk_params)

        pxe_config_template = deploy_utils.get_pxe_config_template(node)

        pxe_utils.create_pxe_config(task, pxe_options,
                                    pxe_config_template)
        deploy_utils.try_set_boot_device(task, boot_devices.PXE)

        if CONF.pxe.ipxe_enabled and CONF.pxe.ipxe_use_swift:
            pxe_info.pop('deploy_kernel', None)
            pxe_info.pop('deploy_ramdisk', None)
        if pxe_info:
            _cache_ramdisk_kernel(task.context, node, pxe_info)
Example #12
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(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})
Example #13
0
 def test_render_string(self):
     self.assertEqual(self.expected,
                      utils.render_template(self.template,
                                            self.params,
                                            is_file=False))
Example #14
0
 def test_render_string(self):
     self.assertEqual(self.expected,
                      utils.render_template(self.template,
                                            self.params,
                                            is_file=False))
Example #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(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})
Example #16
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 = (deploy_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)