Exemple #1
0
def get_disk_label(node):
    """Return the disk label requested for deploy, if any.

    :param node: a single Node.
    :raises: InvalidParameterValue if the capabilities string is not a
             dictionary or is malformed.
    :returns: the disk label or None if no disk label was specified.
    """
    capabilities = utils.parse_instance_info_capabilities(node)
    return capabilities.get('disk_label')
Exemple #2
0
def get_boot_option(node):
    """Gets the boot option.

    :param node: A single Node.
    :raises: InvalidParameterValue if the capabilities string is not a
         dict or is malformed.
    :returns: A string representing the boot option type. Defaults to
        'netboot'.
    """

    # NOTE(TheJulia): Software raid always implies local deployment
    if is_software_raid(node):
        return 'local'
    capabilities = utils.parse_instance_info_capabilities(node)
    return capabilities.get('boot_option', get_default_boot_option()).lower()
Exemple #3
0
def is_trusted_boot_requested(node):
    """Returns True if trusted_boot is requested for deploy.

    This method checks instance property for trusted_boot and returns True
    if it is requested.

    :param node: a single Node.
    :raises: InvalidParameterValue if the capabilities string is not a
             dictionary or is malformed.
    :returns: True if trusted_boot is requested.
    """

    capabilities = common_utils.parse_instance_info_capabilities(node)
    trusted_boot = capabilities.get('trusted_boot', 'false').lower()

    return trusted_boot == 'true'
def is_trusted_boot_requested(node):
    """Returns True if trusted_boot is requested for deploy.

    This method checks instance property for trusted_boot and returns True
    if it is requested.

    :param node: a single Node.
    :raises: InvalidParameterValue if the capabilities string is not a
             dictionary or is malformed.
    :returns: True if trusted_boot is requested.
    """

    capabilities = common_utils.parse_instance_info_capabilities(node)
    trusted_boot = capabilities.get('trusted_boot', 'false').lower()

    return trusted_boot == 'true'
Exemple #5
0
def validate_capabilities(node):
    """Validates that specified supported capabilities have valid value

    This method checks if the any of the supported capability is present in
    Node capabilities. For all supported capabilities specified for a Node,
    it validates that it has a valid value.
    The node can have capability as part of the 'properties' or
    'instance_info' or both.
    Note that the actual value of a capability does not need to be the same
    in the node's 'properties' and 'instance_info'.

    :param node: an ironic node object.
    :raises: InvalidParameterValue, if the capability is not set to a
        valid value.
    """
    exp_str = _("The parameter '%(capability)s' from %(field)s has an "
                "invalid value: '%(value)s'. Acceptable values are: "
                "%(valid_values)s.")

    for capability_name, valid_values in SUPPORTED_CAPABILITIES.items():
        # Validate capability_name in node's properties/capabilities
        value = driver_utils.get_node_capability(node, capability_name)
        if value and (value not in valid_values):
            field = "properties/capabilities"
            raise exception.InvalidParameterValue(
                exp_str % {
                    'capability': capability_name,
                    'field': field,
                    'value': value,
                    'valid_values': ', '.join(valid_values)
                })

        # Validate capability_name in node's instance_info/['capabilities']
        capabilities = utils.parse_instance_info_capabilities(node)
        value = capabilities.get(capability_name)

        if value and (value not in valid_values):
            field = "instance_info['capabilities']"
            raise exception.InvalidParameterValue(
                exp_str % {
                    'capability': capability_name,
                    'field': field,
                    'value': value,
                    'valid_values': ', '.join(valid_values)
                })
Exemple #6
0
def get_boot_mode_for_deploy(node):
    """Returns the boot mode that would be used for deploy.

    This method returns boot mode to be used for deploy.
    It returns 'uefi' if 'secure_boot' is set to 'true' or returns 'bios' if
    'trusted_boot' is set to 'true' in 'instance_info/capabilities' of node.
    Otherwise it returns value of 'boot_mode' in 'properties/capabilities'
    of node if set. If that is not set, it returns boot mode in
    'internal_driver_info/deploy_boot_mode' for the node.
    If that is not set, it returns boot mode in
    'instance_info/deploy_boot_mode' for the node.
    It would return None if boot mode is present neither in 'capabilities' of
    node 'properties' nor in node's 'internal_driver_info' nor in node's
    'instance_info' (which could also be None).

    :param node: an ironic node object.
    :returns: 'bios', 'uefi' or None
    :raises: InvalidParameterValue, if the node boot mode disagrees with
        the boot mode set to node properties/capabilities
    """

    if is_secure_boot_requested(node):
        LOG.debug('Deploy boot mode is uefi for %s.', node.uuid)
        return 'uefi'

    if is_trusted_boot_requested(node):
        # TODO(lintan) Trusted boot also supports uefi, but at the moment,
        # it should only boot with bios.
        LOG.debug('Deploy boot mode is bios for %s.', node.uuid)
        return 'bios'

    # NOTE(etingof):
    # The search for a boot mode should be in the priority order:
    #
    # 1) instance_info.capabilities
    # 2) instance_info.deploy_boot_mode (deprecated in Wallaby)
    # 3) properties.capabilities
    # 4) driver_internal_info.deploy_boot_mode (internal)
    #
    # Because:
    #
    # (1) and (2) are deleted during teardown
    # (4) will never be touched if node properties/capabilities
    #     are still present.
    # (3) becomes operational default as the last resort

    inst_boot_mode = (
        common_utils.parse_instance_info_capabilities(node).get('boot_mode'))
    cap_boot_mode = driver_utils.get_node_capability(node, 'boot_mode')

    old_boot_mode = node.instance_info.get('deploy_boot_mode')
    if old_boot_mode:
        LOG.warning(
            'Using instance_info/deploy_boot_mode is deprecated, '
            'please use instance_info/capabilities with boot mode '
            'for node %s', node.uuid)

    boot_mode = (inst_boot_mode or old_boot_mode or cap_boot_mode
                 or node.driver_internal_info.get('deploy_boot_mode'))

    if not boot_mode:
        return

    boot_mode = boot_mode.lower()

    # NOTE(etingof):
    # Make sure that the ultimate boot_mode agrees with the one set to
    # node properties/capabilities. This locks down node to use only
    # boot mode specified in properties/capabilities.
    # TODO(etingof): this logic will have to go away when we switch to traits
    if cap_boot_mode:
        cap_boot_mode = cap_boot_mode.lower()
        if cap_boot_mode != boot_mode:
            msg = (_("Node %(uuid)s boot mode %(boot_mode)s violates "
                     "node properties/capabilities %(caps)s") % {
                         'uuid': node.uuid,
                         'boot_mode': boot_mode,
                         'caps': cap_boot_mode
                     })
            LOG.error(msg)
            raise exception.InvalidParameterValue(msg)

    LOG.debug('Deploy boot mode is %(boot_mode)s for %(node)s.', {
        'boot_mode': boot_mode,
        'node': node.uuid
    })

    return boot_mode
Exemple #7
0
    def prepare(self, task):
        """Prepare the deployment environment for this node.

        :param task: a TaskManager instance.
        :raises: NetworkError: if the previous cleaning ports cannot be removed
            or if new cleaning ports cannot be created.
        :raises: InvalidParameterValue when the wrong power state is specified
            or the wrong driver info is specified for power management.
        :raises: StorageError If the storage driver is unable to attach the
            configured volumes.
        :raises: other exceptions by the node's power driver if something
            wrong occurred during the power action.
        :raises: exception.ImageRefValidationFailed if image_source is not
            Glance href and is not HTTP(S) URL.
        :raises: exception.InvalidParameterValue if network validation fails.
        :raises: any boot interface's prepare_ramdisk exceptions.
        """
        def _update_instance_info():
            node.instance_info = (
                deploy_utils.build_instance_info_for_deploy(task))
            node.save()

        node = task.node
        deploy_utils.populate_storage_driver_internal_info(task)
        if node.provision_state == states.DEPLOYING:
            # Validate network interface to ensure that it supports boot
            # options configured on the node.
            try:
                task.driver.network.validate(task)
            except exception.InvalidParameterValue:
                # For 'neutron' network interface validation will fail
                # if node is using 'netboot' boot option while provisioning
                # a whole disk image. Updating 'boot_option' in node's
                # 'instance_info' to 'local for backward compatibility.
                # TODO(stendulker): Fail here once the default boot
                # option is local.
                # NOTE(TheJulia): Fixing the default boot mode only
                # masks the failure as the lack of a user definition
                # can be perceived as both an invalid configuration and
                # reliance upon the default configuration. The reality
                # being that in most scenarios, users do not want network
                # booting, so the changed default should be valid.
                with excutils.save_and_reraise_exception(reraise=False) as ctx:
                    instance_info = node.instance_info
                    capabilities = utils.parse_instance_info_capabilities(node)
                    if 'boot_option' not in capabilities:
                        capabilities['boot_option'] = 'local'
                        instance_info['capabilities'] = capabilities
                        node.instance_info = instance_info
                        node.save()
                        # Re-validate the network interface
                        task.driver.network.validate(task)
                    else:
                        ctx.reraise = True
            # Determine if this is a fast track sequence
            fast_track_deploy = manager_utils.is_fast_track(task)
            if fast_track_deploy:
                # The agent has already recently checked in and we are
                # configured to take that as an indicator that we can
                # skip ahead.
                LOG.debug(
                    'The agent for node %(node)s has recently checked '
                    'in, and the node power will remain unmodified.',
                    {'node': task.node.uuid})
            else:
                # Powering off node to setup networking for port and
                # ensure that the state is reset if it is inadvertently
                # on for any unknown reason.
                manager_utils.node_power_action(task, states.POWER_OFF)
            if task.driver.storage.should_write_image(task):
                # NOTE(vdrok): in case of rebuild, we have tenant network
                # already configured, unbind tenant ports if present
                if not fast_track_deploy:
                    power_state_to_restore = (
                        manager_utils.power_on_node_if_needed(task))

                task.driver.network.unconfigure_tenant_networks(task)
                task.driver.network.add_provisioning_network(task)
                if not fast_track_deploy:
                    manager_utils.restore_power_state_if_needed(
                        task, power_state_to_restore)
                else:
                    # Fast track sequence in progress
                    _update_instance_info()
            # Signal to storage driver to attach volumes
            task.driver.storage.attach_volumes(task)
            if (not task.driver.storage.should_write_image(task)
                    or fast_track_deploy):
                # We have nothing else to do as this is handled in the
                # backend storage system, and we can return to the caller
                # as we do not need to boot the agent to deploy.
                # Alternatively, we could be in a fast track deployment
                # and again, we should have nothing to do here.
                return
        if node.provision_state in (states.ACTIVE, states.UNRESCUING):
            # Call is due to conductor takeover
            task.driver.boot.prepare_instance(task)
        elif node.provision_state != states.ADOPTING:
            if node.provision_state not in (states.RESCUING, states.RESCUEWAIT,
                                            states.RESCUE, states.RESCUEFAIL):
                _update_instance_info()
            if CONF.agent.manage_agent_boot:
                deploy_opts = deploy_utils.build_agent_options(node)
                task.driver.boot.prepare_ramdisk(task, deploy_opts)