Example #1
0
def set_config(task, **kwargs):
    """Sets the pending_value parameter for each of the values passed in.

    :param task: a TaskManager instance containing the node to act on.
    :param kwargs: a dictionary of {'AttributeName': 'NewValue'}
    :raises: DracOperationError on an error from python-dracclient.
    :returns: A dictionary containing the commit_required key with a boolean
              value indicating whether commit_bios_config() needs to be called
              to make the changes.
    """
    node = task.node
    drac_job.validate_job_queue(node)

    client = drac_common.get_drac_client(node)
    if 'http_method' in kwargs:
        del kwargs['http_method']

    try:
        return client.set_bios_settings(kwargs)
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to set the BIOS settings for node '
                  '%(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Example #2
0
def delete_virtual_disk(node, virtual_disk):
    """Delete a single virtual disk on a RAID controller.

    The deleted virtual disk will be in pending state. The DRAC card will do
    the actual configuration once the changes are applied by calling the
    ``commit_config`` method.

    :param node: an ironic node object.
    :param virtual_disk: id of the virtual disk.
    :returns: a dictionary containing the commit_needed key with a boolean
              value indicating whether a config job must be created for the
              values to be applied.
    :raises: DracOperationError on an error from python-dracclient.
    """
    drac_job.validate_job_queue(node)

    client = drac_common.get_drac_client(node)

    try:
        return client.delete_virtual_disk(virtual_disk)
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to delete virtual disk '
                  '%(virtual_disk_fqdd)s for node %(node_uuid)s. '
                  'Reason: %(error)s.',
                  {'virtual_disk_fqdd': virtual_disk,
                   'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Example #3
0
    def test_validate_job_queue(self, mock_get_drac_client):
        mock_client = mock.Mock()
        mock_get_drac_client.return_value = mock_client
        mock_client.list_jobs.return_value = []

        drac_job.validate_job_queue(self.node)

        mock_client.list_jobs.assert_called_once_with(only_unfinished=True)
Example #4
0
def set_boot_device(node, device, persistent=False):
    """Set the boot device for a node.

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

    :param node: an ironic node object.
    :param device: the boot device, one of
                   :mod:`ironic.common.boot_devices`.
    :param persistent: Boolean value. True if the boot device will
                       persist to all future boots, False if not.
                       Default: False.
    :raises: DracOperationError on an error from python-dracclient.
    """

    drac_job.validate_job_queue(node)

    client = drac_common.get_drac_client(node)

    try:
        drac_boot_devices = client.list_boot_devices()

        current_boot_device = _get_boot_device(node, drac_boot_devices)
        # If we are already booting from the right device, do nothing.
        if current_boot_device == {'boot_device': device,
                                   'persistent': persistent}:
            LOG.debug('DRAC already set to boot from %s', device)
            return

        drac_boot_device = next(drac_device.id for drac_device
                                in drac_boot_devices[PERSISTENT_BOOT_MODE]
                                if _BOOT_DEVICES_MAP[device] in drac_device.id)

        if persistent:
            boot_list = PERSISTENT_BOOT_MODE
        else:
            boot_list = NON_PERSISTENT_BOOT_MODE

        client.change_boot_device_order(boot_list, drac_boot_device)
        client.commit_pending_bios_changes()
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to change boot device order for '
                  'node %(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid, 'error': exc})
        raise exception.DracOperationError(error=exc)
Example #5
0
def commit_config(task, reboot=False):
    """Commits pending changes added by set_config

    :param task: a TaskManager instance containing the node to act on.
    :param reboot: indicates whether a reboot job should be automatically
                   created with the config job.
    :raises: DracOperationError on an error from python-dracclient.
    :returns: the job_id key with the id of the newly created config job.
    """
    node = task.node
    drac_job.validate_job_queue(node)

    client = drac_common.get_drac_client(node)

    try:
        return client.commit_pending_bios_changes(reboot)
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to commit the pending BIOS changes '
                  'for node %(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Example #6
0
def create_virtual_disk(node, raid_controller, physical_disks, raid_level,
                        size_mb, disk_name=None, span_length=None,
                        span_depth=None):
    """Create a single virtual disk on a RAID controller.

    The created virtual disk will be in pending state. The DRAC card will do
    the actual configuration once the changes are applied by calling the
    ``commit_config`` method.

    :param node: an ironic node object.
    :param raid_controller: id of the RAID controller.
    :param physical_disks: ids of the physical disks.
    :param raid_level: RAID level of the virtual disk.
    :param size_mb: size of the virtual disk.
    :param disk_name: name of the virtual disk. (optional)
    :param span_depth: Number of spans in virtual disk. (optional)
    :param span_length: Number of disks per span. (optional)
    :returns: a dictionary containing the commit_needed key with a boolean
              value indicating whether a config job must be created for the
              values to be applied.
    :raises: DracOperationError on an error from python-dracclient.
    """
    drac_job.validate_job_queue(node)

    client = drac_common.get_drac_client(node)

    try:
        return client.create_virtual_disk(raid_controller, physical_disks,
                                          raid_level, size_mb, disk_name,
                                          span_length, span_depth)
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to create virtual disk for node '
                  '%(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Example #7
0
def set_boot_device(node, device, persistent=False):
    """Set the boot device for a node.

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

    :param node: an ironic node object.
    :param device: the boot device, one of
                   :mod:`ironic.common.boot_devices`.
    :param persistent: Boolean value. True if the boot device will
                       persist to all future boots, False if not.
                       Default: False.
    :raises: DracOperationError on an error from python-dracclient.
    """

    drac_job.validate_job_queue(node)

    client = drac_common.get_drac_client(node)

    try:
        drac_boot_devices = client.list_boot_devices()

        current_boot_device = _get_boot_device(node, drac_boot_devices)
        # If we are already booting from the right device, do nothing.
        if current_boot_device == {'boot_device': device,
                                   'persistent': persistent}:
            LOG.debug('DRAC already set to boot from %s', device)
            return

        persistent_boot_mode = _get_next_persistent_boot_mode(node)

        drac_boot_device = None
        for drac_device in drac_boot_devices[persistent_boot_mode]:
            for id_component in _BOOT_DEVICES_MAP[device]:
                if id_component in drac_device.id:
                    drac_boot_device = drac_device.id
                    break

            if drac_boot_device:
                break

        if drac_boot_device:
            if persistent:
                boot_list = persistent_boot_mode
            else:
                boot_list = _NON_PERSISTENT_BOOT_MODE

            client.change_boot_device_order(boot_list, drac_boot_device)
        else:
            # No DRAC boot device of the type requested by the argument
            # 'device' is present. This is normal for UEFI boot mode,
            # following deployment's writing of the operating system to
            # disk. It can also occur when a server has not been
            # powered on after a new boot device has been installed.
            #
            # If the boot order is flexibly programmable, use that to
            # attempt to detect and boot from a device of the requested
            # type during the next boot. That avoids the need for an
            # extra reboot. Otherwise, this function cannot satisfy the
            # request, because it was called with an invalid device.
            bios_settings = client.list_bios_settings(by_name=True)
            if _is_boot_order_flexibly_programmable(persistent, bios_settings):
                drac_boot_mode = bios_settings['BootMode'].current_value
                if drac_boot_mode not in _DRAC_BOOT_MODES:
                    message = _("DRAC reported unknown boot mode "
                                "'%(drac_boot_mode)s'") % {
                                    'drac_boot_mode': drac_boot_mode}
                    LOG.error('DRAC driver failed to change boot device order '
                              'for node %(node_uuid)s. Reason: %(message)s.',
                              {'node_uuid': node.uuid, 'message': message})
                    raise exception.DracOperationError(error=message)

                flexibly_program_settings = _flexibly_program_boot_order(
                    device, drac_boot_mode)
                client.set_bios_settings(flexibly_program_settings)
            else:
                raise exception.InvalidParameterValue(
                    _("set_boot_device called with invalid device "
                      "'%(device)s' for node %(node_id)s.") %
                    {'device': device, 'node_id': node.uuid})

        client.commit_pending_bios_changes()
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to change boot device order for '
                  'node %(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid, 'error': exc})
        raise exception.DracOperationError(error=exc)
Example #8
0
def set_boot_device(node, device, persistent=False):
    """Set the boot device for a node.

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

    :param node: an ironic node object.
    :param device: the boot device, one of
                   :mod:`ironic.common.boot_devices`.
    :param persistent: Boolean value. True if the boot device will
                       persist to all future boots, False if not.
                       Default: False.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    # If pending BIOS job or pending non-BIOS job found in job queue,
    # we need to clear that jobs before executing clear_job_queue or
    # known_good_state clean step of management interface.
    # Otherwise, pending BIOS config job can cause creating new config jobs
    # to fail and pending non-BIOS job can execute on reboot the node.
    validate_job_queue = True
    if node.driver_internal_info.get("clean_steps"):
        if node.driver_internal_info.get("clean_steps")[0].get(
                'step') in _CLEAR_JOBS_CLEAN_STEPS:
            unfinished_jobs = drac_job.list_unfinished_jobs(node)
            if unfinished_jobs:
                validate_job_queue = False
                client.delete_jobs(job_ids=[job.id for job in unfinished_jobs])

    if validate_job_queue:
        drac_job.validate_job_queue(node)

    try:
        drac_boot_devices = client.list_boot_devices()

        current_boot_device = _get_boot_device(node, drac_boot_devices)
        # If we are already booting from the right device, do nothing.
        if current_boot_device == {'boot_device': device,
                                   'persistent': persistent}:
            LOG.debug('DRAC already set to boot from %s', device)
            return

        persistent_boot_mode = _get_next_persistent_boot_mode(node)

        drac_boot_device = None
        for drac_device in drac_boot_devices[persistent_boot_mode]:
            for id_component in _BOOT_DEVICES_MAP[device]:
                if id_component in drac_device.id:
                    drac_boot_device = drac_device.id
                    break

            if drac_boot_device:
                break

        if drac_boot_device:
            if persistent:
                boot_list = persistent_boot_mode
            else:
                boot_list = _NON_PERSISTENT_BOOT_MODE

            client.change_boot_device_order(boot_list, drac_boot_device)
        else:
            # No DRAC boot device of the type requested by the argument
            # 'device' is present. This is normal for UEFI boot mode,
            # following deployment's writing of the operating system to
            # disk. It can also occur when a server has not been
            # powered on after a new boot device has been installed.
            #
            # If the boot order is flexibly programmable, use that to
            # attempt to detect and boot from a device of the requested
            # type during the next boot. That avoids the need for an
            # extra reboot. Otherwise, this function cannot satisfy the
            # request, because it was called with an invalid device.
            bios_settings = client.list_bios_settings(by_name=True)
            if _is_boot_order_flexibly_programmable(persistent, bios_settings):
                drac_boot_mode = bios_settings['BootMode'].current_value
                if drac_boot_mode not in _DRAC_BOOT_MODES:
                    message = _("DRAC reported unknown boot mode "
                                "'%(drac_boot_mode)s'") % {
                                    'drac_boot_mode': drac_boot_mode}
                    LOG.error('DRAC driver failed to change boot device order '
                              'for node %(node_uuid)s. Reason: %(message)s.',
                              {'node_uuid': node.uuid, 'message': message})
                    raise exception.DracOperationError(error=message)

                flexibly_program_settings = _flexibly_program_boot_order(
                    device, drac_boot_mode)
                client.set_bios_settings(flexibly_program_settings)
            else:
                raise exception.InvalidParameterValue(
                    _("set_boot_device called with invalid device "
                      "'%(device)s' for node %(node_id)s.") %
                    {'device': device, 'node_id': node.uuid})

        job_id = client.commit_pending_bios_changes()
        job_entry = client.get_job(job_id)

        timeout = CONF.drac.boot_device_job_status_timeout
        end_time = time.time() + timeout

        LOG.debug('Waiting for BIOS configuration job %(job_id)s '
                  'to be scheduled for node %(node)s',
                  {'job_id': job_id,
                   'node': node.uuid})

        while job_entry.status != "Scheduled":
            if time.time() >= end_time:
                raise exception.DracOperationError(
                    error=_(
                        'Timed out waiting BIOS configuration for job '
                        '%(job)s to reach Scheduled state.  Job is still '
                        'in %(status)s state.') %
                    {'job': job_id, 'status': job_entry.status})
            time.sleep(3)
            job_entry = client.get_job(job_id)

    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to change boot device order for '
                  'node %(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid, 'error': exc})
        raise exception.DracOperationError(error=exc)
Example #9
0
    def apply_configuration(self, task, settings):
        """Apply the BIOS configuration to the node

        :param task: a TaskManager instance containing the node to act on
        :param settings: List of BIOS settings to apply
        :raises: DRACOperationError upon an error from python-dracclient

        :returns: states.CLEANWAIT (cleaning) or states.DEPLOYWAIT (deployment)
                  if configuration is in progress asynchronously or None if it
                  is completed.
        """

        LOG.debug(
            "Configuring node %(node_uuid)s with BIOS settings:"
            " %(settings)s", {
                "node_uuid": task.node.uuid,
                "settings": settings
            })
        node = task.node
        # convert ironic settings list to DRAC kwsettings
        kwsettings = {s['name']: s['value'] for s in settings}
        drac_job.validate_job_queue(node)
        client = drac_common.get_drac_client(node)
        try:
            #  Argument validation is done by the dracclient method
            #  set_bios_settings. No need to do it here.
            set_result = client.set_bios_settings(kwsettings)
        except drac_exceptions.BaseClientException as exc:
            LOG.error(
                "Failed to apply BIOS config on node %(node_uuid)s."
                " Error %(error)s", {
                    "node_uuid": task.node.uuid,
                    "error": exc
                })
            raise exception.DracOperationError(error=exc)

        # If no commit is required, we're done
        if not set_result['is_commit_required']:
            LOG.info(
                "Completed BIOS configuration on node %(node_uuid)s"
                " with BIOS settings: %(settings)s", {
                    "node_uuid": task.node.uuid,
                    "settings": settings
                })
            return

        # Otherwise, need to reboot the node as well to commit configuration
        else:
            LOG.debug("Rebooting node %(node_uuid)s to apply BIOS settings",
                      {"node_uuid": task.node.uuid})
            reboot_needed = set_result['is_reboot_required']
            try:
                commit_result = client.commit_pending_bios_changes(
                    reboot=reboot_needed)
            except drac_exceptions.BaseClientException as exc:
                LOG.error(
                    "Failed to commit BIOS changes on node %(node_uuid)s"
                    ". Error %(error)s", {
                        "node_uuid": task.node.uuid,
                        "error": exc
                    })
                raise exception.DracOperationError(error=exc)

            # Store JobID for the async job handler _check_node_bios_jobs
            driver_internal_info = node.driver_internal_info
            driver_internal_info.setdefault('bios_config_job_ids',
                                            []).append(commit_result)
            node.driver_internal_info = driver_internal_info

            # This method calls node.save(), bios_config_job_ids will be saved
            # automatically
            # These flags are for the conductor to manage the asynchronous
            # jobs that have been initiated by this method
            deploy_utils.set_async_step_flags(node,
                                              reboot=reboot_needed,
                                              skip_current_step=True,
                                              polling=True)
            # Return the clean/deploy state string
            return deploy_utils.get_async_step_return_state(node)
Example #10
0
    def factory_reset(self, task):
        """Reset the BIOS settings of the node to the factory default.

        This uses the Lifecycle Controller configuration to perform
        BIOS configuration reset. Leveraging the python-dracclient
        methods already available.

        :param task: a TaskManager instance containing the node to act on
        :raises: DracOperationError on an error from python-dracclient
        :returns: states.CLEANWAIT (cleaning) or states.DEPLOYWAIT
                  (deployment) if reset is in progress asynchronously or None
                  if it is completed.
        """
        node = task.node
        drac_job.validate_job_queue(node)
        client = drac_common.get_drac_client(node)
        lc_bios_reset_attrib = {"BIOS Reset To Defaults Requested": "True"}
        try:
            set_result = client.set_lifecycle_settings(lc_bios_reset_attrib)
        except drac_exceptions.BaseClientException as exc:
            LOG.error(
                'Failed to reset BIOS on the node %(node_uuid)s.'
                ' Reason: %(error)s.', {
                    'node_uuid': node.uuid,
                    'error': exc
                })
            raise exception.DracOperationError(error=exc)
        if not set_result['is_commit_required']:
            LOG.info("BIOS reset successful on the node "
                     "%(node_uuid)s", {"node_uuid": node.uuid})
            return
        else:
            # Rebooting the Node is compulsory, LC call returns
            # reboot_required=False/Optional, which is not desired
            reboot_needed = True
            try:
                commit_job_id = client.commit_pending_lifecycle_changes(
                    reboot=reboot_needed)
            except drac_exceptions.BaseClientException as exc:
                LOG.error(
                    'Failed to commit BIOS reset on node '
                    '%(node_uuid)s. Reason: %(error)s.', {
                        'node_uuid': node.uuid,
                        'error': exc
                    })
                raise exception.DracOperationError(error=exc)

            # Store JobID for the async job handler _check_node_bios_jobs
            driver_internal_info = node.driver_internal_info
            driver_internal_info.setdefault('bios_config_job_ids',
                                            []).append(commit_job_id)
            node.driver_internal_info = driver_internal_info

            # This method calls node.save(), bios_config_job_id will be
            # saved automatically
            # These flags are for the conductor to manage the asynchronous
            # jobs that have been initiated by this method
            deploy_utils.set_async_step_flags(node,
                                              reboot=reboot_needed,
                                              skip_current_step=True,
                                              polling=True)

            return deploy_utils.get_async_step_return_state(task.node)