Пример #1
0
    def validate(self, task):
        """Validate the PXE-specific info for booting deploy/instance images.

        This method validates the PXE-specific info for booting the
        ramdisk and instance on the node.  If invalid, raises an
        exception; otherwise returns None.

        :param task: a task from TaskManager.
        :returns: None
        :raises: InvalidParameterValue, if some parameters are invalid.
        :raises: MissingParameterValue, if some required parameters are
            missing.
        """
        self._validate_common(task)

        # NOTE(TheJulia): If we're not writing an image, we can skip
        # the remainder of this method.
        if (not task.driver.storage.should_write_image(task)):
            return

        node = task.node
        d_info = deploy_utils.get_image_instance_info(node)
        if (node.driver_internal_info.get('is_whole_disk_image')
                or deploy_utils.get_boot_option(node) == 'local'):
            props = []
        elif d_info.get('boot_iso'):
            props = ['boot_iso']
        elif service_utils.is_glance_image(d_info['image_source']):
            props = ['kernel_id', 'ramdisk_id']
            if deploy_utils.get_boot_option(node) == 'kickstart':
                props.append('squashfs_id')
        else:
            props = ['kernel', 'ramdisk']
        deploy_utils.validate_image_properties(task.context, d_info, props)
Пример #2
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
        """
        node = task.node
        boot_option = deploy_utils.get_boot_option(node)
        boot_device = None

        if boot_option != "local":
            # 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 iwdi:
                    LOG.warning(
                        _LW("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(
                        _LW("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"), {"node": task.node.uuid})
            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:
            deploy_utils.try_set_boot_device(task, boot_device)
Пример #3
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 database.

        :param task: a task from TaskManager.
        :returns: None
        """
        if task.node.driver_internal_info.get('boot_from_volume'):
            LOG.debug(
                'Node %(node)s is configured for booting from a remote '
                'volume.', {'node': task.node.uuid})
            self._configure_boot_from_volume(task)
            return

        _cleanup_vmedia_boot(task)

        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:
            driver_internal_info = node.driver_internal_info
            root_uuid_or_disk_id = driver_internal_info['root_uuid_or_disk_id']
            self._configure_vmedia_boot(task, root_uuid_or_disk_id)

        # Enable secure boot, if being requested
        if deploy_utils.is_secure_boot_requested(node):
            irmc_common.set_secure_boot_mode(node, enable=True)
Пример #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.
        It does the following depending on boot_option for deploy:

        - If 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.
        """

        ilo_common.cleanup_vmedia_boot(task)

        # For iscsi_ilo driver, we 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(_LW("The UUID for the root partition could not "
                                "be found for node %s"), node.uuid)
Пример #5
0
    def continue_deploy(self, task, **kwargs):
        """Method invoked when deployed with the IPA ramdisk.

        This method is invoked during a heartbeat from an agent when
        the node is in wait-call-back state. This deploys the image on
        the node and then configures the node to boot according to the
        desired boot option (netboot or localboot).

        :param task: a TaskManager object containing the node.
        :param kwargs: the kwargs passed from the heartbeat method.
        :raises: InstanceDeployFailure, if it encounters some error during
            the deploy.
        """
        task.process_event("resume")
        node = task.node
        LOG.debug("Continuing the deployment on node %s", node.uuid)

        uuid_dict_returned = do_agent_iscsi_deploy(task, self._client)

        if deploy_utils.get_boot_option(node) == "local":
            # Install the boot loader
            root_uuid = uuid_dict_returned.get("root uuid")
            efi_sys_uuid = uuid_dict_returned.get("efi system partition uuid")
            self.configure_local_boot(task, root_uuid=root_uuid, efi_system_part_uuid=efi_sys_uuid)

        try:
            task.driver.boot.prepare_instance(task)
        except Exception as e:
            LOG.error(
                _LE("Deploy failed for instance %(instance)s. " "Error: %(error)s"),
                {"instance": node.instance_uuid, "error": e},
            )
            msg = _("Failed to continue agent deployment.")
            deploy_utils.set_failed_state(task, msg)
        self.reboot_and_finish_deploy(task)
Пример #6
0
    def validate(self, task):
        """Validate the deployment information for the task's node.

        :param task: a TaskManager instance containing the node to act on.
        :raises: InvalidParameterValue, if some information is invalid.
        :raises: MissingParameterValue if 'kernel_id' and 'ramdisk_id' are
            missing in the Glance image or 'kernel' and 'ramdisk' not provided
            in instance_info for non-Glance image.
        """
        node = task.node
        boot_option = deploy_utils.get_boot_option(node)
        boot_iso = node.instance_info.get('ilo_boot_iso')
        if (boot_option == "ramdisk" and 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 with 'ramdisk' "
                                  "boot_option 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

        _validate_driver_info(task)

        if not task.driver.storage.should_write_image(task):
            return
        else:
            _validate_instance_image_info(task)
Пример #7
0
    def validate(self, task):
        """Validate the deployment information for the task's node.

        :param task: a TaskManager instance containing the node to act on.
        :raises: InvalidParameterValue, if some information is invalid.
        :raises: MissingParameterValue if 'kernel_id' and 'ramdisk_id' are
            missing in the Glance image or 'kernel' and 'ramdisk' not provided
            in instance_info for non-Glance image.
        """
        node = task.node
        boot_option = deploy_utils.get_boot_option(node)
        boot_iso = node.instance_info.get('ilo_boot_iso')
        if (boot_option == "ramdisk" and 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 with 'ramdisk' "
                                  "boot_option 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

        _validate_driver_info(task)

        if not task.driver.storage.should_write_image(task):
            return
        else:
            _validate_instance_image_info(task)
Пример #8
0
    def prepare_instance_to_boot(self, task, root_uuid, efi_sys_uuid,
                                 prep_boot_part_uuid=None):
        """Prepares instance to boot.

        :param task: a TaskManager object containing the node
        :param root_uuid: the UUID for root partition
        :param efi_sys_uuid: the UUID for the efi partition
        :raises: InvalidState if fails to prepare instance
        """

        node = task.node
        if deploy_utils.get_boot_option(node) == "local":
            # Install the boot loader
            self.configure_local_boot(
                task, root_uuid=root_uuid,
                efi_system_part_uuid=efi_sys_uuid,
                prep_boot_part_uuid=prep_boot_part_uuid)
        try:
            task.driver.boot.prepare_instance(task)
        except Exception as e:
            LOG.error('Preparing instance for booting failed for instance '
                      '%(instance)s. %(cls)s: %(error)s',
                      {'instance': node.instance_uuid,
                       'cls': e.__class__.__name__, 'error': e})
            msg = _('Failed to prepare instance for booting')
            log_and_raise_deployment_error(task, msg, exc=e)
Пример #9
0
    def _test_build_pxe_config_options_pxe(self, render_mock,
                                           whle_dsk_img=False):
        self.config(pxe_append_params='test_param', group='pxe')
        # NOTE: right '/' should be removed from url string
        self.config(api_url='http://192.168.122.184:6385', group='conductor')

        driver_internal_info = self.node.driver_internal_info
        driver_internal_info['is_whole_disk_image'] = whle_dsk_img
        self.node.driver_internal_info = driver_internal_info
        self.node.save()

        tftp_server = CONF.pxe.tftp_server

        deploy_kernel = os.path.join(self.node.uuid, 'deploy_kernel')
        deploy_ramdisk = os.path.join(self.node.uuid, 'deploy_ramdisk')
        kernel = os.path.join(self.node.uuid, 'kernel')
        ramdisk = os.path.join(self.node.uuid, 'ramdisk')
        root_dir = CONF.pxe.tftp_root

        image_info = {
            'deploy_kernel': ('deploy_kernel',
                              os.path.join(root_dir,
                                           self.node.uuid,
                                           'deploy_kernel')),
            'deploy_ramdisk': ('deploy_ramdisk',
                               os.path.join(root_dir,
                                            self.node.uuid,
                                            'deploy_ramdisk'))
        }

        if (whle_dsk_img or
            deploy_utils.get_boot_option(self.node) == 'local'):
                ramdisk = 'no_ramdisk'
                kernel = 'no_kernel'
        else:
            image_info.update({
                'kernel': ('kernel_id',
                           os.path.join(root_dir,
                                        self.node.uuid,
                                        'kernel')),
                'ramdisk': ('ramdisk_id',
                            os.path.join(root_dir,
                                         self.node.uuid,
                                         'ramdisk'))
            })

        expected_options = {
            'ari_path': ramdisk,
            'deployment_ari_path': deploy_ramdisk,
            'pxe_append_params': 'test_param',
            'aki_path': kernel,
            'deployment_aki_path': deploy_kernel,
            'tftp_server': tftp_server,
            'ipxe_timeout': 0,
        }

        with task_manager.acquire(self.context, self.node.uuid,
                                  shared=True) as task:
            options = pxe._build_pxe_config_options(task, image_info)
        self.assertEqual(expected_options, options)
Пример #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 database.

        :param task: a task from TaskManager.
        :returns: None
        """
        if task.node.driver_internal_info.get('boot_from_volume'):
            LOG.debug('Node %(node)s is configured for booting from a remote '
                      'volume.',
                      {'node': task.node.uuid})
            self._configure_boot_from_volume(task)
            return

        _cleanup_vmedia_boot(task)

        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:
            driver_internal_info = node.driver_internal_info
            root_uuid_or_disk_id = driver_internal_info['root_uuid_or_disk_id']
            self._configure_vmedia_boot(task, root_uuid_or_disk_id)

        # Enable secure boot, if being requested
        if deploy_utils.is_secure_boot_requested(node):
            irmc_common.set_secure_boot_mode(node, enable=True)
Пример #11
0
    def continue_deploy(self, task, **kwargs):
        """Method invoked when deployed with the IPA ramdisk.

        This method is invoked during a heartbeat from an agent when
        the node is in wait-call-back state. This deploys the image on
        the node and then configures the node to boot according to the
        desired boot option (netboot or localboot).

        :param task: a TaskManager object containing the node.
        :param kwargs: the kwargs passed from the heartbeat method.
        :raises: InstanceDeployFailure, if it encounters some error during
            the deploy.
        """
        node = task.node
        task.process_event('resume')

        LOG.debug('Continuing IPA deployment on node %s', node.uuid)

        is_whole_disk_image = node.driver_internal_info.get(
            'is_whole_disk_image')
        _cleanup_vmedia_boot(task)
        uuid_dict = iscsi_deploy.do_agent_iscsi_deploy(task, self._client)
        root_uuid = uuid_dict.get('root uuid')

        if (deploy_utils.get_boot_option(node) == "local" or
            is_whole_disk_image):
            efi_system_part_uuid = uuid_dict.get(
                'efi system partition uuid')
            self.configure_local_boot(
                task, root_uuid=root_uuid,
                efi_system_part_uuid=efi_system_part_uuid)
        else:
            self._configure_vmedia_boot(task, root_uuid)

        self.reboot_and_finish_deploy(task)
Пример #12
0
    def _validate_instance_info(self, task):
        """Validate instance image information for the task's node.

        This method validates whether the 'instance_info' property of the
        supplied node contains the required information for this driver.

        :param task: a TaskManager instance containing the node to act on.
        :raises: InvalidParameterValue if any parameters are incorrect
        :raises: MissingParameterValue if some mandatory information
            is missing on the node
        """
        node = task.node

        # NOTE(dtantsur): if we're are writing an image with local boot
        # the boot interface does not care about image parameters and
        # must not validate them.
        if (not task.driver.storage.should_write_image(task)
                or deploy_utils.get_boot_option(node) == 'local'):
            return

        d_info = _parse_deploy_info(node)

        if node.driver_internal_info.get('is_whole_disk_image'):
            props = []
        elif d_info.get('boot_iso'):
            props = ['boot_iso']
        elif service_utils.is_glance_image(d_info['image_source']):
            props = ['kernel_id', 'ramdisk_id']

        else:
            props = ['kernel', 'ramdisk']

        deploy_utils.validate_image_properties(task.context, d_info, props)
Пример #13
0
    def prepare(self, task):
        node = task.node
        # Log a warning if the boot_option is wrong... and
        # otherwise reset it.
        boot_option = deploy_utils.get_boot_option(node)
        if boot_option != 'ramdisk':
            LOG.warning(
                'Incorrect "boot_option" set for node %(node)s '
                'and will be overridden to "ramdisk" as to '
                'match the deploy interface. Found: %(boot_opt)s.', {
                    'node': node.uuid,
                    'boot_opt': boot_option
                })
            i_info = task.node.instance_info
            i_info.update({'capabilities': {'boot_option': 'ramdisk'}})
            node.instance_info = i_info
            node.save()

        deploy_utils.populate_storage_driver_internal_info(task)
        if node.provision_state == states.DEPLOYING:
            # Ask the network interface to validate itself so
            # we can ensure we are able to proceed.
            task.driver.network.validate(task)

            manager_utils.node_power_action(task, states.POWER_OFF)
            # NOTE(TheJulia): If this was any other interface, we would
            # unconfigure tenant networks, add provisioning networks, etc.
            task.driver.storage.attach_volumes(task)
        if node.provision_state in (states.ACTIVE, states.UNRESCUING):
            # In the event of takeover or unrescue.
            task.driver.boot.prepare_instance(task)
Пример #14
0
    def prepare(self, task):
        node = task.node
        # Log a warning if the boot_option is wrong... and
        # otherwise reset it.
        boot_option = deploy_utils.get_boot_option(node)
        if boot_option != 'ramdisk':
            LOG.warning('Incorrect "boot_option" set for node %(node)s '
                        'and will be overridden to "ramdisk" as to '
                        'match the deploy interface. Found: %(boot_opt)s.',
                        {'node': node.uuid,
                         'boot_opt': boot_option})
            i_info = task.node.instance_info
            i_info.update({'capabilities': {'boot_option': 'ramdisk'}})
            node.instance_info = i_info
            node.save()

        deploy_utils.populate_storage_driver_internal_info(task)
        if node.provision_state == states.DEPLOYING:
            # Ask the network interface to validate itself so
            # we can ensure we are able to proceed.
            task.driver.network.validate(task)

            manager_utils.node_power_action(task, states.POWER_OFF)
            # NOTE(TheJulia): If this was any other interface, we would
            # unconfigure tenant networks, add provisioning networks, etc.
            task.driver.storage.attach_volumes(task)
        if node.provision_state in (states.ACTIVE, states.UNRESCUING):
            # In the event of takeover or unrescue.
            task.driver.boot.prepare_instance(task)
Пример #15
0
    def prepare_instance_to_boot(self, task, root_uuid, efi_sys_uuid,
                                 prep_boot_part_uuid=None):
        """Prepares instance to boot.

        :param task: a TaskManager object containing the node
        :param root_uuid: the UUID for root partition
        :param efi_sys_uuid: the UUID for the efi partition
        :raises: InvalidState if fails to prepare instance
        """

        node = task.node
        if deploy_utils.get_boot_option(node) == "local":
            # Install the boot loader
            self.configure_local_boot(
                task, root_uuid=root_uuid,
                efi_system_part_uuid=efi_sys_uuid,
                prep_boot_part_uuid=prep_boot_part_uuid)
        try:
            task.driver.boot.prepare_instance(task)
        except Exception as e:
            LOG.error('Preparing instance for booting failed for instance '
                      '%(instance)s. %(cls)s: %(error)s',
                      {'instance': node.instance_uuid,
                       'cls': e.__class__.__name__, 'error': e})
            msg = _('Failed to prepare instance for booting')
            log_and_raise_deployment_error(task, msg, exc=e)
Пример #16
0
    def clean_up_instance(self, task):
        """Cleans up the boot of instance.

        This method cleans up the environment that was setup for booting
        the instance.

        :param task: A task from TaskManager.
        :returns: None
        """
        LOG.debug("Cleaning up instance boot for "
                  "%(node)s", {'node': task.node.uuid})

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

        _eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_CD)
        d_info = task.node.driver_info
        config_via_floppy = d_info.get('config_via_floppy')
        if config_via_floppy:
            _eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_FLOPPY)

        boot_option = deploy_utils.get_boot_option(task.node)
        if (boot_option == 'ramdisk'
                and task.node.instance_info.get('configdrive')):
            _eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_USBSTICK)
            image_utils.cleanup_disk_image(task, prefix='configdrive')

        image_utils.cleanup_iso_image(task)
Пример #17
0
def finish_deploy(task, address):
    """Notifies the ramdisk to reboot the node and makes the instance active.

    This method notifies the ramdisk to proceed to reboot and then
    makes the instance active.

    :param task: a TaskManager object.
    :param address: The IP address of the bare metal node.
    :raises: InstanceDeployFailure, if notifying ramdisk failed.
    """
    node = task.node
    try:
        deploy_utils.notify_ramdisk_to_proceed(address)
    except Exception as e:
        LOG.error(
            _LE("Deploy failed for instance %(instance)s. " "Error: %(error)s"),
            {"instance": node.instance_uuid, "error": e},
        )
        msg = _("Failed to notify ramdisk to reboot after bootloader " "installation. Error: %s") % e
        deploy_utils.set_failed_state(task, msg)
        raise exception.InstanceDeployFailure(msg)

    # TODO(lucasagomes): When deploying a node with the DIB ramdisk
    # Ironic will not power control the node at the end of the deployment,
    # it's the DIB ramdisk that reboots the node. But, for the SSH driver
    # some changes like setting the boot device only gets applied when the
    # machine is powered off and on again. So the code below is enforcing
    # it. For Liberty we need to change the DIB ramdisk so that Ironic
    # always controls the power state of the node for all drivers.
    if deploy_utils.get_boot_option(node) == "local" and "ssh" in node.driver:
        manager_utils.node_power_action(task, states.REBOOT)

    LOG.info(_LI("Deployment to node %s done"), node.uuid)
    task.process_event("done")
Пример #18
0
def finish_deploy(task, address):
    """Notifies the ramdisk to reboot the node and makes the instance active.

    This method notifies the ramdisk to proceed to reboot and then
    makes the instance active.

    :param task: a TaskManager object.
    :param address: The IP address of the bare metal node.
    :raises: InstanceDeployFailure, if notifying ramdisk failed.
    """
    node = task.node
    try:
        deploy_utils.notify_ramdisk_to_proceed(address)
    except Exception as e:
        LOG.error(_LE('Deploy failed for instance %(instance)s. '
                      'Error: %(error)s'),
                  {'instance': node.instance_uuid, 'error': e})
        msg = (_('Failed to notify ramdisk to reboot after bootloader '
                 'installation. Error: %s') % e)
        deploy_utils.set_failed_state(task, msg)
        raise exception.InstanceDeployFailure(msg)

    # TODO(lucasagomes): When deploying a node with the DIB ramdisk
    # Ironic will not power control the node at the end of the deployment,
    # it's the DIB ramdisk that reboots the node. But, for the SSH driver
    # some changes like setting the boot device only gets applied when the
    # machine is powered off and on again. So the code below is enforcing
    # it. For Liberty we need to change the DIB ramdisk so that Ironic
    # always controls the power state of the node for all drivers.
    if deploy_utils.get_boot_option(node) == "local" and 'ssh' in node.driver:
        manager_utils.node_power_action(task, states.REBOOT)

    LOG.info(_LI('Deployment to node %s done'), node.uuid)
    task.process_event('done')
Пример #19
0
    def clean_up_instance(self, task):
        """Cleans up the boot of instance.

        This method cleans up the environment that was setup for booting
        the instance.

        :param task: a task from TaskManager.
        :returns: None
        :raises: IRMCOperationError if iRMC operation failed.
        """
        if task.node.driver_internal_info.get('boot_from_volume'):
            self._cleanup_boot_from_volume(task)
            return

        # Disable secure boot, if enabled secure boot
        if deploy_utils.is_secure_boot_requested(task.node):
            irmc_common.set_secure_boot_mode(task.node, enable=False)

        _remove_share_file(_get_iso_name(task.node, label='boot'))
        driver_internal_info = task.node.driver_internal_info
        driver_internal_info.pop('irmc_boot_iso', None)

        # When rescue, this function is called. But we need to retain the
        # root_uuid_or_disk_id to use on unrescue (see prepare_instance).
        boot_local_or_iwdi = (
            deploy_utils.get_boot_option(task.node) == "local"
            or driver_internal_info.get('is_whole_disk_image'))
        if task.node.provision_state != states.RESCUING or boot_local_or_iwdi:
            driver_internal_info.pop('root_uuid_or_disk_id', None)

        task.node.driver_internal_info = driver_internal_info
        task.node.save()
        _cleanup_vmedia_boot(task)
Пример #20
0
    def prepare_instance_to_boot(self, task, root_uuid, efi_sys_uuid):
        """Prepares instance to boot.

        :param task: a TaskManager object containing the node
        :param root_uuid: the UUID for root partition
        :param efi_sys_uuid: the UUID for the efi partition
        :raises: InvalidState if fails to prepare instance
        """

        node = task.node
        if deploy_utils.get_boot_option(node) == "local":
            # Install the boot loader
            self.configure_local_boot(task,
                                      root_uuid=root_uuid,
                                      efi_system_part_uuid=efi_sys_uuid)
        try:
            task.driver.boot.prepare_instance(task)
        except Exception as e:
            LOG.error(
                _LE('Deploy failed for instance %(instance)s. '
                    'Error: %(error)s'), {
                        'instance': node.instance_uuid,
                        'error': e
                    })
            msg = _('Failed to continue agent deployment.')
            self._log_and_raise_deployment_error(task, msg)
Пример #21
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})
Пример #22
0
    def pass_deploy_info(self, task, **kwargs):
        """Continues the iSCSI deployment from where ramdisk left off.

        This method continues the iSCSI deployment from the conductor node
        and writes the deploy image to the bare metal's disk. After that,
        it does the following depending on boot_option for deploy:

        - If the boot_option requested for this deploy is 'local', then it
          sets the node to boot from disk (ramdisk installs the boot loader
          present within the image to the bare metal's disk).
        - If the boot_option requested is 'netboot' or no boot_option is
          requested, 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 TaskManager instance containing the node to act on.
        :param kwargs: kwargs containing parameters for iSCSI deployment.
        :raises: InvalidState
        """
        node = task.node
        task.process_event('resume')

        iwdi = node.driver_internal_info.get('is_whole_disk_image')
        ilo_common.cleanup_vmedia_boot(task)
        uuid_dict = iscsi_deploy.continue_deploy(task, **kwargs)
        root_uuid_or_disk_id = uuid_dict.get(
            'root uuid', uuid_dict.get('disk identifier'))

        try:
            # Set boot mode
            ilo_common.update_boot_mode(task)

            # Need to enable secure boot, if being requested
            _update_secure_boot_mode(task, True)

            # For iscsi_ilo driver, we boot from disk every time if the image
            # deployed is a 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)

                # Ask the ramdisk to install bootloader and
                # wait for the call-back through the vendor passthru
                # 'pass_bootloader_install_info', if it's not a whole
                # disk image.
                if not iwdi:
                    deploy_utils.notify_ramdisk_to_proceed(kwargs['address'])
                    task.process_event('wait')
                    return
            else:
                self._configure_vmedia_boot(task, root_uuid_or_disk_id)
        except Exception as e:
            LOG.error(_LE('Deploy failed for instance %(instance)s. '
                          'Error: %(error)s'),
                      {'instance': node.instance_uuid, 'error': e})
            msg = _('Failed to continue iSCSI deployment.')
            deploy_utils.set_failed_state(task, msg)
        else:
            iscsi_deploy.finish_deploy(task, kwargs.get('address'))
Пример #23
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_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, sets the node boot option
          to UEFIHTTP and sets the URL as the boot ISO to boot the instance
          image.

        :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.
        """
        node = task.node
        image_utils.cleanup_iso_image(task)
        boot_option = deploy_utils.get_boot_option(task.node)

        iwdi = node.driver_internal_info.get('is_whole_disk_image')
        if boot_option == "local" or iwdi:
            manager_utils.node_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)
                manager_utils.node_set_boot_device(task, boot_devices.DISK,
                                                   persistent=True)

                return
            params.update(root_uuid=root_uuid)

        d_info = self._parse_deploy_info(node)
        iso_ref = image_utils.prepare_boot_iso(task, d_info, **params)

        if boot_option != 'ramdisk':
            i_info = node.instance_info
            i_info['ilo_boot_iso'] = iso_ref
            node.instance_info = i_info
            node.save()

        ilo_common.setup_uefi_https(task, iso_ref, persistent=True)

        LOG.debug("Node %(node)s is set to boot from UEFIHTTP "
                  "boot option", {'node': task.node.uuid})
Пример #24
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,
        }

        if (node.instance_info.get('image_os_hash_algo')
                and node.instance_info.get('image_os_hash_value')):
            image_info['os_hash_algo'] = node.instance_info[
                'image_os_hash_algo']
            image_info['os_hash_value'] = node.instance_info[
                'image_os_hash_value']

        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)
            image_info['deploy_boot_mode'] = (
                boot_mode_utils.get_boot_mode(node))
            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')
Пример #25
0
    def _validate_common(self, task):
        node = task.node

        if not driver_utils.get_node_mac_addresses(task):
            raise exception.MissingParameterValue(
                _("Node %s does not have any port associated with it.") %
                node.uuid)

        if self.ipxe_enabled:
            if not CONF.deploy.http_url or not CONF.deploy.http_root:
                raise exception.MissingParameterValue(
                    _("iPXE boot is enabled but no HTTP URL or HTTP "
                      "root was specified."))

        # NOTE(zer0c00l): When 'kickstart' boot option is used we need to store
        # kickstart and squashfs files in http_root directory. These files
        # will be eventually requested by anaconda installer during deployment
        # over http(s).
        if deploy_utils.get_boot_option(node) == 'kickstart':
            if not CONF.deploy.http_url or not CONF.deploy.http_root:
                raise exception.MissingParameterValue(
                    _("'kickstart' boot option is set on the node but no HTTP "
                      "URL or HTTP root was specified."))

            if not CONF.anaconda.default_ks_template:
                raise exception.MissingParameterValue(
                    _("'kickstart' boot option is set on the node but no "
                      "default kickstart template is specified."))

        # Check the trusted_boot capabilities value.
        deploy_utils.validate_capabilities(node)
        if deploy_utils.is_trusted_boot_requested(node):
            # Check if 'boot_option' and boot mode is compatible with
            # trusted boot.
            if self.ipxe_enabled:
                # NOTE(TheJulia): So in theory (huge theory here, not put to
                # practice or tested), that one can define the kernel as tboot
                # and define the actual kernel and ramdisk as appended data.
                # Similar to how one can iPXE load the XEN hypervisor.
                # tboot mailing list seem to indicate pxe/ipxe support, or
                # more specifically avoiding breaking the scenarios of use,
                # but there is also no definitive documentation on the subject.
                LOG.warning(
                    'Trusted boot has been requested for %(node)s in '
                    'concert with iPXE. This is not a supported '
                    'configuration for an ironic deployment.',
                    {'node': node.uuid})
            pxe_utils.validate_boot_parameters_for_trusted_boot(node)

        # Check if we have invalid parameters being passed which will not work
        # for ramdisk configurations.
        if (node.instance_info.get('image_source')
                and node.instance_info.get('boot_iso')):
            raise exception.InvalidParameterValue(
                _("An 'image_source' and 'boot_iso' parameter may not be "
                  "specified at the same time."))

        pxe_utils.parse_driver_info(node)
Пример #26
0
    def validate(self, task):
        """Validate the PXE-specific info for booting deploy/instance images.

        This method validates the PXE-specific info for booting the
        ramdisk and instance on the node.  If invalid, raises an
        exception; otherwise returns None.

        :param task: a task from TaskManager.
        :returns: None
        :raises: InvalidParameterValue, if some parameters are invalid.
        :raises: MissingParameterValue, if some required parameters are
            missing.
        """
        node = task.node

        if not driver_utils.get_node_mac_addresses(task):
            raise exception.MissingParameterValue(
                _("Node %s does not have any port associated with it.") %
                node.uuid)

        if not CONF.deploy.http_url or not CONF.deploy.http_root:
            raise exception.MissingParameterValue(
                _("iPXE boot is enabled but no HTTP URL or HTTP "
                  "root was specified."))

        # Check the trusted_boot capabilities value.
        deploy_utils.validate_capabilities(node)
        if deploy_utils.is_trusted_boot_requested(node):
            # Check if 'boot_option' and boot mode is compatible with
            # trusted boot.
            # NOTE(TheJulia): So in theory (huge theory here, not put to
            # practice or tested), that one can define the kernel as tboot
            # and define the actual kernel and ramdisk as appended data.
            # Similar to how one can iPXE load the XEN hypervisor.
            # tboot mailing list seem to indicate pxe/ipxe support, or
            # more specifically avoiding breaking the scenarios of use,
            # but there is also no definitive documentation on the subject.
            LOG.warning(
                'Trusted boot has been requested for %(node)s in '
                'concert with iPXE. This is not a supported '
                'configuration for an ironic deployment.', {'node': node.uuid})
            pxe.validate_boot_parameters_for_trusted_boot(node)

        pxe_utils.parse_driver_info(node)
        # NOTE(TheJulia): If we're not writing an image, we can skip
        # the remainder of this method.
        if (not task.driver.storage.should_write_image(task)):
            return

        d_info = deploy_utils.get_image_instance_info(node)
        if (node.driver_internal_info.get('is_whole_disk_image')
                or deploy_utils.get_boot_option(node) == 'local'):
            props = []
        elif service_utils.is_glance_image(d_info['image_source']):
            props = ['kernel_id', 'ramdisk_id']
        else:
            props = ['kernel', 'ramdisk']
        deploy_utils.validate_image_properties(task.context, d_info, props)
Пример #27
0
def get_deploy_info(node, address, iqn, port=None, lun='1', conv_flags=None):
    """Returns the information required for doing iSCSI deploy in a dictionary.

    :param node: ironic node object
    :param address: iSCSI address
    :param iqn: iSCSI iqn for the target disk
    :param port: iSCSI port, defaults to one specified in the configuration
    :param lun: iSCSI lun, defaults to '1'
    :param conv_flags: flag that will modify the behaviour of the image copy
        to disk.
    :raises: MissingParameterValue, if some required parameters were not
        passed.
    :raises: InvalidParameterValue, if any of the parameters have invalid
        value.
    """
    i_info = deploy_utils.parse_instance_info(node)

    params = {
        'address': address,
        'port': port or CONF.iscsi.portal_port,
        'iqn': iqn,
        'lun': lun,
        'image_path': deploy_utils._get_image_file_path(node.uuid),
        'node_uuid': node.uuid}

    is_whole_disk_image = node.driver_internal_info['is_whole_disk_image']
    if not is_whole_disk_image:
        params.update({'root_mb': i_info['root_mb'],
                       'swap_mb': i_info['swap_mb'],
                       'ephemeral_mb': i_info['ephemeral_mb'],
                       'preserve_ephemeral': i_info['preserve_ephemeral'],
                       'boot_option': deploy_utils.get_boot_option(node),
                       'boot_mode': _get_boot_mode(node),
                       'cpu_arch': node.properties.get('cpu_arch')})

        # Append disk label if specified
        disk_label = deploy_utils.get_disk_label(node)
        if disk_label is not None:
            params['disk_label'] = disk_label

    missing = [key for key in params if params[key] is None]
    if missing:
        raise exception.MissingParameterValue(
            _("Parameters %s were not passed to ironic"
              " for deploy.") % missing)

    # configdrive is nullable
    params['configdrive'] = i_info.get('configdrive')
    if is_whole_disk_image:
        return params

    if conv_flags:
        params['conv_flags'] = conv_flags

    # ephemeral_format is nullable
    params['ephemeral_format'] = i_info.get('ephemeral_format')

    return params
Пример #28
0
    def validate(self, task):
        """Validate the PXE-specific info for booting deploy/instance images.

        This method validates the PXE-specific info for booting the
        ramdisk and instance on the node.  If invalid, raises an
        exception; otherwise returns None.

        :param task: a task from TaskManager.
        :returns: None
        :raises: InvalidParameterValue, if some parameters are invalid.
        :raises: MissingParameterValue, if some required parameters are
            missing.
        """
        node = task.node

        if not driver_utils.get_node_mac_addresses(task):
            raise exception.MissingParameterValue(
                _("Node %s does not have any port associated with it.")
                % node.uuid)

        if not CONF.deploy.http_url or not CONF.deploy.http_root:
            raise exception.MissingParameterValue(_(
                "iPXE boot is enabled but no HTTP URL or HTTP "
                "root was specified."))

        # Check the trusted_boot capabilities value.
        deploy_utils.validate_capabilities(node)
        if deploy_utils.is_trusted_boot_requested(node):
            # Check if 'boot_option' and boot mode is compatible with
            # trusted boot.
            # NOTE(TheJulia): So in theory (huge theory here, not put to
            # practice or tested), that one can define the kernel as tboot
            # and define the actual kernel and ramdisk as appended data.
            # Similar to how one can iPXE load the XEN hypervisor.
            # tboot mailing list seem to indicate pxe/ipxe support, or
            # more specifically avoiding breaking the scenarios of use,
            # but there is also no definitive documentation on the subject.
            LOG.warning('Trusted boot has been requested for %(node)s in '
                        'concert with iPXE. This is not a supported '
                        'configuration for an ironic deployment.',
                        {'node': node.uuid})
            pxe.validate_boot_parameters_for_trusted_boot(node)

        pxe_utils.parse_driver_info(node)
        # NOTE(TheJulia): If we're not writing an image, we can skip
        # the remainder of this method.
        if (not task.driver.storage.should_write_image(task)):
            return

        d_info = deploy_utils.get_image_instance_info(node)
        if (node.driver_internal_info.get('is_whole_disk_image')
                or deploy_utils.get_boot_option(node) == 'local'):
            props = []
        elif service_utils.is_glance_image(d_info['image_source']):
            props = ['kernel_id', 'ramdisk_id']
        else:
            props = ['kernel', 'ramdisk']
        deploy_utils.validate_image_properties(task.context, d_info, props)
Пример #29
0
    def pass_deploy_info(self, task, **kwargs):
        """Continues the deployment of baremetal node over iSCSI.

        This method continues the deployment of the baremetal node over iSCSI
        from where the deployment ramdisk has left off.

        :param task: a TaskManager instance containing the node to act on.
        :param kwargs: kwargs for performing iscsi deployment.
        :raises: InvalidState
        """
        node = task.node
        LOG.warning(_LW("The node %s is using the bash deploy ramdisk for "
                        "its deployment. This deploy ramdisk has been "
                        "deprecated. Please use the ironic-python-agent "
                        "(IPA) ramdisk instead."), node.uuid)

        # TODO(rameshg87): Remove the below code once we stop supporting
        # bash ramdisk in Ironic.
        if node.provision_state == states.CLEANWAIT:
            return self._initiate_cleaning(task)

        task.process_event('resume')
        LOG.debug('Continuing the deployment on node %s', node.uuid)

        is_whole_disk_image = node.driver_internal_info['is_whole_disk_image']
        uuid_dict_returned = continue_deploy(task, **kwargs)
        root_uuid_or_disk_id = uuid_dict_returned.get(
            'root uuid', uuid_dict_returned.get('disk identifier'))

        # save the node's root disk UUID so that another conductor could
        # rebuild the PXE config file. Due to a shortcoming in Nova objects,
        # we have to assign to node.driver_internal_info so the node knows it
        # has changed.
        driver_internal_info = node.driver_internal_info
        driver_internal_info['root_uuid_or_disk_id'] = root_uuid_or_disk_id
        node.driver_internal_info = driver_internal_info
        node.save()

        try:
            task.driver.boot.prepare_instance(task)

            if deploy_utils.get_boot_option(node) == "local":
                if not is_whole_disk_image:
                    LOG.debug('Installing the bootloader on node %s',
                              node.uuid)
                    deploy_utils.notify_ramdisk_to_proceed(kwargs['address'])
                    task.process_event('wait')
                    return

        except Exception as e:
            LOG.error(_LE('Deploy failed for instance %(instance)s. '
                          'Error: %(error)s'),
                      {'instance': node.instance_uuid, 'error': e})
            msg = _('Failed to continue iSCSI deployment.')
            deploy_utils.set_failed_state(task, msg)
        else:
            finish_deploy(task, kwargs.get('address'))
Пример #30
0
    def pass_deploy_info(self, task, **kwargs):
        """Continues the deployment of baremetal node over iSCSI.

        This method continues the deployment of the baremetal node over iSCSI
        from where the deployment ramdisk has left off.

        :param task: a TaskManager instance containing the node to act on.
        :param kwargs: kwargs for performing iscsi deployment.
        :raises: InvalidState
        """
        node = task.node
        LOG.warning(_LW("The node %s is using the bash deploy ramdisk for "
                        "its deployment. This deploy ramdisk has been "
                        "deprecated. Please use the ironic-python-agent "
                        "(IPA) ramdisk instead."), node.uuid)

        # TODO(rameshg87): Remove the below code once we stop supporting
        # bash ramdisk in Ironic.
        if node.provision_state == states.CLEANWAIT:
            return self._initiate_cleaning(task)

        task.process_event('resume')
        LOG.debug('Continuing the deployment on node %s', node.uuid)

        is_whole_disk_image = node.driver_internal_info['is_whole_disk_image']
        uuid_dict_returned = continue_deploy(task, **kwargs)
        root_uuid_or_disk_id = uuid_dict_returned.get(
            'root uuid', uuid_dict_returned.get('disk identifier'))

        # save the node's root disk UUID so that another conductor could
        # rebuild the PXE config file. Due to a shortcoming in Nova objects,
        # we have to assign to node.driver_internal_info so the node knows it
        # has changed.
        driver_internal_info = node.driver_internal_info
        driver_internal_info['root_uuid_or_disk_id'] = root_uuid_or_disk_id
        node.driver_internal_info = driver_internal_info
        node.save()

        try:
            task.driver.boot.prepare_instance(task)

            if deploy_utils.get_boot_option(node) == "local":
                if not is_whole_disk_image:
                    LOG.debug('Installing the bootloader on node %s',
                              node.uuid)
                    deploy_utils.notify_ramdisk_to_proceed(kwargs['address'])
                    task.process_event('wait')
                    return

        except Exception as e:
            LOG.error(_LE('Deploy failed for instance %(instance)s. '
                          'Error: %(error)s'),
                      {'instance': node.instance_uuid, 'error': e})
            msg = _('Failed to continue iSCSI deployment.')
            deploy_utils.set_failed_state(task, msg)
        else:
            finish_deploy(task, kwargs.get('address'))
Пример #31
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
        """
        node = task.node
        boot_option = deploy_utils.get_boot_option(node)

        if boot_option != "local":
            # 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 iwdi:
                    LOG.warn(_LW("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.warn(_LW("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"),
                             {"node": task.node.uuid})
            else:
                pxe_config_path = pxe_utils.get_pxe_config_file_path(
                    task.node.uuid)
                deploy_utils.switch_pxe_config(
                    pxe_config_path, root_uuid_or_disk_id,
                    deploy_utils.get_boot_mode_for_deploy(node),
                    iwdi, deploy_utils.is_trusted_boot_requested(node))

        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)
Пример #32
0
    def pass_deploy_info(self, task, **kwargs):
        """Continues the deployment of baremetal node over iSCSI.

        This method continues the deployment of the baremetal node over iSCSI
        from where the deployment ramdisk has left off.

        :param task: a TaskManager instance containing the node to act on.
        :param kwargs: kwargs for performing iscsi deployment.
        :raises: InvalidState
        """
        node = task.node
        LOG.warning(
            _LW(
                "The node %s is using the bash deploy ramdisk for "
                "its deployment. This deploy ramdisk has been "
                "deprecated. Please use the ironic-python-agent "
                "(IPA) ramdisk instead."
            ),
            node.uuid,
        )
        task.process_event("resume")
        LOG.debug("Continuing the deployment on node %s", node.uuid)

        is_whole_disk_image = node.driver_internal_info["is_whole_disk_image"]
        uuid_dict_returned = continue_deploy(task, **kwargs)
        root_uuid_or_disk_id = uuid_dict_returned.get("root uuid", uuid_dict_returned.get("disk identifier"))

        # save the node's root disk UUID so that another conductor could
        # rebuild the PXE config file. Due to a shortcoming in Nova objects,
        # we have to assign to node.driver_internal_info so the node knows it
        # has changed.
        driver_internal_info = node.driver_internal_info
        driver_internal_info["root_uuid_or_disk_id"] = root_uuid_or_disk_id
        node.driver_internal_info = driver_internal_info
        node.save()

        try:
            task.driver.boot.prepare_instance(task)

            if deploy_utils.get_boot_option(node) == "local":
                if not is_whole_disk_image:
                    LOG.debug("Installing the bootloader on node %s", node.uuid)
                    deploy_utils.notify_ramdisk_to_proceed(kwargs["address"])
                    task.process_event("wait")
                    return

        except Exception as e:
            LOG.error(
                _LE("Deploy failed for instance %(instance)s. " "Error: %(error)s"),
                {"instance": node.instance_uuid, "error": e},
            )
            msg = _("Failed to continue iSCSI deployment.")
            deploy_utils.set_failed_state(task, msg)
        else:
            finish_deploy(task, kwargs.get("address"))
Пример #33
0
def prepare_deploy_iso(task,
                       params,
                       mode,
                       driver_info,
                       use_web_server=False,
                       container=None):
    """Prepare deploy or rescue ISO image

        Build bootable ISO out of
        `[driver_info]/deploy_kernel`/`[driver_info]/deploy_ramdisk` or
        `[driver_info]/rescue_kernel`/`[driver_info]/rescue_ramdisk`
        and `[driver_info]/bootloader`, then push built image up to Glance
        and return temporary Swift URL to the image.

        :param task: a TaskManager instance containing the node to act on.
        :param params: a dictionary containing 'parameter name'->'value'
            mapping to be passed to kernel command line.
        :param mode: either 'deploy' or 'rescue'.
        :param driver_info: a dictionary containing driver_info values.
        :returns: bootable ISO HTTP URL.
        :raises: MissingParameterValue, if any of the required parameters are
            missing.
        :raises: InvalidParameterValue, if any of the parameters have invalid
            value.
        :raises: ImageCreationFailed, if creating ISO image failed.
    """

    kernel_href = driver_info.get('%s_kernel' % mode)
    ramdisk_href = driver_info.get('%s_ramdisk' % mode)
    bootloader_href = driver_info.get('bootloader')
    timeout = None

    if deploy_utils.get_boot_option(task.node) == "ramdisk":
        i_info = task.node.instance_info
        kernel_params = "root=/dev/ram0 text "
        kernel_params += i_info.get("ramdisk_kernel_arguments", "")
    elif task.node.driver == 'redfish':
        kernel_params = CONF.redfish.kernel_append_params
        timeout = CONF.redfish.swift_object_expiry_timeout
    else:
        kernel_params = CONF.pxe.pxe_append_params

    if params:
        kernel_params = ' '.join(
            (kernel_params, ' '.join('%s=%s' % kv for kv in params.items())))

    return prepare_iso_image(task,
                             kernel_href,
                             ramdisk_href,
                             bootloader_href=bootloader_href,
                             kernel_params=kernel_params,
                             timeout=timeout,
                             use_web_server=use_web_server,
                             container=container)
Пример #34
0
def get_deploy_info(node, **kwargs):
    """Returns the information required for doing iSCSI deploy in a dictionary.

    :param node: ironic node object
    :param kwargs: the keyword args passed from the conductor node.
    :raises: MissingParameterValue, if some required parameters were not
        passed.
    :raises: InvalidParameterValue, if any of the parameters have invalid
        value.
    """
    deploy_key = kwargs.get('key')
    i_info = deploy_utils.parse_instance_info(node)
    if i_info['deploy_key'] != deploy_key:
        raise exception.InvalidParameterValue(_("Deploy key does not match"))

    params = {
        'address': kwargs.get('address'),
        'port': kwargs.get('port', CONF.iscsi.portal_port),
        'iqn': kwargs.get('iqn'),
        'lun': kwargs.get('lun', '1'),
        'image_path': _get_image_file_path(node.uuid),
        'node_uuid': node.uuid
    }

    is_whole_disk_image = node.driver_internal_info['is_whole_disk_image']
    if not is_whole_disk_image:
        params.update({
            'root_mb': 1024 * int(i_info['root_gb']),
            'swap_mb': int(i_info['swap_mb']),
            'ephemeral_mb': 1024 * int(i_info['ephemeral_gb']),
            'preserve_ephemeral': i_info['preserve_ephemeral'],
            'boot_option': deploy_utils.get_boot_option(node),
            'boot_mode': _get_boot_mode(node)
        })

        # Append disk label if specified
        disk_label = deploy_utils.get_disk_label(node)
        if disk_label is not None:
            params['disk_label'] = disk_label

    missing = [key for key in params if params[key] is None]
    if missing:
        raise exception.MissingParameterValue(
            _("Parameters %s were not passed to ironic"
              " for deploy.") % missing)

    if is_whole_disk_image:
        return params

    # configdrive and ephemeral_format are nullable
    params['ephemeral_format'] = i_info.get('ephemeral_format')
    params['configdrive'] = i_info.get('configdrive')

    return params
Пример #35
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 = deploy_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')
Пример #36
0
def get_deploy_info(node, address, iqn, port=None, lun='1'):
    """Returns the information required for doing iSCSI deploy in a dictionary.

    :param node: ironic node object
    :param address: iSCSI address
    :param iqn: iSCSI iqn for the target disk
    :param port: iSCSI port, defaults to one specified in the configuration
    :param lun: iSCSI lun, defaults to '1'
    :raises: MissingParameterValue, if some required parameters were not
        passed.
    :raises: InvalidParameterValue, if any of the parameters have invalid
        value.
    """
    i_info = deploy_utils.parse_instance_info(node)

    params = {
        'address': address,
        'port': port or CONF.iscsi.portal_port,
        'iqn': iqn,
        'lun': lun,
        'image_path': _get_image_file_path(node.uuid),
        'node_uuid': node.uuid}

    is_whole_disk_image = node.driver_internal_info['is_whole_disk_image']
    if not is_whole_disk_image:
        params.update({'root_mb': i_info['root_mb'],
                       'swap_mb': i_info['swap_mb'],
                       'ephemeral_mb': i_info['ephemeral_mb'],
                       'preserve_ephemeral': i_info['preserve_ephemeral'],
                       'boot_option': deploy_utils.get_boot_option(node),
                       'boot_mode': _get_boot_mode(node)})

        # Append disk label if specified
        disk_label = deploy_utils.get_disk_label(node)
        if disk_label is not None:
            params['disk_label'] = disk_label

    missing = [key for key in params if params[key] is None]
    if missing:
        raise exception.MissingParameterValue(
            _("Parameters %s were not passed to ironic"
              " for deploy.") % missing)

    # configdrive is nullable
    params['configdrive'] = i_info.get('configdrive')
    if is_whole_disk_image:
        return params

    # ephemeral_format is nullable
    params['ephemeral_format'] = i_info.get('ephemeral_format')

    return params
Пример #37
0
    def validate(self, task):
        """Validate the PXE-specific info for booting deploy/instance images.

        This method validates the PXE-specific info for booting the
        ramdisk and instance on the node.  If invalid, raises an
        exception; otherwise returns None.

        :param task: a task from TaskManager.
        :returns: None
        :raises: InvalidParameterValue, if some parameters are invalid.
        :raises: MissingParameterValue, if some required parameters are
            missing.
        """
        node = task.node

        if not driver_utils.get_node_mac_addresses(task):
            raise exception.MissingParameterValue(
                _("Node %s does not have any port associated with it.")
                % node.uuid)

        # TODO(TheJulia): Once ipxe support is remove from the pxe
        # interface, this can be removed.
        if CONF.pxe.ipxe_enabled:
            if (not CONF.deploy.http_url
                or not CONF.deploy.http_root):
                raise exception.MissingParameterValue(_(
                    "iPXE boot is enabled but no HTTP URL or HTTP "
                    "root was specified."))

        # Check the trusted_boot capabilities value.
        deploy_utils.validate_capabilities(node)
        if deploy_utils.is_trusted_boot_requested(node):
            # Check if 'boot_option' and boot mode is compatible with
            # trusted boot.
            validate_boot_parameters_for_trusted_boot(node)

        pxe_utils.parse_driver_info(node)
        # NOTE(TheJulia): If we're not writing an image, we can skip
        # the remainder of this method.
        if (not task.driver.storage.should_write_image(task)):
            return

        d_info = deploy_utils.get_image_instance_info(node)
        if (node.driver_internal_info.get('is_whole_disk_image')
                or deploy_utils.get_boot_option(node) == 'local'):
            props = []
        elif service_utils.is_glance_image(d_info['image_source']):
            props = ['kernel_id', 'ramdisk_id']
        else:
            props = ['kernel', 'ramdisk']
        deploy_utils.validate_image_properties(task.context, d_info, props)
Пример #38
0
def get_instance_image_info(task, ipxe_enabled=False):
    """Generate the paths for TFTP files for instance related images.

    This method generates the paths for instance kernel and
    instance ramdisk. This method also updates the node, so caller should
    already have a non-shared lock on the node.

    :param task: A TaskManager instance containing node and context.
    :param ipxe_enabled: Default false boolean to indicate if ipxe
                         is in use by the caller.
    :returns: a dictionary whose keys are the names of the images (kernel,
        ramdisk) and values are the absolute paths of them. If it's a whole
        disk image or node is configured for localboot,
        it returns an empty dictionary.
    """
    ctx = task.context
    node = task.node
    image_info = {}
    # NOTE(pas-ha) do not report image kernel and ramdisk for
    # local boot or whole disk images so that they are not cached
    if (node.driver_internal_info.get('is_whole_disk_image')
            or deploy_utils.get_boot_option(node) == 'local'):
        return image_info
    if ipxe_enabled:
        root_dir = get_ipxe_root_dir()
    else:
        root_dir = get_root_dir()
    i_info = node.instance_info
    if i_info.get('boot_iso'):
        image_info['boot_iso'] = (
            i_info['boot_iso'],
            os.path.join(root_dir, node.uuid, 'boot_iso'))

        return image_info

    labels = ('kernel', 'ramdisk')
    d_info = deploy_utils.get_image_instance_info(node)
    if not (i_info.get('kernel') and i_info.get('ramdisk')):
        glance_service = service.GlanceImageService(context=ctx)
        iproperties = glance_service.show(d_info['image_source'])['properties']
        for label in labels:
            i_info[label] = str(iproperties[label + '_id'])
        node.instance_info = i_info
        node.save()
    for label in labels:
        image_info[label] = (
            i_info[label],
            os.path.join(root_dir, node.uuid, label)
        )

    return image_info
Пример #39
0
def get_deploy_info(node, **kwargs):
    """Returns the information required for doing iSCSI deploy in a dictionary.

    :param node: ironic node object
    :param kwargs: the keyword args passed from the conductor node.
    :raises: MissingParameterValue, if some required parameters were not
        passed.
    :raises: InvalidParameterValue, if any of the parameters have invalid
        value.
    """
    deploy_key = kwargs.get('key')
    i_info = parse_instance_info(node)
    if i_info['deploy_key'] != deploy_key:
        raise exception.InvalidParameterValue(_("Deploy key does not match"))

    params = {
        'address': kwargs.get('address'),
        'port': kwargs.get('port', '3260'),
        'iqn': kwargs.get('iqn'),
        'lun': kwargs.get('lun', '1'),
        'image_path': _get_image_file_path(node.uuid),
        'node_uuid': node.uuid}

    is_whole_disk_image = node.driver_internal_info['is_whole_disk_image']
    if not is_whole_disk_image:
        params.update({'root_mb': 1024 * int(i_info['root_gb']),
                       'swap_mb': int(i_info['swap_mb']),
                       'ephemeral_mb': 1024 * int(i_info['ephemeral_gb']),
                       'preserve_ephemeral': i_info['preserve_ephemeral'],
                       'boot_option': deploy_utils.get_boot_option(node),
                       'boot_mode': _get_boot_mode(node)})

        # Append disk label if specified
        disk_label = deploy_utils.get_disk_label(node)
        if disk_label is not None:
            params['disk_label'] = disk_label

    missing = [key for key in params if params[key] is None]
    if missing:
        raise exception.MissingParameterValue(
            _("Parameters %s were not passed to ironic"
              " for deploy.") % missing)

    if is_whole_disk_image:
        return params

    # configdrive and ephemeral_format are nullable
    params['ephemeral_format'] = i_info.get('ephemeral_format')
    params['configdrive'] = i_info.get('configdrive')

    return params
Пример #40
0
    def validate(self, task):
        """Validate the PXE-specific info for booting deploy/instance images.

        This method validates the PXE-specific info for booting the
        ramdisk and instance on the node.  If invalid, raises an
        exception; otherwise returns None.

        :param task: a task from TaskManager.
        :returns: None
        :raises: InvalidParameterValue, if some parameters are invalid.
        :raises: MissingParameterValue, if some required parameters are
            missing.
        """
        node = task.node

        if not driver_utils.get_node_mac_addresses(task):
            raise exception.MissingParameterValue(
                _("Node %s does not have any port associated with it.")
                % node.uuid)

        # TODO(TheJulia): Once ipxe support is remove from the pxe
        # interface, this can be removed.
        if CONF.pxe.ipxe_enabled:
            if (not CONF.deploy.http_url
                or not CONF.deploy.http_root):
                raise exception.MissingParameterValue(_(
                    "iPXE boot is enabled but no HTTP URL or HTTP "
                    "root was specified."))

        # Check the trusted_boot capabilities value.
        deploy_utils.validate_capabilities(node)
        if deploy_utils.is_trusted_boot_requested(node):
            # Check if 'boot_option' and boot mode is compatible with
            # trusted boot.
            validate_boot_parameters_for_trusted_boot(node)

        pxe_utils.parse_driver_info(node)
        # NOTE(TheJulia): If we're not writing an image, we can skip
        # the remainder of this method.
        if (not task.driver.storage.should_write_image(task)):
            return

        d_info = deploy_utils.get_image_instance_info(node)
        if (node.driver_internal_info.get('is_whole_disk_image')
                or deploy_utils.get_boot_option(node) == 'local'):
            props = []
        elif service_utils.is_glance_image(d_info['image_source']):
            props = ['kernel_id', 'ramdisk_id']
        else:
            props = ['kernel', 'ramdisk']
        deploy_utils.validate_image_properties(task.context, d_info, props)
Пример #41
0
def get_deploy_info(node, **kwargs):
    """Returns the information required for doing iSCSI deploy in a dictionary.

    :param node: ironic node object
    :param kwargs: the keyword args passed from the conductor node.
    :raises: MissingParameterValue, if some required parameters were not
        passed.
    :raises: InvalidParameterValue, if any of the parameters have invalid
        value.
    """
    deploy_key = kwargs.get("key")
    i_info = parse_instance_info(node)
    if i_info["deploy_key"] != deploy_key:
        raise exception.InvalidParameterValue(_("Deploy key does not match"))

    params = {
        "address": kwargs.get("address"),
        "port": kwargs.get("port", "3260"),
        "iqn": kwargs.get("iqn"),
        "lun": kwargs.get("lun", "1"),
        "image_path": _get_image_file_path(node.uuid),
        "node_uuid": node.uuid,
    }

    is_whole_disk_image = node.driver_internal_info["is_whole_disk_image"]
    if not is_whole_disk_image:
        params.update(
            {
                "root_mb": 1024 * int(i_info["root_gb"]),
                "swap_mb": int(i_info["swap_mb"]),
                "ephemeral_mb": 1024 * int(i_info["ephemeral_gb"]),
                "preserve_ephemeral": i_info["preserve_ephemeral"],
                "boot_option": deploy_utils.get_boot_option(node),
                "boot_mode": _get_boot_mode(node),
            }
        )

    missing = [key for key in params if params[key] is None]
    if missing:
        raise exception.MissingParameterValue(_("Parameters %s were not passed to ironic" " for deploy.") % missing)

    if is_whole_disk_image:
        return params

    # configdrive and ephemeral_format are nullable
    params["ephemeral_format"] = i_info.get("ephemeral_format")
    params["configdrive"] = i_info.get("configdrive")

    return params
Пример #42
0
    def pass_deploy_info(self, task, **kwargs):
        """Continues the deployment of baremetal node over iSCSI.

        This method continues the deployment of the baremetal node over iSCSI
        from where the deployment ramdisk has left off.

        :param task: a TaskManager instance containing the node to act on.
        :param kwargs: kwargs for performing iscsi deployment.
        :raises: InvalidState
        """
        node = task.node
        task.process_event('resume')
        LOG.debug('Continuing the deployment on node %s', node.uuid)

        is_whole_disk_image = node.driver_internal_info['is_whole_disk_image']
        uuid_dict_returned = continue_deploy(task, **kwargs)
        root_uuid_or_disk_id = uuid_dict_returned.get(
            'root uuid', uuid_dict_returned.get('disk identifier'))

        # save the node's root disk UUID so that another conductor could
        # rebuild the PXE config file. Due to a shortcoming in Nova objects,
        # we have to assign to node.driver_internal_info so the node knows it
        # has changed.
        driver_internal_info = node.driver_internal_info
        driver_internal_info['root_uuid_or_disk_id'] = root_uuid_or_disk_id
        node.driver_internal_info = driver_internal_info
        node.save()

        try:
            if deploy_utils.get_boot_option(node) == "local":
                deploy_utils.try_set_boot_device(task, boot_devices.DISK)

                if not is_whole_disk_image:
                    LOG.debug('Installing the bootloader on node %s',
                              node.uuid)
                    deploy_utils.notify_ramdisk_to_proceed(kwargs['address'])
                    task.process_event('wait')
                    return

            task.driver.boot.prepare_instance(task)
        except Exception as e:
            LOG.error(_LE('Deploy failed for instance %(instance)s. '
                          'Error: %(error)s'),
                      {'instance': node.instance_uuid, 'error': e})
            msg = _('Failed to continue iSCSI deployment.')
            deploy_utils.set_failed_state(task, msg)
        else:
            finish_deploy(task, kwargs.get('address'))
Пример #43
0
    def _eject_all(self, task):
        managers = redfish_utils.get_system(task.node).managers

        _eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_CD)
        d_info = task.node.driver_info
        config_via_floppy = d_info.get('config_via_floppy')
        if config_via_floppy:
            _eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_FLOPPY)

        boot_option = deploy_utils.get_boot_option(task.node)
        if (boot_option == 'ramdisk'
                and task.node.instance_info.get('configdrive')):
            _eject_vmedia(task, managers, sushy.VIRTUAL_MEDIA_USBSTICK)
            image_utils.cleanup_disk_image(task, prefix='configdrive')

        image_utils.cleanup_iso_image(task)
Пример #44
0
    def _test_build_pxe_config_options_pxe(self, render_mock, whle_dsk_img=False):
        self.config(pxe_append_params="test_param", group="pxe")
        # NOTE: right '/' should be removed from url string
        self.config(api_url="http://192.168.122.184:6385", group="conductor")

        driver_internal_info = self.node.driver_internal_info
        driver_internal_info["is_whole_disk_image"] = whle_dsk_img
        self.node.driver_internal_info = driver_internal_info
        self.node.save()

        tftp_server = CONF.pxe.tftp_server

        deploy_kernel = os.path.join(self.node.uuid, "deploy_kernel")
        deploy_ramdisk = os.path.join(self.node.uuid, "deploy_ramdisk")
        kernel = os.path.join(self.node.uuid, "kernel")
        ramdisk = os.path.join(self.node.uuid, "ramdisk")
        root_dir = CONF.pxe.tftp_root

        image_info = {
            "deploy_kernel": ("deploy_kernel", os.path.join(root_dir, self.node.uuid, "deploy_kernel")),
            "deploy_ramdisk": ("deploy_ramdisk", os.path.join(root_dir, self.node.uuid, "deploy_ramdisk")),
        }

        if whle_dsk_img or deploy_utils.get_boot_option(self.node) == "local":
            ramdisk = "no_ramdisk"
            kernel = "no_kernel"
        else:
            image_info.update(
                {
                    "kernel": ("kernel_id", os.path.join(root_dir, self.node.uuid, "kernel")),
                    "ramdisk": ("ramdisk_id", os.path.join(root_dir, self.node.uuid, "ramdisk")),
                }
            )

        expected_options = {
            "ari_path": ramdisk,
            "deployment_ari_path": deploy_ramdisk,
            "pxe_append_params": "test_param",
            "aki_path": kernel,
            "deployment_aki_path": deploy_kernel,
            "tftp_server": tftp_server,
            "ipxe_timeout": 0,
        }

        with task_manager.acquire(self.context, self.node.uuid, shared=True) as task:
            options = pxe._build_pxe_config_options(task, image_info)
        self.assertEqual(expected_options, options)
Пример #45
0
def build_deploy_ramdisk_options(node):
    """Build the ramdisk config options for a node

    This method builds the ramdisk options for a node,
    given all the required parameters for doing iscsi deploy.

    :param node: a single Node.
    :returns: A dictionary of options to be passed to ramdisk for performing
        the deploy.
    """
    # NOTE: we should strip '/' from the end because this is intended for
    # hardcoded ramdisk script
    ironic_api = (CONF.conductor.api_url
                  or keystone.get_service_url()).rstrip('/')

    deploy_key = utils.random_alnum(32)
    i_info = node.instance_info
    i_info['deploy_key'] = deploy_key
    node.instance_info = i_info
    node.save()

    # XXX(jroll) DIB relies on boot_option=local to decide whether or not to
    # lay down a bootloader. Hack this for now; fix it for real in Liberty.
    # See also bug #1441556.
    boot_option = deploy_utils.get_boot_option(node)
    if node.driver_internal_info.get('is_whole_disk_image'):
        boot_option = 'netboot'

    deploy_options = {
        'deployment_id': node['uuid'],
        'deployment_key': deploy_key,
        'iscsi_target_iqn': 'iqn.2008-10.org.openstack:%s' % node.uuid,
        'iscsi_portal_port': CONF.iscsi.portal_port,
        'ironic_api_url': ironic_api,
        'disk': CONF.pxe.disk_devices,
        'boot_option': boot_option,
        'boot_mode': _get_boot_mode(node),
        # NOTE: The below entry is a temporary workaround for bug/1433812
        'coreos.configdrive': 0,
    }

    root_device = deploy_utils.parse_root_device_hints(node)
    if root_device:
        deploy_options['root_device'] = root_device

    return deploy_options
Пример #46
0
def build_deploy_ramdisk_options(node):
    """Build the ramdisk config options for a node

    This method builds the ramdisk options for a node,
    given all the required parameters for doing iscsi deploy.

    :param node: a single Node.
    :returns: A dictionary of options to be passed to ramdisk for performing
        the deploy.
    """
    # NOTE: we should strip '/' from the end because this is intended for
    # hardcoded ramdisk script
    ironic_api = (CONF.conductor.api_url or
                  keystone.get_service_url()).rstrip('/')

    deploy_key = utils.random_alnum(32)
    i_info = node.instance_info
    i_info['deploy_key'] = deploy_key
    node.instance_info = i_info
    node.save()

    # XXX(jroll) DIB relies on boot_option=local to decide whether or not to
    # lay down a bootloader. Hack this for now; fix it for real in Liberty.
    # See also bug #1441556.
    boot_option = deploy_utils.get_boot_option(node)
    if node.driver_internal_info.get('is_whole_disk_image'):
        boot_option = 'netboot'

    deploy_options = {
        'deployment_id': node['uuid'],
        'deployment_key': deploy_key,
        'iscsi_target_iqn': 'iqn.2008-10.org.openstack:%s' % node.uuid,
        'iscsi_portal_port': CONF.iscsi.portal_port,
        'ironic_api_url': ironic_api,
        'disk': CONF.pxe.disk_devices,
        'boot_option': boot_option,
        'boot_mode': _get_boot_mode(node),
        # NOTE: The below entry is a temporary workaround for bug/1433812
        'coreos.configdrive': 0,
    }

    root_device = deploy_utils.parse_root_device_hints(node)
    if root_device:
        deploy_options['root_device'] = root_device

    return deploy_options
Пример #47
0
    def validate(self, task):
        """Validate the deployment information for the task's node.

        This method validates whether the 'driver_info' and/or 'instance_info'
        properties of the task's node contains the required information for
        this interface to function.

        :param task: A TaskManager instance containing the node to act on.
        :raises: InvalidParameterValue on malformed parameter(s)
        :raises: MissingParameterValue on missing parameter(s)
        """
        node = task.node
        boot_option = deploy_utils.get_boot_option(node)
        try:
            boot_mode = ilo_common.get_current_boot_mode(task.node)
        except exception.IloOperationError:
            error = _("Validation for 'ilo-uefi-https' boot interface failed. "
                      "Could not determine current boot mode for node "
                      "%(node)s.") % node.uuid
            raise exception.InvalidParameterValue(error)

        if boot_mode.lower() != 'uefi':
            error = _("Validation for 'ilo-uefi-https' boot interface failed. "
                      "The node is required to be in 'UEFI' boot mode.")
            raise exception.InvalidParameterValue(error)

        boot_iso = node.instance_info.get('ilo_boot_iso')
        if (boot_option == "ramdisk" and 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("UEFI-HTTPS boot with 'ramdisk' "
                                  "boot_option accepts only Glance images or "
                                  "HTTPS URLs as "
                                  "instance_info['ilo_boot_iso']. Either %s "
                                  "is not a valid HTTPS URL or is not "
                                  "reachable.", boot_iso)
            return

        self._validate_driver_info(task)

        if task.driver.storage.should_write_image(task):
            self._validate_instance_image_info(task)
Пример #48
0
def get_instance_image_info(task, ipxe_enabled=False):
    """Generate the paths for TFTP files for instance related images.

    This method generates the paths for instance kernel and
    instance ramdisk. This method also updates the node, so caller should
    already have a non-shared lock on the node.

    :param task: A TaskManager instance containing node and context.
    :param ipxe_enabled: Default false boolean to indicate if ipxe
                         is in use by the caller.
    :returns: a dictionary whose keys are the names of the images (kernel,
        ramdisk) and values are the absolute paths of them. If it's a whole
        disk image or node is configured for localboot,
        it returns an empty dictionary.
    """
    ctx = task.context
    node = task.node
    image_info = {}
    # NOTE(pas-ha) do not report image kernel and ramdisk for
    # local boot or whole disk images so that they are not cached
    if (node.driver_internal_info.get('is_whole_disk_image')
        or deploy_utils.get_boot_option(node) == 'local'):
            return image_info
    if ipxe_enabled:
        root_dir = get_ipxe_root_dir()
    else:
        root_dir = get_root_dir()
    i_info = node.instance_info
    labels = ('kernel', 'ramdisk')
    d_info = deploy_utils.get_image_instance_info(node)
    if not (i_info.get('kernel') and i_info.get('ramdisk')):
        glance_service = service.GlanceImageService(context=ctx)
        iproperties = glance_service.show(d_info['image_source'])['properties']
        for label in labels:
            i_info[label] = str(iproperties[label + '_id'])
        node.instance_info = i_info
        node.save()

    for label in labels:
        image_info[label] = (
            i_info[label],
            os.path.join(root_dir, node.uuid, label)
        )

    return image_info
Пример #49
0
    def validate(self, task):
        """Validate the driver-specific Node deployment info."""
        task.driver.boot.validate(task)

        node = task.node
        iwdi = node.driver_internal_info.get('is_whole_disk_image')
        if not iwdi and deploy_utils.get_boot_option(node) == "netboot":
            raise exception.InvalidParameterValue(_(
                "Node %(node)s is configured to use the %(driver)s driver "
                "which does not support netboot.") % {'node': node.uuid,
                                                      'driver': node.driver})

        params = {}
        image_source = node.instance_info.get('image_source')
        params['instance_info.image_source'] = image_source
        error_msg = _('Node %s failed to validate deploy image info. Some '
                      'parameters were missing') % node.uuid
        deploy_utils.check_for_missing_params(params, error_msg)
Пример #50
0
    def validate(self, task):
        """Validates the network interface.

        :param task: a TaskManager instance.
        :raises: InvalidParameterValue, if the network interface configuration
            is invalid.
        :raises: MissingParameterValue, if some parameters are missing.
        """
        self.get_cleaning_network_uuid(task)
        self.get_provisioning_network_uuid(task)
        node = task.node
        if (node.provision_state == states.DEPLOYING
            and node.driver_internal_info.get('is_whole_disk_image')
            and deploy_utils.get_boot_option(node) == 'netboot'):
            error_msg = (_('The node %s cannot perform "local" boot for '
                           'whole disk image when node is using "neutron" '
                           'network and is configured with "netboot" boot '
                           'option.') % node.uuid)
            raise exception.InvalidParameterValue(error_msg)
Пример #51
0
    def validate(self, task):
        """Validates the network interface.

        :param task: a TaskManager instance.
        :raises: InvalidParameterValue, if the network interface configuration
            is invalid.
        :raises: MissingParameterValue, if some parameters are missing.
        """
        self.get_cleaning_network_uuid(task)
        self.get_provisioning_network_uuid(task)
        node = task.node
        if (node.provision_state == states.DEPLOYING
                and node.driver_internal_info.get('is_whole_disk_image')
                and deploy_utils.get_boot_option(node) == 'netboot'):
            error_msg = (_('The node %s cannot perform "local" boot for '
                           'whole disk image when node is using "neutron" '
                           'network and is configured with "netboot" boot '
                           'option.') % node.uuid)
            raise exception.InvalidParameterValue(error_msg)
Пример #52
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 database.

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

        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:
            driver_internal_info = node.driver_internal_info
            root_uuid_or_disk_id = driver_internal_info["root_uuid_or_disk_id"]
            self._configure_vmedia_boot(task, root_uuid_or_disk_id)
Пример #53
0
    def validate(self, task):
        """Validate the driver-specific Node deployment info."""
        task.driver.boot.validate(task)

        node = task.node
        iwdi = node.driver_internal_info.get('is_whole_disk_image')
        if not iwdi and deploy_utils.get_boot_option(node) == "netboot":
            raise exception.InvalidParameterValue(
                _("Node %(node)s is configured to use the %(driver)s driver "
                  "which does not support netboot.") % {
                      'node': node.uuid,
                      'driver': node.driver
                  })

        params = {}
        image_source = node.instance_info.get('image_source')
        params['instance_info.image_source'] = image_source
        error_msg = _('Node %s failed to validate deploy image info. Some '
                      'parameters were missing') % node.uuid
        deploy_utils.check_for_missing_params(params, error_msg)
Пример #54
0
def _get_instance_image_info(node, ctx):
    """Generate the paths for TFTP files for instance related images.

    This method generates the paths for instance kernel and
    instance ramdisk. This method also updates the node, so caller should
    already have a non-shared lock on the node.

    :param node: a node object
    :param ctx: context
    :returns: a dictionary whose keys are the names of the images (kernel,
        ramdisk) and values are the absolute paths of them. If it's a whole
        disk image or node is configured for localboot,
        it returns an empty dictionary.
    """
    image_info = {}
    # NOTE(pas-ha) do not report image kernel and ramdisk for
    # local boot or whole disk images so that they are not cached
    if (node.driver_internal_info.get('is_whole_disk_image') or
        deploy_utils.get_boot_option(node) == 'local'):
            return image_info

    root_dir = pxe_utils.get_root_dir()
    i_info = node.instance_info
    labels = ('kernel', 'ramdisk')
    d_info = deploy_utils.get_image_instance_info(node)
    if not (i_info.get('kernel') and i_info.get('ramdisk')):
        glance_service = service.GlanceImageService(
            version=CONF.glance.glance_api_version, context=ctx)
        iproperties = glance_service.show(d_info['image_source'])['properties']
        for label in labels:
            i_info[label] = str(iproperties[label + '_id'])
        node.instance_info = i_info
        node.save()

    for label in labels:
        image_info[label] = (
            i_info[label],
            os.path.join(root_dir, node.uuid, label)
        )

    return image_info
Пример #55
0
def validate_boot_parameters_for_trusted_boot(node):
    """Check if boot parameters are valid for trusted boot."""
    boot_mode = deploy_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)
Пример #56
0
def validate_boot_option_for_uefi(node):
    """In uefi boot mode, validate if the boot option is compatible.

    This method raises exception if whole disk image being deployed
    in UEFI boot mode without 'boot_option' being set to 'local'.

    :param node: a single Node.
    :raises: InvalidParameterValue
    """
    boot_mode = deploy_utils.get_boot_mode_for_deploy(node)
    boot_option = deploy_utils.get_boot_option(node)
    if (boot_mode == 'uefi' and
            node.driver_internal_info.get('is_whole_disk_image') and
            boot_option != 'local'):
        LOG.error(_LE("Whole disk image with netboot is not supported in UEFI "
                      "boot mode."))
        raise exception.InvalidParameterValue(_(
            "Conflict: Whole disk image being used for deploy, but "
            "cannot be used with node %(node_uuid)s configured to use "
            "UEFI boot with netboot option") %
            {'node_uuid': node.uuid})
Пример #57
0
    def prepare_instance_to_boot(self, task, root_uuid, efi_sys_uuid):
        """Prepares instance to boot.

        :param task: a TaskManager object containing the node
        :param root_uuid: the UUID for root partition
        :param efi_sys_uuid: the UUID for the efi partition
        :raises: InvalidState if fails to prepare instance
        """

        node = task.node
        if deploy_utils.get_boot_option(node) == "local":
            # Install the boot loader
            self.configure_local_boot(
                task, root_uuid=root_uuid,
                efi_system_part_uuid=efi_sys_uuid)
        try:
            task.driver.boot.prepare_instance(task)
        except Exception as e:
            LOG.error(_LE('Deploy failed for instance %(instance)s. '
                          'Error: %(error)s'),
                      {'instance': node.instance_uuid, 'error': e})
            msg = _('Failed to continue agent deployment.')
            self._log_and_raise_deployment_error(task, msg)
Пример #58
0
    def continue_deploy(self, task, **kwargs):
        """Method invoked when deployed with the IPA ramdisk.

        This method is invoked during a heartbeat from an agent when
        the node is in wait-call-back state. This deploys the image on
        the node and then configures the node to boot according to the
        desired boot option (netboot or localboot).

        :param task: a TaskManager object containing the node.
        :param kwargs: the kwargs passed from the heartbeat method.
        :raises: InstanceDeployFailure, if it encounters some error during
            the deploy.
        """
        task.process_event("resume")
        node = task.node
        LOG.debug("Continuing the deployment on node %s", node.uuid)

        ilo_common.cleanup_vmedia_boot(task)

        iwdi = node.driver_internal_info.get("is_whole_disk_image")
        uuid_dict = iscsi_deploy.do_agent_iscsi_deploy(task, self._client)
        root_uuid = uuid_dict.get("root uuid")

        if deploy_utils.get_boot_option(node) == "local" or iwdi:
            efi_system_part_uuid = uuid_dict.get("efi system partition uuid")
            self.configure_local_boot(task, root_uuid=root_uuid, efi_system_part_uuid=efi_system_part_uuid)
        else:
            self._configure_vmedia_boot(task, root_uuid)

        # Set boot mode
        ilo_common.update_boot_mode(task)

        # Need to enable secure boot, if being requested
        _update_secure_boot_mode(task, True)

        self.reboot_and_finish_deploy(task)