Example #1
0
    def prepare_instance(self, task):
        """Prepares the boot of instance over virtual media.

        This method prepares the boot of the instance after reading
        relevant information from the node's instance_info.

        The internal logic is as follows:

        - If `boot_option` requested for this deploy is 'local', then set the
          node to boot from disk.
        - Unless `boot_option` requested for this deploy is 'ramdisk', pass
          root disk/partition ID to virtual media boot image
        - Otherwise build boot image, insert it into virtual media device
          and set node to boot from CD.

        :param task: a task from TaskManager.
        :returns: None
        :raises: InstanceDeployFailure, if its try to boot iSCSI volume in
                 'BIOS' boot mode.
        """
        node = task.node

        boot_option = deploy_utils.get_boot_option(node)
        self.clean_up_instance(task)
        iwdi = node.driver_internal_info.get('is_whole_disk_image')
        if boot_option == "local" or iwdi:
            self._set_boot_device(task, boot_devices.DISK, persistent=True)

            LOG.debug("Node %(node)s is set to permanently boot from local "
                      "%(device)s", {'node': task.node.uuid,
                                     'device': boot_devices.DISK})
            return

        params = {}

        if boot_option != 'ramdisk':
            root_uuid = node.driver_internal_info.get('root_uuid_or_disk_id')
            if not root_uuid and task.driver.storage.should_write_image(task):
                LOG.warning(
                    "The UUID of the root partition could not be found for "
                    "node %s. Booting instance from disk anyway.", node.uuid)

                self._set_boot_device(task, boot_devices.DISK, persistent=True)

                return

            params.update(root_uuid=root_uuid)

        iso_ref = _prepare_boot_iso(task, **params)
        _eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
        _insert_vmedia(task, iso_ref, sushy.VIRTUAL_MEDIA_CD)

        boot_mode_utils.sync_boot_mode(task)

        self._set_boot_device(task, boot_devices.CDROM, persistent=True)

        LOG.debug("Node %(node)s is set to permanently boot from "
                  "%(device)s", {'node': task.node.uuid,
                                 'device': boot_devices.CDROM})
Example #2
0
    def set_boot_device(self, task, device, persistent=False):
        """Set the boot device for a node.

        Set the boot device to use on next reboot of the node.

        :param task: a task from TaskManager.
        :param device: the boot device, one of
                       :mod:`ironic.common.boot_devices`.
        :param persistent: Boolean value. True if the boot device will
                           persist to all future boots, False if not.
                           Default: False.
        :raises: InvalidParameterValue on malformed parameter(s)
        :raises: MissingParameterValue on missing parameter(s)
        :raises: RedfishConnectionError when it fails to connect to Redfish
        :raises: RedfishError on an error from the Sushy library
        """
        utils.pop_node_nested_field(task.node, 'driver_internal_info',
                                    'redfish_boot_device')
        task.node.save()

        system = redfish_utils.get_system(task.node)

        try:
            _set_boot_device(task,
                             system,
                             BOOT_DEVICE_MAP_REV[device],
                             persistent=persistent)
        except sushy.exceptions.SushyError as e:
            error_msg = (_('Redfish set boot device failed for node '
                           '%(node)s. Error: %(error)s') % {
                               'node': task.node.uuid,
                               'error': e
                           })
            LOG.error(error_msg)
            raise exception.RedfishError(error=error_msg)

        # Ensure that boot mode is synced with what is set.
        # Some BMCs reset it to default (BIOS) when changing the boot device.
        boot_mode_utils.sync_boot_mode(task)
Example #3
0
File: boot.py Project: LZMWL/ironic
    def prepare_ramdisk(self, task, ramdisk_params):
        """Prepares the boot of deploy or rescue ramdisk over virtual media.

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

        :param task: A task from TaskManager.
        :param ramdisk_params: the parameters to be passed to the ramdisk.
        :returns: None
        :raises: MissingParameterValue, if some information is missing in
            node's driver_info or instance_info.
        :raises: InvalidParameterValue, if some information provided is
            invalid.
        :raises: IronicException, if some power or set boot boot device
            operation failed on the node.
        """
        node = task.node
        # NOTE(TheJulia): If this method is being called by something
        # aside from deployment, clean and rescue, such as conductor takeover,
        # we should treat this as a no-op and move on otherwise we would
        # modify the state of the node due to virtual media operations.
        if node.provision_state not in (states.DEPLOYING, states.CLEANING,
                                        states.RESCUING, states.INSPECTING):
            return

        manager_utils.node_power_action(task, states.POWER_OFF)

        d_info = self._parse_driver_info(node)

        config_via_floppy = d_info.get('config_via_floppy')

        deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task)
        ramdisk_params['BOOTIF'] = deploy_nic_mac
        if CONF.debug and 'ipa-debug' not in ramdisk_params:
            ramdisk_params['ipa-debug'] = '1'

        if config_via_floppy:

            if self._has_vmedia_device(task, sushy.VIRTUAL_MEDIA_FLOPPY):
                # NOTE (etingof): IPA will read the diskette only if
                # we tell it to
                ramdisk_params['boot_method'] = 'vmedia'

                floppy_ref = self._prepare_floppy_image(task,
                                                        params=ramdisk_params)

                self._eject_vmedia(task, sushy.VIRTUAL_MEDIA_FLOPPY)
                self._insert_vmedia(task, floppy_ref,
                                    sushy.VIRTUAL_MEDIA_FLOPPY)

                LOG.debug(
                    'Inserted virtual floppy with configuration for '
                    'node %(node)s', {'node': task.node.uuid})

            else:
                LOG.warning(
                    'Config via floppy is requested, but '
                    'Floppy drive is not available on node '
                    '%(node)s', {'node': task.node.uuid})

        mode = deploy_utils.rescue_or_deploy_mode(node)

        iso_ref = self._prepare_deploy_iso(task, ramdisk_params, mode)

        self._eject_vmedia(task, sushy.VIRTUAL_MEDIA_CD)
        self._insert_vmedia(task, iso_ref, sushy.VIRTUAL_MEDIA_CD)

        boot_mode_utils.sync_boot_mode(task)

        self._set_boot_device(task, boot_devices.CDROM)

        LOG.debug("Node %(node)s is set to one time boot from "
                  "%(device)s", {
                      'node': task.node.uuid,
                      'device': boot_devices.CDROM
                  })
Example #4
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
        """
        ipxe_enabled = CONF.pxe.ipxe_enabled
        boot_mode_utils.sync_boot_mode(task)

        node = task.node
        boot_option = deploy_utils.get_boot_option(node)
        boot_device = None
        instance_image_info = {}
        if boot_option == "ramdisk":
            instance_image_info = pxe_utils.get_instance_image_info(task)
            pxe_utils.cache_ramdisk_kernel(task,
                                           instance_image_info,
                                           ipxe_enabled=CONF.pxe.ipxe_enabled)

        if deploy_utils.is_iscsi_boot(task) or boot_option == "ramdisk":
            pxe_utils.prepare_instance_pxe_config(
                task,
                instance_image_info,
                iscsi_boot=deploy_utils.is_iscsi_boot(task),
                ramdisk_boot=(boot_option == "ramdisk"),
                ipxe_enabled=CONF.pxe.ipxe_enabled)
            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 = pxe_utils.get_instance_image_info(task)
                pxe_utils.cache_ramdisk_kernel(
                    task,
                    instance_image_info,
                    ipxe_enabled=CONF.pxe.ipxe_enabled)

            # If it's going to PXE boot we need to update the DHCP server
            dhcp_opts = pxe_utils.dhcp_options_for_instance(task, ipxe_enabled)
            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, ipxe_enabled=CONF.pxe.ipxe_enabled)
                    boot_device = boot_devices.DISK
            else:
                pxe_utils.build_service_pxe_config(task,
                                                   instance_image_info,
                                                   root_uuid_or_disk_id,
                                                   ipxe_enabled=ipxe_enabled)
                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,
                                          ipxe_enabled=CONF.pxe.ipxe_enabled)
            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:
            persistent = True
            if node.driver_info.get('force_persistent_boot_device',
                                    'Default') == 'Never':
                persistent = False
            manager_utils.node_set_boot_device(task,
                                               boot_device,
                                               persistent=persistent)
Example #5
0
    def prepare_ramdisk(self, task, ramdisk_params):
        """Prepares the boot of Ironic ramdisk using PXE.

        This method prepares the boot of the deploy or rescue 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

        # Label indicating a deploy or rescue operation being carried out on
        # the node, 'deploy' or 'rescue'. Unless the node is in a rescue like
        # state, the mode is set to 'deploy', indicating deploy operation is
        # being carried out.
        mode = deploy_utils.rescue_or_deploy_mode(node)
        ipxe_enabled = CONF.pxe.ipxe_enabled
        if ipxe_enabled:
            # NOTE(mjturek): At this point, the ipxe boot script should
            # already exist as it is created at startup time. However, we
            # call the boot script create method here to assert its
            # existence and handle the unlikely case that it wasn't created
            # or was deleted.
            pxe_utils.create_ipxe_boot_script()

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

        pxe_info = pxe_utils.get_image_info(node, mode=mode)

        # NODE: Try to validate and fetch instance images only
        # if we are in DEPLOYING state.
        if node.provision_state == states.DEPLOYING:
            pxe_info.update(pxe_utils.get_instance_image_info(task))
            boot_mode_utils.sync_boot_mode(task)

        pxe_options = pxe_utils.build_pxe_config_options(
            task,
            pxe_info,
            ipxe_enabled=ipxe_enabled,
            ramdisk_params=ramdisk_params)
        # TODO(dtantsur): backwards compability hack, remove in the V release
        if ramdisk_params.get("ipa-api-url"):
            pxe_options["ipa-api-url"] = ramdisk_params["ipa-api-url"]

        pxe_config_template = deploy_utils.get_pxe_config_template(node)

        pxe_utils.create_pxe_config(task,
                                    pxe_options,
                                    pxe_config_template,
                                    ipxe_enabled=CONF.pxe.ipxe_enabled)

        persistent = self._persistent_ramdisk_boot(node)
        manager_utils.node_set_boot_device(task,
                                           boot_devices.PXE,
                                           persistent=persistent)

        if CONF.pxe.ipxe_enabled and CONF.pxe.ipxe_use_swift:
            kernel_label = '%s_kernel' % mode
            ramdisk_label = '%s_ramdisk' % mode
            pxe_info.pop(kernel_label, None)
            pxe_info.pop(ramdisk_label, None)

        if pxe_info:
            pxe_utils.cache_ramdisk_kernel(task,
                                           pxe_info,
                                           ipxe_enabled=CONF.pxe.ipxe_enabled)
        LOG.debug(
            'Ramdisk PXE boot for node %(node)s has been prepared '
            'with kernel params %(params)s', {
                'node': node.uuid,
                'params': pxe_options
            })
Example #6
0
    def prepare_ramdisk(self, task, ramdisk_params):
        """Prepares the boot of Ironic ramdisk using PXE.

        This method prepares the boot of the deploy or rescue 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.
        :param mode: Label indicating a deploy or rescue operation
            being carried out on the node. Supported values are
            'deploy' and 'rescue'. Defaults to 'deploy', indicating
            deploy operation is being carried out.
        :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
        mode = deploy_utils.rescue_or_deploy_mode(node)

        # NOTE(mjturek): At this point, the ipxe boot script should
        # already exist as it is created at startup time. However, we
        # call the boot script create method here to assert its
        # existence and handle the unlikely case that it wasn't created
        # or was deleted.
        pxe_utils.create_ipxe_boot_script()

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

        pxe_info = pxe_utils.get_image_info(node, mode=mode)

        # NODE: Try to validate and fetch instance images only
        # if we are in DEPLOYING state.
        if node.provision_state == states.DEPLOYING:
            pxe_info.update(
                pxe_utils.get_instance_image_info(task, ipxe_enabled=True))
            boot_mode_utils.sync_boot_mode(task)

        pxe_options = pxe_utils.build_pxe_config_options(task,
                                                         pxe_info,
                                                         ipxe_enabled=True)
        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,
                                    ipxe_enabled=True)
        persistent = strutils.bool_from_string(
            node.driver_info.get('force_persistent_boot_device', False))
        manager_utils.node_set_boot_device(task,
                                           boot_devices.PXE,
                                           persistent=persistent)

        if CONF.pxe.ipxe_use_swift:
            kernel_label = '%s_kernel' % mode
            ramdisk_label = '%s_ramdisk' % mode
            pxe_info.pop(kernel_label, None)
            pxe_info.pop(ramdisk_label, None)

        if pxe_info:
            pxe_utils.cache_ramdisk_kernel(task, pxe_info, ipxe_enabled=True)
Example #7
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)
        boot_mode_utils.configure_secure_boot_if_needed(task)

        node = task.node
        boot_option = deploy_utils.get_boot_option(node)
        boot_device = None
        instance_image_info = {}
        if boot_option == "ramdisk":
            instance_image_info = pxe_utils.get_instance_image_info(
                task, ipxe_enabled=self.ipxe_enabled)
            pxe_utils.cache_ramdisk_kernel(task, instance_image_info,
                                           ipxe_enabled=self.ipxe_enabled)

        if deploy_utils.is_iscsi_boot(task) or boot_option == "ramdisk":
            pxe_utils.prepare_instance_pxe_config(
                task, instance_image_info,
                iscsi_boot=deploy_utils.is_iscsi_boot(task),
                ramdisk_boot=(boot_option == "ramdisk"),
                ipxe_enabled=self.ipxe_enabled)
            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 = pxe_utils.get_instance_image_info(
                    task, ipxe_enabled=self.ipxe_enabled)
                pxe_utils.cache_ramdisk_kernel(task, instance_image_info,
                                               ipxe_enabled=self.ipxe_enabled)

            # If it's going to PXE boot we need to update the DHCP server
            dhcp_opts = pxe_utils.dhcp_options_for_instance(
                task, ipxe_enabled=self.ipxe_enabled, ip_version=4)
            dhcp_opts += pxe_utils.dhcp_options_for_instance(
                task, ipxe_enabled=self.ipxe_enabled, ip_version=6)
            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, ipxe_enabled=self.ipxe_enabled)
                    boot_device = boot_devices.DISK
            else:
                pxe_utils.build_service_pxe_config(
                    task, instance_image_info, root_uuid_or_disk_id,
                    ipxe_enabled=self.ipxe_enabled)
                boot_device = boot_devices.PXE
        else:
            # NOTE(dtantsur): create a PXE configuration as a safety net for
            # hardware uncapable of persistent boot. If on a reboot it will try
            # to boot from PXE, this configuration will return it back.
            if CONF.pxe.enable_netboot_fallback:
                pxe_utils.build_service_pxe_config(
                    task, instance_image_info,
                    task.node.driver_internal_info.get('root_uuid_or_disk_id'),
                    ipxe_enabled=self.ipxe_enabled,
                    # PXE config for whole disk images is identical to what
                    # we need to boot from local disk, so use True even
                    # for partition images.
                    is_whole_disk_image=True)
            else:
                # Clean up the deployment configuration
                pxe_utils.clean_up_pxe_config(
                    task, ipxe_enabled=self.ipxe_enabled)
            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)
Example #8
0
    def prepare_ramdisk(self, task, ramdisk_params):
        """Prepares the boot of deploy or rescue ramdisk over virtual media.

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

        :param task: A task from TaskManager.
        :param ramdisk_params: the parameters to be passed to the ramdisk.
        :returns: None
        :raises: MissingParameterValue, if some information is missing in
            node's driver_info or instance_info.
        :raises: InvalidParameterValue, if some information provided is
            invalid.
        :raises: IronicException, if some power or set boot boot device
            operation failed on the node.
        """
        node = task.node
        # NOTE(TheJulia): If this method is being called by something
        # aside from deployment, clean and rescue, such as conductor takeover,
        # we should treat this as a no-op and move on otherwise we would
        # modify the state of the node due to virtual media operations.
        if node.provision_state not in (states.DEPLOYING, states.CLEANING,
                                        states.RESCUING, states.INSPECTING):
            return

        d_info = _parse_driver_info(node)
        managers = redfish_utils.get_system(task.node).managers

        if manager_utils.is_fast_track(task):
            if _has_vmedia_device(managers,
                                  sushy.VIRTUAL_MEDIA_CD,
                                  inserted=True):
                LOG.debug(
                    'Fast track operation for node %s, not inserting '
                    'any devices', node.uuid)
                return
            else:
                LOG.warning(
                    'Fast track is possible for node %s, but no ISO '
                    'is currently inserted! Proceeding with '
                    'normal operation.', node.uuid)

        # NOTE(TheJulia): Since we're deploying, cleaning, or rescuing,
        # with virtual media boot, we should generate a token!
        # However, we don't have a way to inject it with a pre-built ISO
        # if a removable disk is not used.
        can_config = d_info.pop('can_provide_config', True)
        if can_config:
            manager_utils.add_secret_token(node, pregenerated=True)
            node.save()
            ramdisk_params['ipa-agent-token'] = \
                node.driver_internal_info['agent_secret_token']

        manager_utils.node_power_action(task, states.POWER_OFF)

        deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task)
        if deploy_nic_mac is not None:
            ramdisk_params['BOOTIF'] = deploy_nic_mac
        if CONF.debug and 'ipa-debug' not in ramdisk_params:
            ramdisk_params['ipa-debug'] = '1'

        # NOTE(TheJulia): This is a mandatory setting for virtual media
        # based deployment operations.
        ramdisk_params['boot_method'] = 'vmedia'

        config_via_removable = d_info.get('config_via_removable')
        if config_via_removable:

            removable = _has_vmedia_device(
                managers,
                # Prefer USB devices since floppies are outdated
                [sushy.VIRTUAL_MEDIA_USBSTICK, sushy.VIRTUAL_MEDIA_FLOPPY])
            if removable:
                floppy_ref = image_utils.prepare_floppy_image(
                    task, params=ramdisk_params)

                _eject_vmedia(task, managers, removable)
                _insert_vmedia(task, managers, floppy_ref, removable)

                LOG.info(
                    'Inserted virtual %(type)s device with configuration'
                    ' for node %(node)s', {
                        'node': task.node.uuid,
                        'type': removable
                    })

            else:
                LOG.warning(
                    'Config via a removable device is requested, but '
                    'virtual USB and floppy devices are not '
                    'available on node %(node)s', {'node': task.node.uuid})

        mode = deploy_utils.rescue_or_deploy_mode(node)

        iso_ref = image_utils.prepare_deploy_iso(task, ramdisk_params, mode,
                                                 d_info)

        _eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_CD)
        _insert_vmedia(task, managers, iso_ref, sushy.VIRTUAL_MEDIA_CD)

        del managers

        boot_mode_utils.sync_boot_mode(task)

        self._set_boot_device(task, boot_devices.CDROM)

        LOG.debug("Node %(node)s is set to one time boot from "
                  "%(device)s", {
                      'node': task.node.uuid,
                      'device': boot_devices.CDROM
                  })
Example #9
0
    def prepare_instance(self, task):
        """Prepares the boot of instance over virtual media.

        This method prepares the boot of the instance after reading
        relevant information from the node's instance_info.

        The internal logic is as follows:

        - If `boot_option` requested for this deploy is 'local', then set the
          node to boot from disk.
        - Unless `boot_option` requested for this deploy is 'ramdisk', pass
          root disk/partition ID to virtual media boot image
        - Otherwise build boot image, insert it into virtual media device
          and set node to boot from CD.

        :param task: a task from TaskManager.
        :returns: None
        :raises: InstanceDeployFailure, if its try to boot iSCSI volume in
                 'BIOS' boot mode.
        """
        node = task.node

        self._eject_all(task)

        boot_mode_utils.sync_boot_mode(task)
        boot_mode_utils.configure_secure_boot_if_needed(task)

        boot_option = deploy_utils.get_boot_option(node)
        iwdi = node.driver_internal_info.get('is_whole_disk_image')
        if boot_option == "local" or iwdi:
            self._set_boot_device(task, boot_devices.DISK, persistent=True)

            LOG.debug(
                "Node %(node)s is set to permanently boot from local "
                "%(device)s", {
                    'node': task.node.uuid,
                    'device': boot_devices.DISK
                })
            return

        params = {}

        if boot_option != 'ramdisk':
            root_uuid = node.driver_internal_info.get('root_uuid_or_disk_id')
            if not root_uuid and task.driver.storage.should_write_image(task):
                LOG.warning(
                    "The UUID of the root partition could not be found for "
                    "node %s. Booting instance from disk anyway.", node.uuid)

                self._set_boot_device(task, boot_devices.DISK, persistent=True)

                return

            params.update(root_uuid=root_uuid)

        managers = redfish_utils.get_system(task.node).managers

        deploy_info = _parse_deploy_info(node)
        configdrive = node.instance_info.get('configdrive')
        iso_ref = image_utils.prepare_boot_iso(task, deploy_info, **params)
        _eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_CD)
        _insert_vmedia(task, managers, iso_ref, sushy.VIRTUAL_MEDIA_CD)

        if configdrive and boot_option == 'ramdisk':
            _eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_USBSTICK)
            cd_ref = image_utils.prepare_configdrive_image(task, configdrive)
            try:
                _insert_vmedia(task, managers, cd_ref,
                               sushy.VIRTUAL_MEDIA_USBSTICK)
            except exception.InvalidParameterValue:
                raise exception.InstanceDeployFailure(
                    _('Cannot attach configdrive for node %s: no suitable '
                      'virtual USB slot has been found') % node.uuid)

        del managers

        self._set_boot_device(task, boot_devices.CDROM, persistent=True)

        LOG.debug(
            "Node %(node)s is set to permanently boot from "
            "%(device)s", {
                'node': task.node.uuid,
                'device': boot_devices.CDROM
            })
Example #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.

        :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
        instance_image_info = {}

        if boot_option == "ramdisk":
            instance_image_info = pxe_utils.get_instance_image_info(
                task, ipxe_enabled=True)
            pxe_utils.cache_ramdisk_kernel(task, instance_image_info,
                                           ipxe_enabled=True)

        if deploy_utils.is_iscsi_boot(task) or boot_option == "ramdisk":
            pxe_utils.prepare_instance_pxe_config(
                task, instance_image_info,
                iscsi_boot=deploy_utils.is_iscsi_boot(task),
                ramdisk_boot=(boot_option == "ramdisk"),
                ipxe_enabled=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 = pxe_utils.get_instance_image_info(
                    task, ipxe_enabled=True)
                pxe_utils.cache_ramdisk_kernel(task, instance_image_info,
                                               ipxe_enabled=True)

            # If it's going to PXE boot we need to update the DHCP server
            dhcp_opts = pxe_utils.dhcp_options_for_instance(task,
                                                            ipxe_enabled=True)
            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, ipxe_enabled=True)
                    boot_device = boot_devices.DISK
            else:
                pxe_utils.build_service_pxe_config(task, instance_image_info,
                                                   root_uuid_or_disk_id,
                                                   ipxe_enabled=True)
                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, ipxe_enabled=True)
            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:
            persistent = True
            if node.driver_info.get('force_persistent_boot_device',
                                    'Default') == 'Never':
                persistent = False
            manager_utils.node_set_boot_device(task, boot_device,
                                               persistent=persistent)
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 or rescue 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

        # Label indicating a deploy or rescue operation being carried out on
        # the node, 'deploy' or 'rescue'. Unless the node is in a rescue like
        # state, the mode is set to 'deploy', indicating deploy operation is
        # being carried out.
        mode = deploy_utils.rescue_or_deploy_mode(node)

        # NOTE(mjturek): At this point, the ipxe boot script should
        # already exist as it is created at startup time. However, we
        # call the boot script create method here to assert its
        # existence and handle the unlikely case that it wasn't created
        # or was deleted.
        pxe_utils.create_ipxe_boot_script()

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

        pxe_info = pxe_utils.get_image_info(node, mode=mode,
                                            ipxe_enabled=True)

        # NODE: Try to validate and fetch instance images only
        # if we are in DEPLOYING state.
        if node.provision_state == states.DEPLOYING:
            pxe_info.update(
                pxe_utils.get_instance_image_info(task, ipxe_enabled=True))
            boot_mode_utils.sync_boot_mode(task)

        pxe_options = pxe_utils.build_pxe_config_options(task, pxe_info,
                                                         ipxe_enabled=True)
        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,
                                    ipxe_enabled=True)
        persistent = False
        value = node.driver_info.get('force_persistent_boot_device',
                                     'Default')
        if value in {'Always', 'Default', 'Never'}:
            if value == 'Always':
                persistent = True
        else:
            persistent = strutils.bool_from_string(value, False)
        manager_utils.node_set_boot_device(task, boot_devices.PXE,
                                           persistent=persistent)

        if CONF.pxe.ipxe_use_swift:
            kernel_label = '%s_kernel' % mode
            ramdisk_label = '%s_ramdisk' % mode
            pxe_info.pop(kernel_label, None)
            pxe_info.pop(ramdisk_label, None)

        if pxe_info:
            pxe_utils.cache_ramdisk_kernel(task, pxe_info, ipxe_enabled=True)