diff options
Diffstat (limited to 'ironic/drivers/modules/drac/management.py')
-rw-r--r-- | ironic/drivers/modules/drac/management.py | 172 |
1 files changed, 153 insertions, 19 deletions
diff --git a/ironic/drivers/modules/drac/management.py b/ironic/drivers/modules/drac/management.py index 7439c2fda..b0c621a57 100644 --- a/ironic/drivers/modules/drac/management.py +++ b/ironic/drivers/modules/drac/management.py @@ -2,6 +2,7 @@ # # Copyright 2014 Red Hat, Inc. # All Rights Reserved. +# Copyright (c) 2017-2018 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 @@ -37,15 +38,40 @@ 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: 'HardDisk', - boot_devices.PXE: 'NIC', - boot_devices.CDROM: 'Optical', + boot_devices.DISK: ['AHCI', 'Disk', 'RAID'], + boot_devices.PXE: ['NIC'], + boot_devices.CDROM: ['Optical'], } -# BootMode constants -PERSISTENT_BOOT_MODE = 'IPL' -NON_PERSISTENT_BOOT_MODE = 'OneTime' +_DRAC_BOOT_MODES = ['Bios', 'Uefi'] + +# BootMode constant +_NON_PERSISTENT_BOOT_MODE = 'OneTime' def _get_boot_device(node, drac_boot_devices=None): @@ -54,19 +80,31 @@ def _get_boot_device(node, drac_boot_devices=None): 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 + 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) + # 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 == PERSISTENT_BOOT_MODE} + 'persistent': next_boot_mode != _NON_PERSISTENT_BOOT_MODE} except (drac_exceptions.BaseClientException, IndexError) as exc: LOG.error(_LE('DRAC driver failed to get next boot mode for ' 'node %(node_uuid)s. Reason: %(error)s.'), @@ -74,6 +112,60 @@ def _get_boot_device(node, drac_boot_devices=None): raise exception.DracOperationError(error=exc) +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 + + +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 + + def set_boot_device(node, device, persistent=False): """Set the boot device for a node. @@ -102,16 +194,58 @@ def set_boot_device(node, device, persistent=False): 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) + 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 - if persistent: - boot_list = PERSISTENT_BOOT_MODE + client.change_boot_device_order(boot_list, drac_boot_device) else: - boot_list = NON_PERSISTENT_BOOT_MODE + # 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.change_boot_device_order(boot_list, drac_boot_device) client.commit_pending_bios_changes() except drac_exceptions.BaseClientException as exc: LOG.error(_LE('DRAC driver failed to change boot device order for ' |