Exemple #1
0
    def test_get_drac_client(self, mock_dracclient):
        expected_call = mock.call('1.2.3.4', 'admin', 'fake', 443, '/wsman',
                                  'https')
        node = obj_utils.create_test_node(self.context,
                                          driver='fake_drac',
                                          driver_info=INFO_DICT)

        drac_common.get_drac_client(node)

        self.assertEqual(mock_dracclient.mock_calls, [expected_call])
Exemple #2
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 'is_commit_required' key with a
              boolean value indicating whether commit_config() needs to be
              called to make the changes, and the 'is_reboot_required' key
              which has a value of 'true' or 'false'.  This key is used to
              indicate to the commit_config() call if a reboot should be
              performed.
    """
    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)
Exemple #3
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)
Exemple #4
0
def commit_config(node, raid_controller, reboot=False, realtime=False):
    """Apply all pending changes on a RAID controller.

    :param node: an ironic node object.
    :param raid_controller: id of the RAID controller.
    :param reboot: indicates whether a reboot job should be automatically
                   created with the config job. (optional, defaults to False)
    :param realtime: indicates RAID controller supports realtime.
                     (optional, defaults to False)
    :returns: id of the created job
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        return client.commit_pending_raid_changes(
            raid_controller=raid_controller,
            reboot=reboot,
            realtime=realtime)
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to commit pending RAID config for'
                  ' controller %(raid_controller_fqdd)s on node '
                  '%(node_uuid)s. Reason: %(error)s.',
                  {'raid_controller_fqdd': raid_controller,
                   'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #5
0
def _is_raid_controller(node, raid_controller_fqdd, raid_controllers=None):
    """Find out if object's fqdd is for a raid controller or not

    :param node: an ironic node object
    :param raid_controller_fqdd: The object's fqdd we are testing to see
                                 if it is a raid controller or not.
    :param raid_controllers: A list of RAIDControllers used to check for
                             the presence of BOSS cards.  If None, the
                             iDRAC will be queried for the list of
                             controllers.
    :returns: boolean, True if the device is a RAID controller,
              False if not.
    """
    client = drac_common.get_drac_client(node)

    try:
        return client.is_raid_controller(raid_controller_fqdd,
                                         raid_controllers)
    except drac_exceptions.BaseClientException as exc:
        LOG.error('Unable to determine if controller %(raid_controller_fqdd)s '
                  'on node %(node_uuid)s is a RAID controller. '
                  'Reason: %(error)s. ',
                  {'raid_controller_fqdd': raid_controller_fqdd,
                   'node_uuid': node.uuid, 'error': exc})

        raise exception.DracOperationError(error=exc)
Exemple #6
0
def clear_foreign_config(node, raid_controller):
    """Free up the foreign drives.

    :param node: an ironic node object.
    :param raid_controller: id of the RAID controller.
    :returns: a dictionary containing
              - The is_commit_required needed key with a
              boolean value indicating whether a config job must be created
              for the values to be applied.
              - The is_reboot_required key with a RebootRequired enumerated
              value indicating whether the server must be rebooted to
              clear foreign configuration.
    :raises: DracOperationError on an error from python-dracclient.
    """
    try:

        drac_job.validate_job_queue(node)

        client = drac_common.get_drac_client(node)
        return client.clear_foreign_config(raid_controller)
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to free foreign driver '
                  'on %(raid_controller_fqdd)s '
                  'for node %(node_uuid)s. '
                  'Reason: %(error)s.',
                  {'raid_controller_fqdd': raid_controller,
                   'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #7
0
def validate_job_queue(node):
    """Validates the job queue on the node.

    It raises an exception if an unfinished configuration job exists.

    :param node: an ironic node object.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        unfinished_jobs = client.list_jobs(only_unfinished=True)
    except drac_exceptions.BaseClientException as exc:
        LOG.error(
            _LE('DRAC driver failed to get the list of unfinished jobs '
                'for node %(node_uuid)s. Reason: %(error)s.'), {
                    'node_uuid': node.uuid,
                    'error': exc
                })
        raise exception.DracOperationError(error=exc)

    if unfinished_jobs:
        msg = _('Unfinished config jobs found: %(jobs)r. Make sure they are '
                'completed before retrying.') % {
                    'jobs': unfinished_jobs
                }
        raise exception.DracOperationError(error=msg)
Exemple #8
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)
Exemple #9
0
def _get_next_persistent_boot_mode(node):
    client = drac_common.get_drac_client(node)

    try:
        boot_modes = client.list_boot_modes()
    except drac_exceptions.BaseClientException as exc:
        LOG.error(
            'DRAC driver failed to get next persistent boot mode for '
            'node %(node_uuid)s. Reason: %(error)s', {
                'node_uuid': node.uuid,
                'error': exc
            })
        raise exception.DracOperationError(error=exc)

    next_persistent_boot_mode = None
    for mode in boot_modes:
        if mode.is_next and mode.id != _NON_PERSISTENT_BOOT_MODE:
            next_persistent_boot_mode = mode.id
            break

    if not next_persistent_boot_mode:
        message = _('List of boot modes, %(list_boot_modes)s, does not '
                    'contain a persistent mode') % {
                        'list_boot_modes': boot_modes
                    }
        LOG.error(
            'DRAC driver failed to get next persistent boot mode for '
            'node %(node_uuid)s. Reason: %(message)s', {
                'node_uuid': node.uuid,
                'message': message
            })
        raise exception.DracOperationError(error=message)

    return next_persistent_boot_mode
Exemple #10
0
def _get_next_persistent_boot_mode(node):
    client = drac_common.get_drac_client(node)

    try:
        boot_modes = client.list_boot_modes()
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to get next persistent boot mode for '
                  'node %(node_uuid)s. Reason: %(error)s',
                  {'node_uuid': node.uuid, 'error': exc})
        raise exception.DracOperationError(error=exc)

    next_persistent_boot_mode = None
    for mode in boot_modes:
        if mode.is_next and mode.id != _NON_PERSISTENT_BOOT_MODE:
            next_persistent_boot_mode = mode.id
            break

    if not next_persistent_boot_mode:
        message = _('List of boot modes, %(list_boot_modes)s, does not '
                    'contain a persistent mode') % {
                        'list_boot_modes': boot_modes}
        LOG.error('DRAC driver failed to get next persistent boot mode for '
                  'node %(node_uuid)s. Reason: %(message)s',
                  {'node_uuid': node.uuid, 'message': message})
        raise exception.DracOperationError(error=message)

    return next_persistent_boot_mode
Exemple #11
0
def _set_power_state(node, power_state):
    """Turns the server power on/off or do a reboot.

    :param node: an ironic node object.
    :param power_state: a power state from :mod:`ironic.common.states`.
    :raises: InvalidParameterValue if required DRAC credentials are missing.
    :raises: DracOperationError on an error from python-dracclient
    """

    # NOTE(ifarkas): DRAC interface doesn't allow changing the boot device
    #                multiple times in a row without a reboot. This is
    #                because a change need to be committed via a
    #                configuration job, and further configuration jobs
    #                cannot be created until the previous one is processed
    #                at the next boot. As a workaround, it is saved to
    #                driver_internal_info during set_boot_device and committing
    #                it here.
    _commit_boot_list_change(node)

    client = drac_common.get_drac_client(node)
    target_power_state = REVERSE_POWER_STATES[power_state]

    try:
        client.set_power_state(target_power_state)
    except drac_exceptions.BaseClientException as exc:
        LOG.error(_LE('DRAC driver failed to set power state for node '
                      '%(node_uuid)s to %(power_state)s. '
                      'Reason: %(error)s.'),
                  {'node_uuid': node.uuid,
                   'power_state': power_state,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #12
0
def _get_boot_device(node, drac_boot_devices=None):
    client = drac_common.get_drac_client(node)

    try:
        boot_modes = client.list_boot_modes()
        next_boot_modes = [mode.id for mode in boot_modes if mode.is_next]
        if NON_PERSISTENT_BOOT_MODE in next_boot_modes:
            next_boot_mode = NON_PERSISTENT_BOOT_MODE
        else:
            next_boot_mode = next_boot_modes[0]

        if drac_boot_devices is None:
            drac_boot_devices = client.list_boot_devices()
        drac_boot_device = drac_boot_devices[next_boot_mode][0]

        boot_device = next(key for (key, value) in _BOOT_DEVICES_MAP.items()
                           if value in drac_boot_device.id)
        return {
            'boot_device': boot_device,
            'persistent': next_boot_mode == PERSISTENT_BOOT_MODE
        }
    except (drac_exceptions.BaseClientException, IndexError) as exc:
        LOG.error(
            'DRAC driver failed to get next boot mode for '
            'node %(node_uuid)s. Reason: %(error)s.', {
                'node_uuid': node.uuid,
                'error': exc
            })
        raise exception.DracOperationError(error=exc)
Exemple #13
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)
Exemple #14
0
def commit_config(node, raid_controller, reboot=False, realtime=False):
    """Apply all pending changes on a RAID controller.

    :param node: an ironic node object.
    :param raid_controller: id of the RAID controller.
    :param reboot: indicates whether a reboot job should be automatically
                   created with the config job. (optional, defaults to False)
    :param realtime: indicates RAID controller supports realtime.
                     (optional, defaults to False)
    :returns: id of the created job
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        return client.commit_pending_raid_changes(
            raid_controller=raid_controller, reboot=reboot, realtime=realtime)
    except drac_exceptions.BaseClientException as exc:
        LOG.error(
            'DRAC driver failed to commit pending RAID config for'
            ' controller %(raid_controller_fqdd)s on node '
            '%(node_uuid)s. Reason: %(error)s.', {
                'raid_controller_fqdd': raid_controller,
                'node_uuid': node.uuid,
                'error': exc
            })
        raise exception.DracOperationError(error=exc)
Exemple #15
0
def _set_power_state(node, power_state):
    """Turns the server power on/off or do a reboot.

    :param node: an ironic node object.
    :param power_state: a power state from :mod:`ironic.common.states`.
    :raises: InvalidParameterValue if required DRAC credentials are missing.
    :raises: DracOperationError on an error from python-dracclient
    """

    # NOTE(ifarkas): DRAC interface doesn't allow changing the boot device
    #                multiple times in a row without a reboot. This is
    #                because a change need to be committed via a
    #                configuration job, and further configuration jobs
    #                cannot be created until the previous one is processed
    #                at the next boot. As a workaround, it is saved to
    #                driver_internal_info during set_boot_device and committing
    #                it here.
    _commit_boot_list_change(node)

    client = drac_common.get_drac_client(node)
    target_power_state = REVERSE_POWER_STATES[power_state]

    try:
        client.set_power_state(target_power_state)
    except drac_exceptions.BaseClientException as exc:
        LOG.error(
            'DRAC driver failed to set power state for node '
            '%(node_uuid)s to %(power_state)s. '
            'Reason: %(error)s.', {
                'node_uuid': node.uuid,
                'power_state': power_state,
                'error': exc
            })
        raise exception.DracOperationError(error=exc)
Exemple #16
0
    def reset_idrac(self, task):
        """Reset the iDRAC.

        :param task: a TaskManager instance containing the node to act on.
        :returns: None if it is completed.
        :raises: DracOperationError on an error from python-dracclient.
        """
        node = task.node

        client = drac_common.get_drac_client(node)
        client.reset_idrac(force=True, wait=True)
Exemple #17
0
    def known_good_state(self, task):
        """Reset the iDRAC, Clear the job queue.

        :param task: a TaskManager instance containing the node to act on.
        :returns: None if it is completed.
        :raises: DracOperationError on an error from python-dracclient.
        """
        node = task.node

        client = drac_common.get_drac_client(node)
        client.reset_idrac(force=True, wait=True)
        client.delete_jobs(job_ids=[_CLEAR_JOB_IDS])
Exemple #18
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)
Exemple #19
0
def list_raid_controllers(node):
    """List the RAID controllers of the node.

    :param node: an ironic node object.
    :returns: a list of RAIDController objects from dracclient.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        return client.list_raid_controllers()
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to get the list of RAID controllers '
                  'for node %(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid, 'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #20
0
def list_raid_controllers(node):
    """List the RAID controllers of the node.

    :param node: an ironic node object.
    :returns: a list of RAIDController objects from dracclient.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        return client.list_raid_controllers()
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to get the list of RAID controllers '
                  'for node %(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid, 'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #21
0
    def clear_job_queue(self, task):
        """Clear the job queue.

        :param task: a TaskManager instance containing the node to act on.
        :returns: None if it is completed.
        :raises: DracOperationError on an error from python-dracclient.
        """
        try:
            node = task.node

            client = drac_common.get_drac_client(node)
            client.delete_jobs(job_ids=[_CLEAR_JOB_IDS])
        except drac_exceptions.BaseClientException as exc:
            LOG.error('DRAC driver failed to clear the job queue for node '
                      '%(node_uuid)s. Reason: %(error)s.',
                      {'node_uuid': node.uuid, 'error': exc})
            raise exception.DracOperationError(error=exc)
Exemple #22
0
def list_unfinished_jobs(node):
    """List unfinished config jobs of the node.

    :param node: an ironic node object.
    :returns: a list of Job objects from dracclient.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        return client.list_jobs(only_unfinished=True)
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to get the list of unfinished jobs '
                  'for node %(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #23
0
    def cache_bios_settings(self, task):
        """Store or update the current BIOS settings for the node.

        Get the current BIOS settings and store them in the bios_settings
        database table.

        :param task: a TaskManager instance containing the node to act on.
        :raises: DracOperationError on an error from python-dracclient
        """
        node = task.node
        node_id = node.id
        node_uuid = node.uuid

        client = drac_common.get_drac_client(node)

        try:
            kwsettings = client.list_bios_settings()
        except drac_exceptions.BaseClientException as exc:
            LOG.error(
                'DRAC driver failed to get the BIOS settings for node '
                '%(node_uuid)s. Reason: %(error)s.', {
                    'node_uuid': node.uuid,
                    'error': exc
                })
            raise exception.DracOperationError(error=exc)

        # convert dracclient BIOS settings into ironic settings list
        settings = [{
            "name": name,
            "value": attrib.current_value
        } for name, attrib in kwsettings.items()]

        # Store them in the database table
        LOG.debug('Caching BIOS settings for node %(node_uuid)s',
                  {'node_uuid': node_uuid})
        create_list, update_list, delete_list, nochange_list = (
            objects.BIOSSettingList.sync_node_setting(task.context, node_id,
                                                      settings))

        if create_list:
            objects.BIOSSettingList.create(task.context, node_id, create_list)
        if update_list:
            objects.BIOSSettingList.save(task.context, node_id, update_list)
        if delete_list:
            delete_names = [d['name'] for d in delete_list]
            objects.BIOSSettingList.delete(task.context, node_id, delete_names)
Exemple #24
0
def abandon_config(task):
    """Abandons uncommitted changes added by set_config

    :param task: a TaskManager instance containing the node to act on.
    :raises: DracOperationError on an error from python-dracclient.
    """
    node = task.node
    client = drac_common.get_drac_client(node)

    try:
        client.abandon_pending_bios_changes()
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to delete the pending BIOS '
                  'settings for node %(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #25
0
def get_job(node, job_id):
    """Get the details of a Lifecycle job of the node.

    :param node: an ironic node object.
    :param job_id: ID of the Lifecycle job.
    :returns: a Job object from dracclient.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        return client.get_job(job_id)
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to get the job %(job_id)s '
                  'for node %(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #26
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)
Exemple #27
0
def list_physical_disks(node):
    """List the physical disks of the node.

    :param node: an ironic node object.
    :returns: a list of PhysicalDisk objects from dracclient.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        return client.list_physical_disks()
    except drac_exceptions.BaseClientException as exc:
        LOG.error(
            _LE('DRAC driver failed to get the list of physical disks '
                'for node %(node_uuid)s. Reason: %(error)s.'), {
                    'node_uuid': node.uuid,
                    'error': exc
                })
        raise exception.DracOperationError(error=exc)
Exemple #28
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(
            _LE('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)
Exemple #29
0
def abandon_config(node, raid_controller):
    """Deletes all pending changes on a RAID controller.

    :param node: an ironic node object.
    :param raid_controller: id of the RAID controller.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        client.abandon_pending_raid_changes(raid_controller)
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to delete pending RAID config '
                  'for controller %(raid_controller_fqdd)s on node '
                  '%(node_uuid)s. Reason: %(error)s.',
                  {'raid_controller_fqdd': raid_controller,
                   'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #30
0
def abandon_config(node, raid_controller):
    """Deletes all pending changes on a RAID controller.

    :param node: an ironic node object.
    :param raid_controller: id of the RAID controller.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        client.abandon_pending_raid_changes(raid_controller)
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to delete pending RAID config '
                  'for controller %(raid_controller_fqdd)s on node '
                  '%(node_uuid)s. Reason: %(error)s.',
                  {'raid_controller_fqdd': raid_controller,
                   'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #31
0
def _get_power_state(node):
    """Returns the current power state of the node.

    :param node: an ironic node object.
    :returns: the power state, one of :mod:`ironic.common.states`.
    :raises: InvalidParameterValue if required DRAC credentials are missing.
    :raises: DracOperationError on an error from python-dracclient
    """

    client = drac_common.get_drac_client(node)

    try:
        drac_power_state = client.get_power_state()
    except drac_exceptions.BaseClientException as exc:
        LOG.error(_LE('DRAC driver failed to get power state for node '
                      '%(node_uuid)s. Reason: %(error)s.'),
                  {'node_uuid': node.uuid, 'error': exc})
        raise exception.DracOperationError(error=exc)

    return POWER_STATES[drac_power_state]
Exemple #32
0
def _get_power_state(node):
    """Returns the current power state of the node.

    :param node: an ironic node object.
    :returns: the power state, one of :mod:`ironic.common.states`.
    :raises: InvalidParameterValue if required DRAC credentials are missing.
    :raises: DracOperationError on an error from python-dracclient
    """

    client = drac_common.get_drac_client(node)

    try:
        drac_power_state = client.get_power_state()
    except drac_exceptions.BaseClientException as exc:
        LOG.error(_LE('DRAC driver failed to get power state for node '
                      '%(node_uuid)s. Reason: %(error)s.'),
                  {'node_uuid': node.uuid, 'error': exc})
        raise exception.DracOperationError(error=exc)

    return POWER_STATES[drac_power_state]
Exemple #33
0
def _get_boot_device(node, drac_boot_devices=None):
    client = drac_common.get_drac_client(node)

    try:
        boot_modes = client.list_boot_modes()
        next_boot_modes = [mode.id for mode in boot_modes if mode.is_next]
        if _NON_PERSISTENT_BOOT_MODE in next_boot_modes:
            next_boot_mode = _NON_PERSISTENT_BOOT_MODE
        else:
            next_boot_mode = next_boot_modes[0]

        if drac_boot_devices is None:
            drac_boot_devices = client.list_boot_devices()

        # It is possible for there to be no boot device.
        boot_device = None

        if next_boot_mode in drac_boot_devices:
            drac_boot_device = drac_boot_devices[next_boot_mode][0]

            for key, value in _BOOT_DEVICES_MAP.items():
                for id_component in value:
                    if id_component in drac_boot_device.id:
                        boot_device = key
                        break

                if boot_device:
                    break

        return {
            'boot_device': boot_device,
            'persistent': next_boot_mode != _NON_PERSISTENT_BOOT_MODE
        }
    except (drac_exceptions.BaseClientException, IndexError) as exc:
        LOG.error(
            'DRAC driver failed to get next boot mode for '
            'node %(node_uuid)s. Reason: %(error)s.', {
                'node_uuid': node.uuid,
                'error': exc
            })
        raise exception.DracOperationError(error=exc)
Exemple #34
0
    def delete_configuration(self, task):
        """Delete the RAID configuration.

        :param task: a TaskManager instance containing the node to act on.
        :returns: states.CLEANWAIT if deletion is in progress asynchronously
                  or None if it is completed.
        :raises: DracOperationError on an error from python-dracclient.
        """
        node = task.node

        # The node is rebooting at this point, so wait for the iDRAC to
        # enter the ready state before proceeding with cleaning
        client = drac_common.get_drac_client(node)
        client.wait_until_idrac_is_ready()

        controllers = set()
        for disk in list_virtual_disks(node):
            controllers.add(disk.controller)
            delete_virtual_disk(node, disk.id)

        return _commit_to_controllers(node, list(controllers))
Exemple #35
0
    def delete_configuration(self, task):
        """Delete the RAID configuration.

        :param task: a TaskManager instance containing the node to act on.
        :returns: states.CLEANWAIT if deletion is in progress asynchronously
                  or None if it is completed.
        :raises: DracOperationError on an error from python-dracclient.
        """
        node = task.node

        # The node is rebooting at this point, so wait for the iDRAC to
        # enter the ready state before proceeding with cleaning
        client = drac_common.get_drac_client(node)
        client.wait_until_idrac_is_ready()

        controllers = set()
        for disk in list_virtual_disks(node):
            controllers.add(disk.controller)
            delete_virtual_disk(node, disk.id)

        return _commit_to_controllers(node, list(controllers))
Exemple #36
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)
Exemple #37
0
def _get_boot_device(node, drac_boot_devices=None):
    client = drac_common.get_drac_client(node)

    try:
        boot_modes = client.list_boot_modes()
        next_boot_modes = [mode.id for mode in boot_modes if mode.is_next]
        if _NON_PERSISTENT_BOOT_MODE in next_boot_modes:
            next_boot_mode = _NON_PERSISTENT_BOOT_MODE
        else:
            next_boot_mode = next_boot_modes[0]

        if drac_boot_devices is None:
            drac_boot_devices = client.list_boot_devices()

        # It is possible for there to be no boot device.
        boot_device = None

        if next_boot_mode in drac_boot_devices:
            drac_boot_device = drac_boot_devices[next_boot_mode][0]

            for key, value in _BOOT_DEVICES_MAP.items():
                for id_component in value:
                    if id_component in drac_boot_device.id:
                        boot_device = key
                        break

                if boot_device:
                    break

        return {'boot_device': boot_device,
                'persistent': next_boot_mode != _NON_PERSISTENT_BOOT_MODE}
    except (drac_exceptions.BaseClientException, IndexError) as exc:
        LOG.error('DRAC driver failed to get next boot mode for '
                  'node %(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid, 'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #38
0
def validate_job_queue(node):
    """Validates the job queue on the node.

    It raises an exception if an unfinished configuration job exists.

    :param node: an ironic node object.
    :raises: DracOperationError on an error from python-dracclient.
    """
    client = drac_common.get_drac_client(node)

    try:
        unfinished_jobs = client.list_jobs(only_unfinished=True)
    except drac_exceptions.BaseClientException as exc:
        LOG.error(
            _LE("DRAC driver failed to get the list of unfinished jobs " "for node %(node_uuid)s. Reason: %(error)s."),
            {"node_uuid": node.uuid, "error": exc},
        )
        raise exception.DracOperationError(error=exc)

    if unfinished_jobs:
        msg = _("Unfinished config jobs found: %(jobs)r. Make sure they are " "completed before retrying.") % {
            "jobs": unfinished_jobs
        }
        raise exception.DracOperationError(error=msg)
Exemple #39
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)
Exemple #40
0
def _get_boot_device(node, drac_boot_devices=None):
    client = drac_common.get_drac_client(node)

    try:
        boot_modes = client.list_boot_modes()
        next_boot_modes = [mode.id for mode in boot_modes if mode.is_next]
        if NON_PERSISTENT_BOOT_MODE in next_boot_modes:
            next_boot_mode = NON_PERSISTENT_BOOT_MODE
        else:
            next_boot_mode = next_boot_modes[0]

        if drac_boot_devices is None:
            drac_boot_devices = client.list_boot_devices()
        drac_boot_device = drac_boot_devices[next_boot_mode][0]

        boot_device = next(key for (key, value) in _BOOT_DEVICES_MAP.items()
                           if value in drac_boot_device.id)
        return {'boot_device': boot_device,
                'persistent': next_boot_mode == PERSISTENT_BOOT_MODE}
    except (drac_exceptions.BaseClientException, IndexError) as exc:
        LOG.error('DRAC driver failed to get next boot mode for '
                  'node %(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid, 'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #41
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:
                factory_reset_time_before_reboot =\
                    client.get_system().last_system_inventory_time

                LOG.debug(
                    "Factory resetting node %(node_uuid)s "
                    "last inventory reboot time before factory reset "
                    "%(factory_reset_time_before_reboot)s", {
                        "node_uuid":
                        task.node.uuid,
                        "factory_reset_time_before_reboot":
                        factory_reset_time_before_reboot
                    })

                commit_job_id = client.commit_pending_lifecycle_changes(
                    reboot=reboot_needed)
                LOG.info(
                    "Commit job id of a node %(node_uuid)s."
                    "%(commit_job_id)s", {
                        'node_uuid': node.uuid,
                        "commit_job_id": commit_job_id
                    })
            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 the last inventory time on reboot for async job handler
            # _check_last_system_inventory_changed
            driver_internal_info = node.driver_internal_info
            driver_internal_info['factory_reset_time_before_reboot'] = \
                factory_reset_time_before_reboot
            # Store the current time to later check if factory reset times out
            driver_internal_info['factory_reset_time'] = str(
                timeutils.utcnow(with_timezone=True))

            node.driver_internal_info = driver_internal_info
            # rebooting the server to apply factory reset value
            client.set_power_state('REBOOT')

            # 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)
Exemple #42
0
def get_config(node):
    """Get the BIOS configuration.

    The BIOS settings look like::

        {'EnumAttrib': {'name': 'EnumAttrib',
                        'current_value': 'Value',
                        'pending_value': 'New Value', # could also be None
                        'read_only': False,
                        'possible_values': ['Value', 'New Value', 'None']},
         'StringAttrib': {'name': 'StringAttrib',
                          'current_value': 'Information',
                          'pending_value': None,
                          'read_only': False,
                          'min_length': 0,
                          'max_length': 255,
                          'pcre_regex': '^[0-9A-Za-z]{0,255}$'},
         'IntegerAttrib': {'name': 'IntegerAttrib',
                           'current_value': 0,
                           'pending_value': None,
                           'read_only': True,
                           'lower_bound': 0,
                           'upper_bound': 65535}}

    :param node: an ironic node object.
    :raises: DracOperationError on an error from python-dracclient.
    :returns: a dictionary containing BIOS settings

    The above values are only examples, of course.  BIOS attributes exposed via
    this API will always be either an enumerated attribute, a string attribute,
    or an integer attribute.  All attributes have the following parameters:

    :param name: is the name of the BIOS attribute.
    :param current_value: is the current value of the attribute.
                          It will always be either an integer or a string.
    :param pending_value: is the new value that we want the attribute to have.
                          None means that there is no pending value.
    :param read_only: indicates whether this attribute can be changed.
                      Trying to change a read-only value will result in
                      an error. The read-only flag can change depending
                      on other attributes.
                      A future version of this call may expose the
                      dependencies that indicate when that may happen.

    Enumerable attributes also have the following parameters:

    :param possible_values: is an array of values it is permissible to set
                            the attribute to.

    String attributes also have the following parameters:

    :param min_length: is the minimum length of the string.
    :param max_length: is the maximum length of the string.
    :param pcre_regex: is a PCRE compatible regular expression that the string
                       must match.  It may be None if the string is read only
                       or if the string does not have to match any particular
                       regular expression.

    Integer attributes also have the following parameters:

    :param lower_bound: is the minimum value the attribute can have.
    :param upper_bound: is the maximum value the attribute can have.
    """

    client = drac_common.get_drac_client(node)

    try:
        return client.list_bios_settings()
    except drac_exceptions.BaseClientException as exc:
        LOG.error('DRAC driver failed to get the BIOS settings for node '
                  '%(node_uuid)s. Reason: %(error)s.',
                  {'node_uuid': node.uuid,
                   'error': exc})
        raise exception.DracOperationError(error=exc)
Exemple #43
0
    def inspect_hardware(self, task):
        """Inspect hardware.

        Inspect hardware to obtain the essential & additional hardware
        properties.

        :param task: a TaskManager instance containing the node to act on.
        :raises: HardwareInspectionFailure, if unable to get essential
                 hardware properties.
        :returns: states.MANAGEABLE
        """

        node = task.node
        client = drac_common.get_drac_client(node)
        properties = {}

        try:
            properties['memory_mb'] = sum(
                [memory.size_mb for memory in client.list_memory()])
            cpus = client.list_cpus()
            if cpus:
                properties['cpus'] = sum(
                    [self._calculate_cpus(cpu) for cpu in cpus])
                properties['cpu_arch'] = 'x86_64' if cpus[0].arch64 else 'x86'

            bios_settings = client.list_bios_settings()
            current_capabilities = node.properties.get('capabilities', '')
            new_capabilities = {
                'boot_mode': bios_settings["BootMode"].current_value.lower()}
            capabilties = utils.get_updated_capabilities(current_capabilities,
                                                         new_capabilities)
            properties['capabilities'] = capabilties

            virtual_disks = client.list_virtual_disks()
            root_disk = self._guess_root_disk(virtual_disks)
            if root_disk:
                properties['local_gb'] = int(root_disk.size_mb / units.Ki)
            else:
                physical_disks = client.list_physical_disks()
                root_disk = self._guess_root_disk(physical_disks)
                if root_disk:
                    properties['local_gb'] = int(
                        root_disk.size_mb / units.Ki)
        except drac_exceptions.BaseClientException as exc:
            LOG.error('DRAC driver failed to introspect node '
                      '%(node_uuid)s. Reason: %(error)s.',
                      {'node_uuid': node.uuid, 'error': exc})
            raise exception.HardwareInspectionFailure(error=exc)

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

        node.properties = dict(node.properties, **properties)
        node.save()

        try:
            nics = client.list_nics()
        except drac_exceptions.BaseClientException as exc:
            LOG.error('DRAC driver failed to introspect node '
                      '%(node_uuid)s. Reason: %(error)s.',
                      {'node_uuid': node.uuid, 'error': exc})
            raise exception.HardwareInspectionFailure(error=exc)

        pxe_dev_nics = self._get_pxe_dev_nics(client, nics, node)
        if pxe_dev_nics is None:
            LOG.warning('No PXE enabled NIC was found for node '
                        '%(node_uuid)s.', {'node_uuid': node.uuid})

        for nic in nics:
            try:
                port = objects.Port(task.context, address=nic.mac,
                                    node_id=node.id,
                                    pxe_enabled=(nic.id in pxe_dev_nics))
                port.create()

                LOG.info('Port created with MAC address %(mac)s '
                         'for node %(node_uuid)s during inspection',
                         {'mac': nic.mac, 'node_uuid': node.uuid})
            except exception.MACAlreadyExists:
                LOG.warning('Failed to create a port with MAC address '
                            '%(mac)s when inspecting the node '
                            '%(node_uuid)s because the address is already '
                            'registered',
                            {'mac': nic.mac, 'node_uuid': node.uuid})

        LOG.info('Node %s successfully inspected.', node.uuid)
        return states.MANAGEABLE
Exemple #44
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)
Exemple #45
0
    def create_configuration(self, task,
                             create_root_volume=True,
                             create_nonroot_volumes=True):
        """Create the RAID configuration.

        This method creates the RAID configuration on the given node.

        :param task: a TaskManager instance containing the node to act on.
        :param create_root_volume: If True, a root volume is created
            during RAID configuration. Otherwise, no root volume is
            created. Default is True.
        :param create_nonroot_volumes: If True, non-root volumes are
            created. If False, no non-root volumes are created. Default
            is True.
        :returns: states.CLEANWAIT if creation is in progress asynchronously
                  or None if it is completed.
        :raises: MissingParameterValue, if node.target_raid_config is missing
            or empty.
        :raises: DracOperationError on an error from python-dracclient.
        """
        node = task.node

        # The node is rebooting at this point, so wait for the iDRAC to
        # enter the ready state before proceeding with cleaning
        client = drac_common.get_drac_client(node)
        client.wait_until_idrac_is_ready()

        logical_disks = node.target_raid_config['logical_disks']
        for disk in logical_disks:
            if (disk['size_gb'] == 'MAX' and 'physical_disks' not in disk):
                raise exception.InvalidParameterValue(
                    _("create_configuration called with invalid "
                      "target_raid_configuration for node %(node_id)s. "
                      "'physical_disks' is missing from logical_disk while "
                      "'size_gb'='MAX' was requested: "
                      "%(logical_disk)s") % {'node_id': node.uuid,
                                             'logical_disk': disk})

            if disk['size_gb'] == 'MAX':
                disk['size_mb'] = 'MAX'
            else:
                disk['size_mb'] = disk['size_gb'] * units.Ki

            del disk['size_gb']

        physical_disks = list_physical_disks(node)
        logical_disks = _find_configuration(logical_disks, physical_disks)

        logical_disks_to_create = _filter_logical_disks(
            logical_disks, create_root_volume, create_nonroot_volumes)

        controllers = set()
        for logical_disk in logical_disks_to_create:
            controllers.add(logical_disk['controller'])
            create_virtual_disk(
                node,
                raid_controller=logical_disk['controller'],
                physical_disks=logical_disk['physical_disks'],
                raid_level=logical_disk['raid_level'],
                size_mb=logical_disk['size_mb'],
                disk_name=logical_disk.get('name'),
                span_length=logical_disk.get('span_length'),
                span_depth=logical_disk.get('span_depth'))

        return _commit_to_controllers(node, list(controllers))
Exemple #46
0
    def create_configuration(self,
                             task,
                             create_root_volume=True,
                             create_nonroot_volumes=True):
        """Create the RAID configuration.

        This method creates the RAID configuration on the given node.

        :param task: a TaskManager instance containing the node to act on.
        :param create_root_volume: If True, a root volume is created
            during RAID configuration. Otherwise, no root volume is
            created. Default is True.
        :param create_nonroot_volumes: If True, non-root volumes are
            created. If False, no non-root volumes are created. Default
            is True.
        :returns: states.CLEANWAIT if creation is in progress asynchronously
                  or None if it is completed.
        :raises: MissingParameterValue, if node.target_raid_config is missing
            or empty.
        :raises: DracOperationError on an error from python-dracclient.
        """
        node = task.node

        # The node is rebooting at this point, so wait for the iDRAC to
        # enter the ready state before proceeding with cleaning
        client = drac_common.get_drac_client(node)
        client.wait_until_idrac_is_ready()

        logical_disks = node.target_raid_config['logical_disks']
        for disk in logical_disks:
            if (disk['size_gb'] == 'MAX' and 'physical_disks' not in disk):
                raise exception.InvalidParameterValue(
                    _("create_configuration called with invalid "
                      "target_raid_configuration for node %(node_id)s. "
                      "'physical_disks' is missing from logical_disk while "
                      "'size_gb'='MAX' was requested: "
                      "%(logical_disk)s") % {
                          'node_id': node.uuid,
                          'logical_disk': disk
                      })

            if disk['size_gb'] == 'MAX':
                disk['size_mb'] = 'MAX'
            else:
                disk['size_mb'] = disk['size_gb'] * units.Ki

            del disk['size_gb']

        physical_disks = list_physical_disks(node)
        logical_disks = _find_configuration(logical_disks, physical_disks)

        logical_disks_to_create = _filter_logical_disks(
            logical_disks, create_root_volume, create_nonroot_volumes)

        controllers = set()
        for logical_disk in logical_disks_to_create:
            controllers.add(logical_disk['controller'])
            create_virtual_disk(node,
                                raid_controller=logical_disk['controller'],
                                physical_disks=logical_disk['physical_disks'],
                                raid_level=logical_disk['raid_level'],
                                size_mb=logical_disk['size_mb'],
                                disk_name=logical_disk.get('name'),
                                span_length=logical_disk.get('span_length'),
                                span_depth=logical_disk.get('span_depth'))

        return _commit_to_controllers(node, list(controllers))
Exemple #47
0
def get_config(node):
    """Get the BIOS configuration.

    :param node: an ironic node object.
    :raises: DracOperationError on an error from python-dracclient.
    :returns: a dictionary containing BIOS settings in the form of:
        {'EnumAttrib': {'name': 'EnumAttrib',
                        'current_value': 'Value',
                        'pending_value': 'New Value', # could also be None
                        'read_only': False,
                        'possible_values': ['Value', 'New Value', 'None']},
         'StringAttrib': {'name': 'StringAttrib',
                          'current_value': 'Information',
                          'pending_value': None,
                          'read_only': False,
                          'min_length': 0,
                          'max_length': 255,
                          'pcre_regex': '^[0-9A-Za-z]{0,255}$'},
         'IntegerAttrib': {'name': 'IntegerAttrib',
                           'current_value': 0,
                           'pending_value': None,
                           'read_only': True,
                           'lower_bound': 0,
                           'upper_bound': 65535}
        }

    The above values are only examples, of course.  BIOS attributes exposed via
    this API will always be either an enumerated attribute, a string attribute,
    or an integer attribute.  All attributes have the following parameters:
    :name: is the name of the BIOS attribute.
    :current_value: is the current value of the attribute.
                    It will always be either an integer or a string.
    :pending_value: is the new value that we want the attribute to have.
                    None means that there is no pending value.
    :read_only: indicates whether this attribute can be changed.  Trying to
                change a read-only value will result in an error.
                The read-only flag can change depending on other attributes.
                A future version of this call may expose the dependencies
                that indicate when that may happen.

    Enumerable attributes also have the following parameters:
    :possible_values: is an array of values it is permissible to set
                      the attribute to.

    String attributes also have the following parameters:
    :min_length: is the minimum length of the string.
    :max_length: is the maximum length of the string.
    :pcre_regex: is a PCRE compatible regular expression that the string
                 must match.  It may be None if the string is read only
                 or if the string does not have to match any particular
                 regular expression.

    Integer attributes also have the following parameters:
    :lower_bound: is the minimum value the attribute can have.
    :upper_bound: is the maximum value the attribute can have.
    """

    client = drac_common.get_drac_client(node)

    try:
        return client.list_bios_settings()
    except drac_exceptions.BaseClientException as exc:
        LOG.error(
            _LE('DRAC driver failed to get the BIOS settings for node '
                '%(node_uuid)s. Reason: %(error)s.'), {
                    'node_uuid': node.uuid,
                    'error': exc
                })
        raise exception.DracOperationError(error=exc)
Exemple #48
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)
Exemple #49
0
    def _check_last_system_inventory_changed(self, task):
        """Check the progress of last system inventory time of a node.

        This handles jobs for BIOS factory reset. Handle means,
        it checks for job status to not only signify completed jobs but
        also handle failures by invoking the 'fail' event, allowing the
        conductor to put the node into clean/deploy FAIL state.

        :param task: a TaskManager instance with the node to act on
        """
        node = task.node
        client = drac_common.get_drac_client(node)
        # Get the last system inventory time from node before reboot
        factory_reset_time_before_reboot = node.driver_internal_info.get(
            'factory_reset_time_before_reboot')

        # Get the factory reset start time
        factory_reset_time = node.driver_internal_info.get(
            'factory_reset_time')
        LOG.debug(
            "Factory resetting node %(node_uuid)s factory reset time "
            " %(factory_reset_time)s", {
                "node_uuid": task.node.uuid,
                "factory_reset_time": factory_reset_time
            })
        # local variable to track difference between current time and factory
        # reset start time
        time_difference = 0
        # Get the last system inventory time after reboot
        factory_reset_time_endof_reboot = (
            client.get_system().last_system_inventory_time)

        LOG.debug(
            "Factory resetting node %(node_uuid)s "
            "last inventory reboot time after factory reset "
            "%(factory_reset_time_endof_reboot)s", {
                "node_uuid": task.node.uuid,
                "factory_reset_time_endof_reboot":
                factory_reset_time_endof_reboot
            })

        if factory_reset_time_before_reboot != factory_reset_time_endof_reboot:
            # from the database cleanup with factory reset time
            self._delete_cached_reboot_time(node)
            # Cache the new BIOS settings,
            self.cache_bios_settings(task)
            self._resume_current_operation(task)
        else:
            # Calculate difference between current time and factory reset
            # start time if it is more than configured timeout then set
            # the node to fail state
            time = timeutils.utcnow(
                with_timezone=True) - timeutils.parse_isotime(
                    str(factory_reset_time))
            time_difference = time.total_seconds()
            LOG.debug(
                "Factory resetting node %(node_uuid)s "
                "time difference %(time_difference)s ", {
                    "node_uuid": task.node.uuid,
                    "time_difference": time_difference
                })

            if time_difference > CONF.drac.bios_factory_reset_timeout:
                task.upgrade_lock()
                self._delete_cached_reboot_time(node)
                error_message = ("BIOS factory reset was not completed within "
                                 "{} seconds, unable to cache updated bios "
                                 "setting").format(
                                     CONF.drac.bios_factory_reset_timeout)
                self._set_failed(task, error_message)
            else:
                LOG.debug(
                    "Factory reset for a node %(node)s is not done "
                    "will check again later", {'node': task.node.uuid})
Exemple #50
0
    def inspect_hardware(self, task):
        """Inspect hardware.

        Inspect hardware to obtain the essential & additional hardware
        properties.

        :param task: a TaskManager instance containing the node to act on.
        :raises: HardwareInspectionFailure, if unable to get essential
                 hardware properties.
        :returns: states.MANAGEABLE
        """

        node = task.node
        client = drac_common.get_drac_client(node)
        properties = {}

        try:
            properties['memory_mb'] = sum(
                [memory.size_mb for memory in client.list_memory()])
            cpus = client.list_cpus()
            if cpus:
                properties['cpus'] = len(cpus)
                properties['cpu_arch'] = 'x86_64' if cpus[0].arch64 else 'x86'

            virtual_disks = client.list_virtual_disks()
            root_disk = self._guess_root_disk(virtual_disks)
            if root_disk:
                properties['local_gb'] = int(root_disk.size_mb / units.Ki)
            else:
                physical_disks = client.list_physical_disks()
                root_disk = self._guess_root_disk(physical_disks)
                if root_disk:
                    properties['local_gb'] = int(
                        root_disk.size_mb / units.Ki)
        except drac_exceptions.BaseClientException as exc:
            LOG.error(_LE('DRAC driver failed to introspect node '
                          '%(node_uuid)s. Reason: %(error)s.'),
                      {'node_uuid': node.uuid, 'error': exc})
            raise exception.HardwareInspectionFailure(error=exc)

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

        node.properties = dict(node.properties, **properties)
        node.save()

        try:
            nics = client.list_nics()
        except drac_exceptions.BaseClientException as exc:
            LOG.error(_LE('DRAC driver failed to introspect node '
                          '%(node_uuid)s. Reason: %(error)s.'),
                      {'node_uuid': node.uuid, 'error': exc})
            raise exception.HardwareInspectionFailure(error=exc)

        for nic in nics:
            try:
                port = objects.Port(task.context, address=nic.mac,
                                    node_id=node.id)
                port.create()
                LOG.info(_LI('Port created with MAC address %(mac)s '
                             'for node %(node_uuid)s during inspection'),
                         {'mac': nic.mac, 'node_uuid': node.uuid})
            except exception.MACAlreadyExists:
                LOG.warning(_LW('Failed to create a port with MAC address '
                                '%(mac)s when inspecting the node '
                                '%(node_uuid)s because the address is already '
                                'registered'),
                            {'mac': nic.mac, 'node_uuid': node.uuid})

        LOG.info(_LI('Node %s successfully inspected.'), node.uuid)
        return states.MANAGEABLE