Exemplo n.º 1
0
    def test_get_node_capability(self):
        properties = {"capabilities": "cap1:value1, cap2: value2"}
        self.node.properties = properties
        expected = "value1"
        expected2 = "value2"

        result = driver_utils.get_node_capability(self.node, "cap1")
        result2 = driver_utils.get_node_capability(self.node, "cap2")
        self.assertEqual(expected, result)
        self.assertEqual(expected2, result2)
Exemplo n.º 2
0
    def test_get_node_capability(self):
        properties = {'capabilities': 'cap1:value1, cap2: value2'}
        self.node.properties = properties
        expected = 'value1'
        expected2 = 'value2'

        result = driver_utils.get_node_capability(self.node, 'cap1')
        result2 = driver_utils.get_node_capability(self.node, 'cap2')
        self.assertEqual(expected, result)
        self.assertEqual(expected2, result2)
Exemplo n.º 3
0
def dhcp_options_for_instance(task):
    """Retrieves the DHCP PXE boot options.

    :param task: A TaskManager instance.
    """
    dhcp_opts = []
    if CONF.pxe.ipxe_enabled:
        script_name = os.path.basename(CONF.pxe.ipxe_boot_script)
        ipxe_script_url = '/'.join([CONF.pxe.http_url, script_name])
        # if the request comes from dumb firmware send them the iPXE
        # boot image. !175 == non-iPXE.
        # http://ipxe.org/howto/dhcpd#ipxe-specific_options
        dhcp_opts.append({'opt_name': '!175,bootfile-name',
                          'opt_value': CONF.pxe.pxe_bootfile_name})
        # If the request comes from iPXE, direct it to boot from the
        # iPXE script
        dhcp_opts.append({'opt_name': 'bootfile-name',
                          'opt_value': ipxe_script_url})
    else:
        if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
            boot_file = CONF.pxe.uefi_pxe_bootfile_name
        else:
            boot_file = CONF.pxe.pxe_bootfile_name

        dhcp_opts.append({'opt_name': 'bootfile-name',
                          'opt_value': boot_file})

    dhcp_opts.append({'opt_name': 'server-ip-address',
                      'opt_value': CONF.pxe.tftp_server})
    dhcp_opts.append({'opt_name': 'tftp-server',
                      'opt_value': CONF.pxe.tftp_server})
    return dhcp_opts
Exemplo n.º 4
0
    def prepare(self, task):
        """Prepare the deployment environment for this task's node.

        Generates the TFTP configuration for PXE-booting both the deployment
        and user images, fetches the TFTP image from Glance and add it to the
        local cache.

        :param task: a TaskManager instance containing the node to act on.
        """
        # TODO(deva): optimize this if rerun on existing files
        if CONF.pxe.ipxe_enabled:
            # Copy the iPXE boot script to HTTP root directory
            bootfile_path = os.path.join(CONF.pxe.http_root,
                                   os.path.basename(CONF.pxe.ipxe_boot_script))
            shutil.copyfile(CONF.pxe.ipxe_boot_script, bootfile_path)
        pxe_info = _get_image_info(task.node, task.context)
        pxe_options = _build_pxe_config_options(task.node, pxe_info,
                                                task.context)

        if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
            pxe_config_template = CONF.pxe.uefi_pxe_config_template
        else:
            pxe_config_template = CONF.pxe.pxe_config_template

        pxe_utils.create_pxe_config(task, pxe_options,
                                    pxe_config_template)
        _cache_ramdisk_kernel(task.context, task.node, pxe_info)
Exemplo n.º 5
0
def get_boot_mode_for_deploy(node):
    """Returns the boot mode that would be used for deploy.

    This method returns boot mode to used for deploy using following order:
    It returns 'uefi' if 'secure_boot' is set to 'true' in
    'instance_info/capabilities' of node.
    It returns value of 'boot_mode' in 'properties/capabilities' of node.
    It returns boot mode specified in 'instance_info/deploy_boot_mode' of
    node.
    It would return None if boot mode is present neither in 'capabilities' of
    node 'properties' nor in node's 'instance_info'.

    :param node: an ironic node object.
    :returns: 'bios', 'uefi' or None
    """

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

    boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
    if boot_mode is None:
        instance_info = node.instance_info
        boot_mode = instance_info.get('deploy_boot_mode')

    LOG.debug('Deploy boot mode is %(boot_mode)s for %(node)s.',
              {'boot_mode': boot_mode, 'node': node.uuid})
    return boot_mode
Exemplo n.º 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.
        :raises: MissingParameterValue
        """

        # Check the boot_mode capability parameter value.
        driver_utils.validate_boot_mode_capability(task.node)

        if CONF.pxe.ipxe_enabled:
            if not CONF.pxe.http_url or not CONF.pxe.http_root:
                raise exception.MissingParameterValue(_(
                    "iPXE boot is enabled but no HTTP URL or HTTP "
                    "root was specified."))
            # iPXE and UEFI should not be configured together.
            if driver_utils.get_node_capability(task.node,
                                                'boot_mode') == 'uefi':
                LOG.error(_LE("UEFI boot mode is not supported with "
                              "iPXE boot enabled."))
                raise exception.InvalidParameterValue(_(
                    "Conflict: iPXE is enabled, but cannot be used with node"
                    "%(node_uuid)s configured to use UEFI boot") %
                    {'node_uuid': task.node.uuid})

        d_info = _parse_deploy_info(task.node)

        iscsi_deploy.validate(task)

        props = ['kernel_id', 'ramdisk_id']
        iscsi_deploy.validate_glance_image_properties(task.context, d_info,
                                                      props)
Exemplo n.º 7
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
    '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 'instance_info' (which could also be None).

    :param node: an ironic node object.
    :returns: 'bios', 'uefi' or None
    """

    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"

    boot_mode = driver_utils.get_node_capability(node, "boot_mode")
    if boot_mode is None:
        instance_info = node.instance_info
        boot_mode = instance_info.get("deploy_boot_mode")

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

    return boot_mode.lower() if boot_mode else boot_mode
Exemplo n.º 8
0
def create_pxe_config(task, pxe_options, template=None):
    """Generate PXE configuration file and MAC address links for it.

    This method will generate the PXE configuration file for the task's
    node under a directory named with the UUID of that node. For each
    MAC address (port) of that node, a symlink for the configuration file
    will be created under the PXE configuration directory, so regardless
    of which port boots first they'll get the same PXE configuration.

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

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

    if template is None:
        template = CONF.pxe.pxe_config_template

    _ensure_config_dirs_exist(task.node.uuid)

    pxe_config_file_path = get_pxe_config_file_path(task.node.uuid)
    pxe_config = _build_pxe_config(pxe_options, template)
    utils.write_to_file(pxe_config_file_path, pxe_config)

    if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
        _link_ip_address_pxe_configs(task)
    else:
        _link_mac_pxe_configs(task)
Exemplo n.º 9
0
def clean_up_pxe_config(task):
    """Clean up the TFTP environment for the task's node.

    :param task: A TaskManager instance.

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

    if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
        api = dhcp_factory.DHCPFactory().provider
        ip_addresses = api.get_ip_addresses(task)
        if not ip_addresses:
            return

        for port_ip_address in ip_addresses:
            try:
                ip_address_path = _get_pxe_ip_address_path(port_ip_address)
            except exception.InvalidIPv4Address:
                continue
            utils.unlink_without_raise(ip_address_path)
    else:
        for mac in driver_utils.get_node_mac_addresses(task):
            utils.unlink_without_raise(_get_pxe_mac_path(mac))

    utils.rmtree_without_raise(os.path.join(get_root_dir(),
                                            task.node.uuid))
Exemplo n.º 10
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' 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
    '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 'instance_info' (which could also be None).

    :param node: an ironic node object.
    :returns: 'bios', 'uefi' or None
    """

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

    boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
    if boot_mode is None:
        instance_info = node.instance_info
        boot_mode = instance_info.get('deploy_boot_mode')

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

    return boot_mode.lower() if boot_mode else boot_mode
Exemplo n.º 11
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.
        :raises: MissingParameterValue
        """
        node = task.node

        # Check the boot_mode and boot_option capabilities values.
        driver_utils.validate_boot_mode_capability(node)
        driver_utils.validate_boot_option_capability(node)

        boot_mode = driver_utils.get_node_capability(node, 'boot_mode')
        boot_option = driver_utils.get_node_capability(node, 'boot_option')

        # NOTE(lucasagomes): We don't support UEFI + localboot because
        # we do not support creating an EFI boot partition, including the
        # EFI modules and managing the bootloader variables via efibootmgr.
        if boot_mode == 'uefi' and boot_option == 'local':
            error_msg = (_("Local boot is requested, but can't be "
                           "used with node %s because it's configured "
                           "to use UEFI boot") % node.uuid)
            LOG.error(error_msg)
            raise exception.InvalidParameterValue(error_msg)

        if CONF.pxe.ipxe_enabled:
            if not CONF.pxe.http_url or not CONF.pxe.http_root:
                raise exception.MissingParameterValue(_(
                    "iPXE boot is enabled but no HTTP URL or HTTP "
                    "root was specified."))
            # iPXE and UEFI should not be configured together.
            if boot_mode == 'uefi':
                LOG.error(_LE("UEFI boot mode is not supported with "
                              "iPXE boot enabled."))
                raise exception.InvalidParameterValue(_(
                    "Conflict: iPXE is enabled, but cannot be used with node"
                    "%(node_uuid)s configured to use UEFI boot") %
                    {'node_uuid': node.uuid})

        d_info = _parse_deploy_info(node)

        iscsi_deploy.validate(task)

        props = ['kernel_id', 'ramdisk_id']
        iscsi_deploy.validate_glance_image_properties(task.context, d_info,
                                                      props)
Exemplo n.º 12
0
    def validate(self, task):
        """Validate storage_interface configuration for Cinder usage.

        In order to provide fail fast functionality prior to nodes being
        requested to enter the active state, this method performs basic
        checks of the volume connectors, volume targets, and operator
        defined capabilities. These checks are to help ensure that we
        should have a compatible configuration prior to activating the
        node.

        :param task: The task object.
        :raises: InvalidParameterValue If a misconfiguration or mismatch
                 exists that would prevent storage the cinder storage
                 driver from initializing attachments.
        """

        found_types = self._validate_connectors(task)
        node = task.node
        iscsi_boot = strutils.bool_from_string(
            utils.get_node_capability(node, 'iscsi_boot'))
        fc_boot = strutils.bool_from_string(
            utils.get_node_capability(node, 'fibre_channel_boot'))

        # Validate capability configuration against configured volumes
        # such that we raise errors for missing connectors if the
        # boot capability is defined.
        if iscsi_boot and not found_types['iscsi_found']:
            valid_types = ', '.join(VALID_ISCSI_TYPES)
            msg = (_("In order to enable the 'iscsi_boot' capability for "
                     "the node, an associated volume_connector type "
                     "must be valid for iSCSI (%(options)s).") %
                   {'options': valid_types})
            self._fail_validation(task, msg)

        if fc_boot and not found_types['fc_found']:
            valid_types = ', '.join(VALID_FC_TYPES)
            msg = (_("In order to enable the 'fibre_channel_boot' capability "
                     "for the node, an associated volume_connector type must "
                     "be valid for Fibre Channel (%(options)s).") %
                   {'options': valid_types})
            self._fail_validation(task, msg)

        self._validate_targets(task, found_types, iscsi_boot, fc_boot)
Exemplo n.º 13
0
    def prepare(self, task):
        """Prepare the deployment environment for this task's node.

        :param task: a TaskManager instance containing the node to act on.
        :raises: IloOperationError, if some operation on iLO failed.
        """
        boot_mode = driver_utils.get_node_capability(task.node, 'boot_mode')
        if boot_mode is not None:
            ilo_common.set_boot_mode(task.node, boot_mode)
        else:
            ilo_common.update_boot_mode_capability(task)
Exemplo n.º 14
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 = 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),
                }
            )
Exemplo n.º 15
0
 def test__prepare_node_for_deploy(self,
                                   func_node_power_action,
                                   func_disable_secure_boot,
                                   func_update_boot_mode):
     with task_manager.acquire(self.context, self.node.uuid,
                               shared=False) as task:
         func_disable_secure_boot.return_value = False
         ilo_deploy._prepare_node_for_deploy(task)
         func_node_power_action.assert_called_once_with(task,
                                                        states.POWER_OFF)
         func_disable_secure_boot.assert_called_once_with(task)
         func_update_boot_mode.assert_called_once_with(task)
         bootmode = driver_utils.get_node_capability(task.node, "boot_mode")
         self.assertIsNone(bootmode)
Exemplo n.º 16
0
    def _continue_deploy(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')

        _destroy_token_file(node)

        root_uuid = iscsi_deploy.continue_deploy(task, **kwargs)

        if not root_uuid:
            return

        # 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'] = root_uuid
        node.driver_internal_info = driver_internal_info
        node.save()

        try:
            if iscsi_deploy.get_boot_option(node) == "local":
                try_set_boot_device(task, boot_devices.DISK)
                # If it's going to boot from the local disk, get rid of
                # the PXE configuration files used for the deployment
                pxe_utils.clean_up_pxe_config(task)
            else:
                pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
                deploy_utils.switch_pxe_config(pxe_config_path, root_uuid,
                    driver_utils.get_node_capability(node, 'boot_mode'))

            deploy_utils.notify_deploy_complete(kwargs['address'])
            LOG.info(_LI('Deployment to node %s done'), node.uuid)
            task.process_event('done')
        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)
Exemplo n.º 17
0
    def set_boot_device(self, task, device, persistent=False):
        """Set the boot device for a node.

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

        :param task: A task from TaskManager.
        :param device: The boot device, one of the supported devices
                       listed in :mod:`ironic.common.boot_devices`.
        :param persistent: Boolean value. True if the boot device will
                           persist to all future boots, False if not.
                           Default: False.
        :raises: InvalidParameterValue if an invalid boot device is
                 specified.
        :raises: MissingParameterValue if a required parameter is missing.
        :raises: IPMIFailure on an error from ipmitool.

        """
        if device not in self.get_supported_boot_devices(task):
            raise exception.InvalidParameterValue(_(
                "Invalid boot device %s specified.") % device)

        uefi_mode = (
            driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi')

        # disable 60 secs timer
        timeout_disable = "0x00 0x08 0x03 0x08"
        ipmitool.send_raw(task, timeout_disable)

        # note(naohirot):
        # Set System Boot Options : ipmi cmd '0x08', bootparam '0x05'
        #
        # $ ipmitool raw 0x00 0x08 0x05 data1 data2 0x00 0x00 0x00
        #
        # data1 : '0xe0' persistent + uefi
        #         '0xc0' persistent + bios
        #         '0xa0' next only  + uefi
        #         '0x80' next only  + bios
        # data2 : boot device defined in the dict _BOOTPARAM5_DATA2

        bootparam5 = '0x00 0x08 0x05 %s %s 0x00 0x00 0x00'
        if persistent:
            data1 = '0xe0' if uefi_mode else '0xc0'
        else:
            data1 = '0xa0' if uefi_mode else '0x80'
        data2 = _BOOTPARAM5_DATA2[device]

        cmd8 = bootparam5 % (data1, data2)
        ipmitool.send_raw(task, cmd8)
Exemplo n.º 18
0
def try_set_boot_device(task, device, persistent=True):
    # NOTE(faizan): Under UEFI boot mode, setting of boot device may differ
    # between different machines. IPMI does not work for setting boot
    # devices in UEFI mode for certain machines.
    # Expected IPMI failure for uefi boot mode. Logging a message to
    # set the boot device manually and continue with deploy.
    try:
        manager_utils.node_set_boot_device(task, device, persistent=persistent)
    except exception.IPMIFailure:
        if driver_utils.get_node_capability(task.node,
                                            'boot_mode') == 'uefi':
            LOG.warning(_LW("ipmitool is unable to set boot device while "
                            "the node %s is in UEFI boot mode. Please set "
                            "the boot device manually.") % task.node.uuid)
        else:
            raise
Exemplo n.º 19
0
def try_set_boot_device(task, device, persistent=True):
    # NOTE(faizan): Under UEFI boot mode, setting of boot device may differ
    # between different machines. IPMI does not work for setting boot
    # devices in UEFI mode for certain machines.
    # Expected IPMI failure for uefi boot mode. Logging a message to
    # set the boot device manually and continue with deploy.
    try:
        manager_utils.node_set_boot_device(task, device, persistent=persistent)
    except exception.IPMIFailure:
        if driver_utils.get_node_capability(task.node,
                                            'boot_mode') == 'uefi':
            LOG.warning(_LW("ipmitool is unable to set boot device while "
                            "the node %s is in UEFI boot mode. Please set "
                            "the boot device manually.") % task.node.uuid)
        else:
            raise
Exemplo n.º 20
0
 def test__prepare_node_for_deploy_sec_boot_on_inst_info(
         self, func_node_power_action, func_disable_secure_boot,
         func_update_boot_mode):
     instance_info = {'capabilities': '{"secure_boot": "true"}'}
     with task_manager.acquire(self.context, self.node.uuid,
                               shared=False) as task:
         func_disable_secure_boot.return_value = True
         task.node.instance_info = instance_info
         ilo_deploy._prepare_node_for_deploy(task)
         func_node_power_action.assert_called_once_with(task,
                                                        states.POWER_OFF)
         func_disable_secure_boot.assert_called_once_with(task)
         self.assertFalse(func_update_boot_mode.called)
         bootmode = driver_utils.get_node_capability(task.node, "boot_mode")
         self.assertIsNone(bootmode)
         self.assertNotIn('deploy_boot_mode', task.node.instance_info)
Exemplo n.º 21
0
    def prepare(self, task):
        """Prepare the deployment environment for this task's node.

        If the node's 'capabilities' property includes a boot_mode, that
        boot mode will be applied for the node. Otherwise, the existing
        boot mode of the node is used in the node's 'capabilities' property.

        PXEDeploys' prepare method is then called, to prepare the deploy
        environment for the node

        :param task: a TaskManager instance containing the node to act on.
        """
        boot_mode = driver_utils.get_node_capability(task.node, 'boot_mode')
        if boot_mode is None:
            ilo_common.update_boot_mode_capability(task)
        else:
            ilo_common.set_boot_mode(task.node, boot_mode)
        super(IloPXEDeploy, self).prepare(task)
Exemplo n.º 22
0
    def set_boot_device(self, task, device, persistent=False):
        """Set the boot device for a node.

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

        :param task: A task from TaskManager.
        :param device: The boot device, one of the supported devices
                       listed in :mod:`ironic.common.boot_devices`.
        :param persistent: Boolean value. True if the boot device will
                           persist to all future boots, False if not.
                           Default: False.
        :raises: InvalidParameterValue if an invalid boot device is
                 specified.
        :raises: MissingParameterValue if a required parameter is missing.
        :raises: IPMIFailure on an error from ipmitool.

        """
        if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
            if device not in self.get_supported_boot_devices():
                raise exception.InvalidParameterValue(_(
                    "Invalid boot device %s specified.") % device)
            timeout_disable = "0x00 0x08 0x03 0x08"
            ipmitool.send_raw(task, timeout_disable)

            # note(naohirot): As of ipmitool version 1.8.13,
            # in case of chassis command, the efiboot option doesn't
            # get set with persistent at the same time.
            # $ ipmitool chassis bootdev pxe options=efiboot,persistent
            # In case of raw command, however, both can be set at the
            # same time.
            # $ ipmitool raw 0x00 0x08 0x05 0xe0 0x04 0x00 0x00 0x00
            #                           data1^^  ^^data2
            # ipmi cmd '0x08' : Set System Boot Options
            # data1    '0xe0' : persistent and uefi
            # data1    '0xa0' : next boot only and uefi
            #
            data1 = '0xe0' if persistent else '0xa0'
            bootparam5 = '0x00 0x08 0x05 %s %s 0x00 0x00 0x00'
            cmd08 = bootparam5 % (data1, _BOOTPARAM5_DATA2[device])
            ipmitool.send_raw(task, cmd08)

        else:
            super(IRMCManagement, self).set_boot_device(
                task, device, persistent)
Exemplo n.º 23
0
    def deploy(self, task):
        """Start deployment of the task's node'.

        Fetches instance image, creates a temporary keystone token file,
        updates the DHCP port options for next boot, and issues a reboot
        request to the power driver.
        This causes the node to boot into the deployment ramdisk and triggers
        the next phase of PXE-based deployment via
        VendorPassthru._continue_deploy().

        :param task: a TaskManager instance containing the node to act on.
        :returns: deploy state DEPLOYING.
        """
        iscsi_deploy.cache_instance_image(task.context, task.node)
        iscsi_deploy.check_image_size(task)

        # TODO(yuriyz): more secure way needed for pass auth token
        #               to deploy ramdisk
        _create_token_file(task)
        dhcp_opts = pxe_utils.dhcp_options_for_instance(task)
        provider = dhcp_factory.DHCPFactory()
        provider.update_dhcp(task, dhcp_opts)

        # NOTE(faizan): Under UEFI boot mode, setting of boot device may differ
        # between different machines. IPMI does not work for setting boot
        # devices in UEFI mode for certain machines.
        # Expected IPMI failure for uefi boot mode. Logging a message to
        # set the boot device manually and continue with deploy.
        try:
            manager_utils.node_set_boot_device(task, 'pxe', persistent=True)
        except exception.IPMIFailure:
            if driver_utils.get_node_capability(task.node,
                                                'boot_mode') == 'uefi':
                LOG.warning(_LW("ipmitool is unable to set boot device while "
                                "the node is in UEFI boot mode."
                                "Please set the boot device manually."))
            else:
                raise

        manager_utils.node_power_action(task, states.REBOOT)

        return states.DEPLOYWAIT
Exemplo n.º 24
0
    def set_boot_device(self, task, device, persistent=False):
        """Set the boot device for the task's node.

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

        :param task: a task from TaskManager.
        :param device: the boot device.
        :param persistent: Boolean value. True if the boot device will
                           persist to all future boots, False if not.
                           Default: False.
        :raises: InvalidParameterValue if an invalid boot device is specified.
        """
        self._check_valid_device(device, task.node)
        client, blade_id = msftocs_common.get_client_info(
            task.node.driver_info)

        boot_mode = drivers_utils.get_node_capability(task.node, 'boot_mode')
        uefi = (boot_mode == 'uefi')

        boot_type = DEVICE_TO_BOOT_TYPE_MAP[device]
        client.set_next_boot(blade_id, boot_type, persistent, uefi)
Exemplo n.º 25
0
def dhcp_options_for_instance(task):
    """Retrieves the DHCP PXE boot options.

    :param task: A TaskManager instance.
    """
    dhcp_opts = []
    if CONF.pxe.ipxe_enabled:
        script_name = os.path.basename(CONF.pxe.ipxe_boot_script)
        ipxe_script_url = '/'.join([CONF.pxe.http_url, script_name])
        dhcp_provider_name = dhcp_factory.CONF.dhcp.dhcp_provider
        # if the request comes from dumb firmware send them the iPXE
        # boot image.
        if dhcp_provider_name == 'neutron':
            # Neutron use dnsmasq as default DHCP agent, add extra config
            # to neutron "dhcp-match=set:ipxe,175" and use below option
            dhcp_opts.append({'opt_name': 'tag:!ipxe,bootfile-name',
                              'opt_value': CONF.pxe.pxe_bootfile_name})
        else:
            # !175 == non-iPXE.
            # http://ipxe.org/howto/dhcpd#ipxe-specific_options
            dhcp_opts.append({'opt_name': '!175,bootfile-name',
                              'opt_value': CONF.pxe.pxe_bootfile_name})
        # If the request comes from iPXE, direct it to boot from the
        # iPXE script
        dhcp_opts.append({'opt_name': 'bootfile-name',
                          'opt_value': ipxe_script_url})
    else:
        if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
            boot_file = CONF.pxe.uefi_pxe_bootfile_name
        else:
            boot_file = CONF.pxe.pxe_bootfile_name

        dhcp_opts.append({'opt_name': 'bootfile-name',
                          'opt_value': boot_file})

    dhcp_opts.append({'opt_name': 'server-ip-address',
                      'opt_value': CONF.pxe.tftp_server})
    dhcp_opts.append({'opt_name': 'tftp-server',
                      'opt_value': CONF.pxe.tftp_server})
    return dhcp_opts
Exemplo n.º 26
0
    def _continue_deploy(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')

        _destroy_token_file(node)

        root_uuid = iscsi_deploy.continue_deploy(task, **kwargs)

        if not root_uuid:
            return

        try:
            if iscsi_deploy.get_boot_option(node) == "local":
                try_set_boot_device(task, boot_devices.DISK)
                # If it's going to boot from the local disk, get rid of
                # the PXE configuration files used for the deployment
                pxe_utils.clean_up_pxe_config(task)
            else:
                pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
                deploy_utils.switch_pxe_config(pxe_config_path, root_uuid,
                    driver_utils.get_node_capability(node, 'boot_mode'))

            deploy_utils.notify_deploy_complete(kwargs['address'])
            LOG.info(_LI('Deployment to node %s done'), node.uuid)
            task.process_event('done')
        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)
Exemplo n.º 27
0
    def _continue_deploy(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.
        """
        node = task.node

        if node.provision_state != states.DEPLOYWAIT:
            LOG.error(_LE('Node %s is not waiting to be deployed.'), node.uuid)
            return

        _destroy_token_file(node)

        root_uuid = iscsi_deploy.continue_deploy(task, **kwargs)

        if not root_uuid:
            return

        try:
            pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
            deploy_utils.switch_pxe_config(pxe_config_path, root_uuid,
                          driver_utils.get_node_capability(node, 'boot_mode'))

            deploy_utils.notify_deploy_complete(kwargs['address'])

            LOG.info(_LI('Deployment to node %s done'), node.uuid)
            node.provision_state = states.ACTIVE
            node.target_provision_state = states.NOSTATE
            node.save()
        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.')
            iscsi_deploy.set_failed_state(task, msg)
Exemplo n.º 28
0
    def _continue_deploy(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')

        _destroy_token_file(node)

        root_uuid = iscsi_deploy.continue_deploy(task, **kwargs)

        if not root_uuid:
            return

        try:
            pxe_config_path = pxe_utils.get_pxe_config_file_path(node.uuid)
            deploy_utils.switch_pxe_config(
                pxe_config_path, root_uuid,
                driver_utils.get_node_capability(node, 'boot_mode'))

            deploy_utils.notify_deploy_complete(kwargs['address'])

            LOG.info(_LI('Deployment to node %s done'), node.uuid)
            task.process_event('done')
        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)
Exemplo n.º 29
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
    # 2) properties.capabilities
    # 3) driver_internal_info
    #
    # Because:
    #
    # (1) can be deleted before teardown
    # (3) will never be touched if node properties/capabilities
    #     are still present.
    # (2) becomes operational default as the last resort

    instance_info = node.instance_info

    cap_boot_mode = driver_utils.get_node_capability(node, 'boot_mode')

    boot_mode = instance_info.get('deploy_boot_mode')
    if boot_mode is None:
        boot_mode = cap_boot_mode
        if cap_boot_mode is None:
            driver_internal_info = node.driver_internal_info
            boot_mode = 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
Exemplo n.º 30
0
    def inspect_hardware(self, task):
        """Inspect hardware to get the hardware properties.

        Inspects hardware to get the essential properties.
        It fails if any of the essential properties
        are not received from the node.

        :param task: a TaskManager instance.
        :raises: HardwareInspectionFailure if essential properties
                 could not be retrieved successfully.
        :returns: The resulting state of inspection.

        """
        system = redfish_utils.get_system(task.node)

        # get the essential properties and update the node properties
        # with it.
        inspected_properties = task.node.properties

        if system.memory_summary and system.memory_summary.size_gib:
            inspected_properties['memory_mb'] = str(
                system.memory_summary.size_gib * units.Ki)

        if system.processors and system.processors.summary:
            cpus, arch = system.processors.summary
            if cpus:
                inspected_properties['cpus'] = cpus

            if arch:
                try:
                    inspected_properties['cpu_arch'] = CPU_ARCH_MAP[arch]

                except KeyError:
                    LOG.warning("Unknown CPU arch %(arch)s discovered "
                                "for node %(node)s", {'node': task.node.uuid,
                                                      'arch': arch})

        simple_storage_size = 0

        try:
            LOG.debug("Attempting to discover system simple storage size for "
                      "node %(node)s", {'node': task.node.uuid})
            if (system.simple_storage and
                    system.simple_storage.disks_sizes_bytes):
                simple_storage_size = [
                    size for size in system.simple_storage.disks_sizes_bytes
                    if size >= 4 * units.Gi
                ] or [0]

                simple_storage_size = simple_storage_size[0]

        except sushy.exceptions.SushyError as ex:
            LOG.debug("No simple storage information discovered "
                      "for node %(node)s: %(err)s", {'node': task.node.uuid,
                                                     'err': ex})

        storage_size = 0

        try:
            LOG.debug("Attempting to discover system storage volume size for "
                      "node %(node)s", {'node': task.node.uuid})
            if system.storage and system.storage.volumes_sizes_bytes:
                storage_size = [
                    size for size in system.storage.volumes_sizes_bytes
                    if size >= 4 * units.Gi
                ] or [0]

                storage_size = storage_size[0]

        except sushy.exceptions.SushyError as ex:
            LOG.debug("No storage volume information discovered "
                      "for node %(node)s: %(err)s", {'node': task.node.uuid,
                                                     'err': ex})

        try:
            if not storage_size:
                LOG.debug("Attempting to discover system storage drive size "
                          "for node %(node)s", {'node': task.node.uuid})
                if system.storage and system.storage.drives_sizes_bytes:
                    storage_size = [
                        size for size in system.storage.drives_sizes_bytes
                        if size >= 4 * units.Gi
                    ] or [0]

                    storage_size = storage_size[0]

        except sushy.exceptions.SushyError as ex:
            LOG.debug("No storage drive information discovered "
                      "for node %(node)s: %(err)s", {'node': task.node.uuid,
                                                     'err': ex})

        # NOTE(etingof): pick the smallest disk larger than 4G among available
        if simple_storage_size and storage_size:
            local_gb = min(simple_storage_size, storage_size)

        else:
            local_gb = max(simple_storage_size, storage_size)

        # Note(deray): Convert the received size to GiB and reduce the
        # value by 1 GB as consumers like Ironic requires the ``local_gb``
        # to be returned 1 less than actual size.
        local_gb = max(0, int(local_gb / units.Gi - 1))

        # TODO(etingof): should we respect root device hints here?

        if local_gb:
            inspected_properties['local_gb'] = str(local_gb)
        else:
            LOG.warning("Could not provide a valid storage size configured "
                        "for node %(node)s. Assuming this is a disk-less node",
                        {'node': task.node.uuid})
            inspected_properties['local_gb'] = '0'

        if system.boot.mode:
            if not drivers_utils.get_node_capability(task.node, 'boot_mode'):
                capabilities = utils.get_updated_capabilities(
                    inspected_properties.get('capabilities', ''),
                    {'boot_mode': BOOT_MODE_MAP[system.boot.mode]})

                inspected_properties['capabilities'] = capabilities

        valid_keys = self.ESSENTIAL_PROPERTIES
        missing_keys = valid_keys - set(inspected_properties)
        if missing_keys:
            error = (_('Failed to discover the following properties: '
                       '%(missing_keys)s on node %(node)s'),
                     {'missing_keys': ', '.join(missing_keys),
                      'node': task.node.uuid})
            raise exception.HardwareInspectionFailure(error=error)

        task.node.properties = inspected_properties
        task.node.save()

        LOG.debug("Node properties for %(node)s are updated as "
                  "%(properties)s", {'properties': inspected_properties,
                                     'node': task.node.uuid})

        if (system.ethernet_interfaces and
                system.ethernet_interfaces.summary):
            macs = system.ethernet_interfaces.summary

            # Create ports for the discovered NICs being in 'enabled' state
            enabled_macs = {nic_mac: nic_state
                            for nic_mac, nic_state in macs.items()
                            if nic_state == sushy.STATE_ENABLED}
            if enabled_macs:
                inspect_utils.create_ports_if_not_exist(
                    task, enabled_macs, get_mac_address=lambda x: x[0])
            else:
                LOG.warning("Not attempting to create any port as no NICs "
                            "were discovered in 'enabled' state for node "
                            "%(node)s: %(mac_data)s",
                            {'mac_data': macs, 'node': task.node.uuid})
        else:
            LOG.warning("No NIC information discovered "
                        "for node %(node)s", {'node': task.node.uuid})

        return states.MANAGEABLE
Exemplo n.º 31
0
def _get_boot_iso(task, root_uuid):
    """This method returns a boot ISO to boot the node.

    It chooses one of the two options in the order as below:
    1. Image deployed has a meta-property 'boot_iso' in Glance. This should
       refer to the UUID of the boot_iso which exists in Glance.
    2. Generates a boot ISO on the fly using kernel and ramdisk mentioned in
       the image deployed. It uploads the generated boot ISO to Swift.

    :param task: a TaskManager instance containing the node to act on.
    :param root_uuid: the uuid of the root partition.
    :returns: the information about the boot ISO. Returns the information in
        the format 'glance:<glance-boot-iso-uuid>' or
        'swift:<swift-boot_iso-object-name>'.  In case of Swift, it is assumed
        that the object exists in CONF.ilo.swift_ilo_container.
        On error finding the boot iso, it returns None.
    :raises: MissingParameterValue, if any of the required parameters are
        missing in the node's driver_info or instance_info.
    :raises: InvalidParameterValue, if any of the parameters have invalid
        value in the node's driver_info or instance_info.
    :raises: SwiftOperationError, if operation with Swift fails.
    :raises: ImageCreationFailed, if creation of boot ISO failed.
    """
    # Option 1 - Check if user has provided a boot_iso in Glance.
    LOG.debug("Trying to get a boot ISO to boot the baremetal node")
    deploy_info = _parse_deploy_info(task.node)

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

    boot_iso_uuid = glance_properties.get('boot_iso')
    kernel_uuid = glance_properties.get('kernel_id')
    ramdisk_uuid = glance_properties.get('ramdisk_id')

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

    # NOTE(faizan) For uefi boot_mode, operator should provide efi capable
    # boot-iso in glance
    if driver_utils.get_node_capability(task.node, 'boot_mode') == 'uefi':
        LOG.error(_LE("Unable to find boot_iso in Glance, required to deploy "
                      "node %(node)s in UEFI boot mode."),
                  {'node': task.node.uuid})
        return

    if not kernel_uuid or not ramdisk_uuid:
        LOG.error(_LE("Unable to find 'kernel_id' and 'ramdisk_id' in Glance "
                      "image %(image)s for generating boot ISO for %(node)s"),
                  {'image': image_href, 'node': task.node.uuid})
        return

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

    # Option 2 - Create boot_iso from kernel/ramdisk, upload to Swift
    # and provide its name.
    boot_iso_object_name = _get_boot_iso_object_name(task.node)
    kernel_params = CONF.pxe.pxe_append_params
    container = CONF.ilo.swift_ilo_container

    with tempfile.NamedTemporaryFile() as fileobj:
        boot_iso_tmp_file = fileobj.name
        images.create_boot_iso(task.context, boot_iso_tmp_file,
                kernel_uuid, ramdisk_uuid, root_uuid, kernel_params)
        swift_api = swift.SwiftAPI()
        swift_api.create_object(container, boot_iso_object_name,
                boot_iso_tmp_file)

    LOG.debug("Created boot_iso %s in Swift", boot_iso_object_name)

    return 'swift:%s' % boot_iso_object_name
Exemplo n.º 32
0
    def inspect_hardware(self, task):
        """Inspect hardware to get the hardware properties.

        Inspects hardware to get the essential properties.
        It fails if any of the essential properties
        are not received from the node.

        :param task: a TaskManager instance.
        :raises: HardwareInspectionFailure if essential properties
                 could not be retrieved successfully.
        :returns: The resulting state of inspection.

        """
        system = redfish_utils.get_system(task.node)

        # get the essential properties and update the node properties
        # with it.
        inspected_properties = task.node.properties

        if system.memory_summary and system.memory_summary.size_gib:
            inspected_properties['memory_mb'] = str(
                system.memory_summary.size_gib * units.Ki)

        if system.processors and system.processors.summary:
            cpus, arch = system.processors.summary
            if cpus:
                inspected_properties['cpus'] = cpus

            if arch:
                try:
                    inspected_properties['cpu_arch'] = CPU_ARCH_MAP[arch]

                except KeyError:
                    LOG.warning(
                        "Unknown CPU arch %(arch)s discovered "
                        "for node %(node)s", {
                            'node': task.node.uuid,
                            'arch': arch
                        })

        simple_storage_size = 0

        try:
            LOG.debug(
                "Attempting to discover system simple storage size for "
                "node %(node)s", {'node': task.node.uuid})
            if (system.simple_storage
                    and system.simple_storage.disks_sizes_bytes):
                simple_storage_size = [
                    size for size in system.simple_storage.disks_sizes_bytes
                    if size >= 4 * units.Gi
                ] or [0]

                simple_storage_size = simple_storage_size[0]

        except sushy.exceptions.SushyError as ex:
            LOG.debug(
                "No simple storage information discovered "
                "for node %(node)s: %(err)s", {
                    'node': task.node.uuid,
                    'err': ex
                })

        storage_size = 0

        try:
            LOG.debug(
                "Attempting to discover system storage volume size for "
                "node %(node)s", {'node': task.node.uuid})
            if system.storage and system.storage.volumes_sizes_bytes:
                storage_size = [
                    size for size in system.storage.volumes_sizes_bytes
                    if size >= 4 * units.Gi
                ] or [0]

                storage_size = storage_size[0]

        except sushy.exceptions.SushyError as ex:
            LOG.debug(
                "No storage volume information discovered "
                "for node %(node)s: %(err)s", {
                    'node': task.node.uuid,
                    'err': ex
                })

        try:
            if not storage_size:
                LOG.debug(
                    "Attempting to discover system storage drive size "
                    "for node %(node)s", {'node': task.node.uuid})
                if system.storage and system.storage.drives_sizes_bytes:
                    storage_size = [
                        size for size in system.storage.drives_sizes_bytes
                        if size >= 4 * units.Gi
                    ] or [0]

                    storage_size = storage_size[0]

        except sushy.exceptions.SushyError as ex:
            LOG.debug(
                "No storage drive information discovered "
                "for node %(node)s: %(err)s", {
                    'node': task.node.uuid,
                    'err': ex
                })

        # NOTE(etingof): pick the smallest disk larger than 4G among available
        if simple_storage_size and storage_size:
            local_gb = min(simple_storage_size, storage_size)

        else:
            local_gb = max(simple_storage_size, storage_size)

        # Note(deray): Convert the received size to GiB and reduce the
        # value by 1 GB as consumers like Ironic requires the ``local_gb``
        # to be returned 1 less than actual size.
        local_gb = max(0, int(local_gb / units.Gi - 1))

        # TODO(etingof): should we respect root device hints here?

        if local_gb:
            inspected_properties['local_gb'] = str(local_gb)
        else:
            LOG.warning(
                "Could not provide a valid storage size configured "
                "for node %(node)s. Assuming this is a disk-less node",
                {'node': task.node.uuid})
            inspected_properties['local_gb'] = '0'

        if system.boot.mode:
            if not drivers_utils.get_node_capability(task.node, 'boot_mode'):
                capabilities = utils.get_updated_capabilities(
                    inspected_properties.get('capabilities', ''),
                    {'boot_mode': BOOT_MODE_MAP[system.boot.mode]})

                inspected_properties['capabilities'] = capabilities

        valid_keys = self.ESSENTIAL_PROPERTIES
        missing_keys = valid_keys - set(inspected_properties)
        if missing_keys:
            error = (_('Failed to discover the following properties: '
                       '%(missing_keys)s on node %(node)s'), {
                           'missing_keys': ', '.join(missing_keys),
                           'node': task.node.uuid
                       })
            raise exception.HardwareInspectionFailure(error=error)

        task.node.properties = inspected_properties
        task.node.save()

        LOG.debug(
            "Node properties for %(node)s are updated as "
            "%(properties)s", {
                'properties': inspected_properties,
                'node': task.node.uuid
            })

        if (system.ethernet_interfaces and system.ethernet_interfaces.summary):
            macs = system.ethernet_interfaces.summary

            # Create ports for the discovered NICs being in 'enabled' state
            enabled_macs = {
                nic_mac: nic_state
                for nic_mac, nic_state in macs.items()
                if nic_state == sushy.STATE_ENABLED
            }
            if enabled_macs:
                inspect_utils.create_ports_if_not_exist(
                    task, enabled_macs, get_mac_address=lambda x: x[0])
            else:
                LOG.warning(
                    "Not attempting to create any port as no NICs "
                    "were discovered in 'enabled' state for node "
                    "%(node)s: %(mac_data)s", {
                        'mac_data': macs,
                        'node': task.node.uuid
                    })
        else:
            LOG.warning("No NIC information discovered "
                        "for node %(node)s", {'node': task.node.uuid})

        return states.MANAGEABLE
Exemplo n.º 33
0
    def test_get_node_capability_returns_none(self):
        properties = {'capabilities': 'cap1:value1,cap2:value2'}
        self.node.properties = properties

        result = driver_utils.get_node_capability(self.node, 'capX')
        self.assertIsNone(result)
Exemplo n.º 34
0
    def inspect_hardware(self, task):
        """Inspect hardware to get the hardware properties.

        Inspects hardware to get the essential properties.
        It fails if any of the essential properties
        are not received from the node.

        :param task: a TaskManager instance.
        :raises: HardwareInspectionFailure if essential properties
                 could not be retrieved successfully.
        :returns: The resulting state of inspection.

        """
        system = redfish_utils.get_system(task.node)

        # get the essential properties and update the node properties
        # with it.
        inspected_properties = task.node.properties

        if system.memory_summary and system.memory_summary.size_gib:
            inspected_properties['memory_mb'] = str(
                system.memory_summary.size_gib * units.Ki)

        if system.processors and system.processors.summary:
            cpus, arch = system.processors.summary
            if cpus:
                inspected_properties['cpus'] = cpus

            if arch:
                try:
                    inspected_properties['cpu_arch'] = CPU_ARCH_MAP[arch]

                except KeyError:
                    LOG.warning(
                        "Unknown CPU arch %(arch)s discovered "
                        "for node %(node)s", {
                            'node': task.node.uuid,
                            'arch': arch
                        })

        # TODO(etingof): should we respect root device hints here?
        local_gb = self._detect_local_gb(task, system)

        if local_gb:
            inspected_properties['local_gb'] = str(local_gb)
        else:
            LOG.warning(
                "Could not provide a valid storage size configured "
                "for node %(node)s. Assuming this is a disk-less node",
                {'node': task.node.uuid})
            inspected_properties['local_gb'] = '0'

        if system.boot.mode:
            if not drivers_utils.get_node_capability(task.node, 'boot_mode'):
                capabilities = utils.get_updated_capabilities(
                    inspected_properties.get('capabilities', ''),
                    {'boot_mode': BOOT_MODE_MAP[system.boot.mode]})

                inspected_properties['capabilities'] = capabilities

        valid_keys = self.ESSENTIAL_PROPERTIES
        missing_keys = valid_keys - set(inspected_properties)
        if missing_keys:
            error = (_('Failed to discover the following properties: '
                       '%(missing_keys)s on node %(node)s'), {
                           'missing_keys': ', '.join(missing_keys),
                           'node': task.node.uuid
                       })
            raise exception.HardwareInspectionFailure(error=error)

        task.node.properties = inspected_properties
        task.node.save()

        LOG.debug(
            "Node properties for %(node)s are updated as "
            "%(properties)s", {
                'properties': inspected_properties,
                'node': task.node.uuid
            })

        self._create_ports(task, system)

        return states.MANAGEABLE