Source code for ironic.drivers.modules.drac.management

# -*- coding: utf-8 -*-
#
# Copyright 2014 Red Hat, Inc.
# All Rights Reserved.
# Copyright (c) 2017-2021 Dell Inc. or its subsidiaries.
#
#    Licensed under the Apache License, Version 2.0 (the "License"); you may
#    not use this file except in compliance with the License. You may obtain
#    a copy of the License at
#
#         http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#    License for the specific language governing permissions and limitations
#    under the License.

"""
DRAC management interface
"""

from oslo_log import log as logging

from ironic.common import boot_devices
from ironic.common import exception
from ironic.common import metrics_utils
from ironic.common import states
from ironic.drivers import base
from ironic.drivers.modules.drac import utils as drac_utils
from ironic.drivers.modules.redfish import management as redfish_management
from ironic.drivers.modules.redfish import utils as redfish_utils


LOG = logging.getLogger(__name__)

METRICS = metrics_utils.get_metrics_logger(__name__)

# This dictionary is used to map boot device names between two (2) name
# spaces. The name spaces are:
#
#     1) ironic boot devices
#     2) iDRAC boot sources
#
# Mapping can be performed in both directions.
#
# The keys are ironic boot device types. Each value is a list of strings
# that appear in the identifiers of iDRAC boot sources.
#
# The iDRAC represents boot sources with class DCIM_BootSourceSetting
# [1]. Each instance of that class contains a unique identifier, which
# is called an instance identifier, InstanceID,
#
# An InstanceID contains the Fully Qualified Device Descriptor (FQDD) of
# the physical device that hosts the boot source [2].
#
# [1] "Dell EMC BIOS and Boot Management Profile", Version 4.0.0, July
#     10, 2017, Section 7.2 "Boot Management", pp. 44-47 --
#     http://en.community.dell.com/techcenter/extras/m/white_papers/20444495/download
# [2] "Lifecycle Controller Version 3.15.15.15 User's Guide", Dell EMC,
#     2017, Table 13, "Easy-to-use Names of System Components", pp. 71-74 --
#     http://topics-cdn.dell.com/pdf/idrac9-lifecycle-controller-v3.15.15.15_users-guide2_en-us.pdf
_BOOT_DEVICES_MAP = {
    boot_devices.DISK: ['AHCI', 'Disk', 'RAID'],
    boot_devices.PXE: ['NIC'],
    boot_devices.CDROM: ['Optical'],
}

_DRAC_BOOT_MODES = ['Bios', 'Uefi']

# BootMode constant
_NON_PERSISTENT_BOOT_MODE = 'OneTime'

# Clear job id's constant
_CLEAR_JOB_IDS = 'JID_CLEARALL'

# Clean steps constant
_CLEAR_JOBS_CLEAN_STEPS = ['clear_job_queue', 'known_good_state']


def _is_boot_order_flexibly_programmable(persistent, bios_settings):
    return persistent and 'SetBootOrderFqdd1' in bios_settings


def _flexibly_program_boot_order(device, drac_boot_mode):
    if device == boot_devices.DISK:
        if drac_boot_mode == 'Bios':
            bios_settings = {'SetBootOrderFqdd1': 'HardDisk.List.1-1'}
        else:
            # 'Uefi'
            bios_settings = {
                'SetBootOrderFqdd1': '*.*.*',  # Disks, which are all else
                'SetBootOrderFqdd2': 'NIC.*.*',
                'SetBootOrderFqdd3': 'Optical.*.*',
                'SetBootOrderFqdd4': 'Floppy.*.*',
            }
    elif device == boot_devices.PXE:
        bios_settings = {'SetBootOrderFqdd1': 'NIC.*.*'}
    else:
        # boot_devices.CDROM
        bios_settings = {'SetBootOrderFqdd1': 'Optical.*.*'}

    return bios_settings


[docs] class DracRedfishManagement(redfish_management.RedfishManagement): """iDRAC Redfish interface for management-related actions."""
[docs] @METRICS.timer('DracRedfishManagement.clear_job_queue') @base.verify_step(priority=0) @base.clean_step(priority=0, requires_ramdisk=False) def clear_job_queue(self, task): """Clear iDRAC job queue. :param task: a TaskManager instance containing the node to act on. :raises: RedfishError on an error. """ try: drac_utils.execute_oem_manager_method( task, 'clear job queue', lambda m: m.job_service.delete_jobs(job_ids=['JID_CLEARALL'])) except exception.RedfishError as exc: if "Oem/Dell/DellJobService is missing" in str(exc): LOG.warning('iDRAC on node %(node)s does not support ' 'clearing Lifecycle Controller job queue ' 'using the idrac-redfish driver. ' 'If using iDRAC9, consider upgrading firmware.', {'node': task.node.uuid}) if task.node.provision_state != states.VERIFYING: raise
[docs] @METRICS.timer('DracRedfishManagement.reset_idrac') @base.verify_step(priority=0) @base.clean_step(priority=0, requires_ramdisk=False) def reset_idrac(self, task): """Reset the iDRAC. :param task: a TaskManager instance containing the node to act on. :raises: RedfishError on an error. """ try: drac_utils.execute_oem_manager_method( task, 'reset iDRAC', lambda m: m.reset_idrac()) redfish_utils.wait_until_get_system_ready(task.node) LOG.info('Reset iDRAC for node %(node)s done', {'node': task.node.uuid}) except exception.RedfishError as exc: if "Oem/Dell/DelliDRACCardService is missing" in str(exc): LOG.warning('iDRAC on node %(node)s does not support ' 'iDRAC reset using the idrac-redfish driver. ' 'If using iDRAC9, consider upgrading firmware. ', {'node': task.node.uuid}) if task.node.provision_state != states.VERIFYING: raise
[docs] @METRICS.timer('DracRedfishManagement.known_good_state') @base.verify_step(priority=0) @base.clean_step(priority=0, requires_ramdisk=False) def known_good_state(self, task): """Reset iDRAC to known good state. An iDRAC is reset to a known good state by resetting it and clearing its job queue. :param task: a TaskManager instance containing the node to act on. :raises: RedfishError on an error. """ self.reset_idrac(task) self.clear_job_queue(task) LOG.info('Reset iDRAC to known good state for node %(node)s', {'node': task.node.uuid})