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

# Copyright 2014 Hewlett-Packard Development Company, L.P.
#
# 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.
"""
iLO Management Interface
"""

from ironic_lib import metrics_utils
from oslo_log import log as logging
from oslo_utils import excutils
from oslo_utils import importutils
import six

from ironic.common import boot_devices
from ironic.common import exception
from ironic.common.i18n import _, _LE, _LI, _LW
from ironic.conductor import task_manager
from ironic.conf import CONF
from ironic.drivers import base
from ironic.drivers.modules.ilo import common as ilo_common
from ironic.drivers.modules.ilo import firmware_processor
from ironic.drivers.modules import ipmitool

LOG = logging.getLogger(__name__)

METRICS = metrics_utils.get_metrics_logger(__name__)

ilo_error = importutils.try_import('proliantutils.exception')

BOOT_DEVICE_MAPPING_TO_ILO = {
    boot_devices.PXE: 'NETWORK',
    boot_devices.DISK: 'HDD',
    boot_devices.CDROM: 'CDROM'
}
BOOT_DEVICE_ILO_TO_GENERIC = {
    v: k for k, v in BOOT_DEVICE_MAPPING_TO_ILO.items()}

MANAGEMENT_PROPERTIES = ilo_common.REQUIRED_PROPERTIES.copy()
MANAGEMENT_PROPERTIES.update(ilo_common.CLEAN_PROPERTIES)


def _execute_ilo_clean_step(node, step, *args, **kwargs):
    """Executes a particular clean step.

    :param node: an Ironic node object.
    :param step: a clean step to be executed.
    :param args: The args to be passed to the clean step.
    :param kwargs: The kwargs to be passed to the clean step.
    :raises: NodeCleaningFailure, on failure to execute step.
    """
    ilo_object = ilo_common.get_ilo_object(node)

    try:
        clean_step = getattr(ilo_object, step)
    except AttributeError:
        # The specified clean step is not present in the proliantutils
        # package. Raise exception to update the proliantutils package
        # to newer version.
        raise exception.NodeCleaningFailure(
            _("Clean step '%s' not found. 'proliantutils' package needs to be "
              "updated.") % step)
    try:
        clean_step(*args, **kwargs)
    except ilo_error.IloCommandNotSupportedError:
        # This clean step is not supported on Gen8 and below servers.
        # Log the failure and continue with cleaning.
        LOG.warning(_LW("'%(step)s' clean step is not supported on node "
                        "%(uuid)s. Skipping the clean step."),
                    {'step': step, 'uuid': node.uuid})
    except ilo_error.IloError as ilo_exception:
        raise exception.NodeCleaningFailure(_(
            "Clean step %(step)s failed "
            "on node %(node)s with error: %(err)s") %
            {'node': node.uuid, 'step': step, 'err': ilo_exception})


[docs]class IloManagement(base.ManagementInterface):
[docs] def get_properties(self): return MANAGEMENT_PROPERTIES
@METRICS.timer('IloManagement.validate')
[docs] def validate(self, task): """Check that 'driver_info' contains required ILO credentials. Validates whether the 'driver_info' property of the supplied task's node contains the required credentials information. :param task: a task from TaskManager. :raises: InvalidParameterValue if required iLO parameters are not valid. :raises: MissingParameterValue if a required parameter is missing. """ ilo_common.parse_driver_info(task.node)
@METRICS.timer('IloManagement.get_supported_boot_devices')
[docs] def get_supported_boot_devices(self, task): """Get a list of the supported boot devices. :param task: a task from TaskManager. :returns: A list with the supported boot devices defined in :mod:`ironic.common.boot_devices`. """ return list(BOOT_DEVICE_MAPPING_TO_ILO.keys())
@METRICS.timer('IloManagement.get_boot_device')
[docs] def get_boot_device(self, task): """Get the current boot device for a node. Returns the current boot device of the node. :param task: a task from TaskManager. :raises: MissingParameterValue if a required iLO parameter is missing. :raises: IloOperationError on an error from IloClient library. :returns: a dictionary containing: :boot_device: the boot device, one of the supported devices listed in :mod:`ironic.common.boot_devices` or None if it is unknown. :persistent: Whether the boot device will persist to all future boots or not, None if it is unknown. """ ilo_object = ilo_common.get_ilo_object(task.node) persistent = False try: # Return one time boot device if set, else return # the persistent boot device next_boot = ilo_object.get_one_time_boot() if next_boot == 'Normal': # One time boot is not set. Check for persistent boot. persistent = True next_boot = ilo_object.get_persistent_boot_device() except ilo_error.IloError as ilo_exception: operation = _("Get boot device") raise exception.IloOperationError(operation=operation, error=ilo_exception) boot_device = BOOT_DEVICE_ILO_TO_GENERIC.get(next_boot, None) if boot_device is None: persistent = None return {'boot_device': boot_device, 'persistent': persistent}
@METRICS.timer('IloManagement.set_boot_device') @task_manager.require_exclusive_lock
[docs] def set_boot_device(self, task, device, persistent=False): """Set the boot device for a node. Set the boot device to use on next reboot of the node. :param task: a task from TaskManager. :param device: the boot device, one of the supported devices listed in :mod:`ironic.common.boot_devices`. :param persistent: Boolean value. True if the boot device will persist to all future boots, False if not. Default: False. :raises: InvalidParameterValue if an invalid boot device is specified. :raises: MissingParameterValue if a required parameter is missing. :raises: IloOperationError on an error from IloClient library. """ try: boot_device = BOOT_DEVICE_MAPPING_TO_ILO[device] except KeyError: raise exception.InvalidParameterValue(_( "Invalid boot device %s specified.") % device) try: ilo_object = ilo_common.get_ilo_object(task.node) if not persistent: ilo_object.set_one_time_boot(boot_device) else: ilo_object.update_persistent_boot([boot_device]) except ilo_error.IloError as ilo_exception: operation = _("Setting %s as boot device") % device raise exception.IloOperationError(operation=operation, error=ilo_exception) LOG.debug("Node %(uuid)s set to boot from %(device)s.", {'uuid': task.node.uuid, 'device': device})
@METRICS.timer('IloManagement.get_sensors_data')
[docs] def get_sensors_data(self, task): """Get sensors data. :param task: a TaskManager instance. :raises: FailedToGetSensorData when getting the sensor data fails. :raises: FailedToParseSensorData when parsing sensor data fails. :raises: InvalidParameterValue if required ipmi parameters are missing. :raises: MissingParameterValue if a required parameter is missing. :returns: returns a dict of sensor data group by sensor type. """ ilo_common.update_ipmi_properties(task) ipmi_management = ipmitool.IPMIManagement() return ipmi_management.get_sensors_data(task)
@METRICS.timer('IloManagement.reset_ilo') @base.clean_step(priority=CONF.ilo.clean_priority_reset_ilo)
[docs] def reset_ilo(self, task): """Resets the iLO. :param task: a task from TaskManager. :raises: NodeCleaningFailure, on failure to execute step. """ return _execute_ilo_clean_step(task.node, 'reset_ilo')
@METRICS.timer('IloManagement.reset_ilo_credential') @base.clean_step(priority=CONF.ilo.clean_priority_reset_ilo_credential)
[docs] def reset_ilo_credential(self, task): """Resets the iLO password. :param task: a task from TaskManager. :raises: NodeCleaningFailure, on failure to execute step. """ info = task.node.driver_info password = info.pop('ilo_change_password', None) if not password: LOG.info(_LI("Missing 'ilo_change_password' parameter in " "driver_info. Clean step 'reset_ilo_credential' is " "not performed on node %s."), task.node.uuid) return _execute_ilo_clean_step(task.node, 'reset_ilo_credential', password) info['ilo_password'] = password task.node.driver_info = info task.node.save()
@METRICS.timer('IloManagement.reset_bios_to_default') @base.clean_step(priority=CONF.ilo.clean_priority_reset_bios_to_default)
[docs] def reset_bios_to_default(self, task): """Resets the BIOS settings to default values. Resets BIOS to default settings. This operation is currently supported only on HP Proliant Gen9 and above servers. :param task: a task from TaskManager. :raises: NodeCleaningFailure, on failure to execute step. """ return _execute_ilo_clean_step(task.node, 'reset_bios_to_default')
@METRICS.timer('IloManagement.reset_secure_boot_keys_to_default') @base.clean_step(priority=CONF.ilo. clean_priority_reset_secure_boot_keys_to_default)
[docs] def reset_secure_boot_keys_to_default(self, task): """Reset secure boot keys to manufacturing defaults. Resets the secure boot keys to manufacturing defaults. This operation is supported only on HP Proliant Gen9 and above servers. :param task: a task from TaskManager. :raises: NodeCleaningFailure, on failure to execute step. """ return _execute_ilo_clean_step(task.node, 'reset_secure_boot_keys')
@METRICS.timer('IloManagement.clear_secure_boot_keys') @base.clean_step(priority=CONF.ilo.clean_priority_clear_secure_boot_keys)
[docs] def clear_secure_boot_keys(self, task): """Clear all secure boot keys. Clears all the secure boot keys. This operation is supported only on HP Proliant Gen9 and above servers. :param task: a task from TaskManager. :raises: NodeCleaningFailure, on failure to execute step. """ return _execute_ilo_clean_step(task.node, 'clear_secure_boot_keys')
@METRICS.timer('IloManagement.activate_license') @base.clean_step(priority=0, abortable=False, argsinfo={ 'ilo_license_key': { 'description': ( 'The HPE iLO Advanced license key to activate enterprise ' 'features.' ), 'required': True } })
[docs] def activate_license(self, task, **kwargs): """Activates iLO Advanced license. :param task: a TaskManager object. :raises: InvalidParameterValue, if any of the arguments are invalid. :raises: NodeCleaningFailure, on failure to execute clean step. """ ilo_license_key = kwargs.get('ilo_license_key') node = task.node if not isinstance(ilo_license_key, six.string_types): msg = (_("Value of 'ilo_license_key' must be a string instead of " "'%(value)s'. Step 'activate_license' is not executed " "for %(node)s.") % {'value': ilo_license_key, 'node': node.uuid}) LOG.error(msg) raise exception.InvalidParameterValue(msg) LOG.debug("Activating iLO license for node %(node)s ...", {'node': node.uuid}) _execute_ilo_clean_step(node, 'activate_license', ilo_license_key) LOG.info(_LI("iLO license activated for node %(node)s."), {'node': node.uuid})
@METRICS.timer('IloManagement.update_firmware') @base.clean_step(priority=0, abortable=False, argsinfo={ 'firmware_update_mode': { 'description': ( "This argument indicates the mode (or mechanism) of firmware " "update procedure. Supported value is 'ilo'." ), 'required': True }, 'firmware_images': { 'description': ( "This argument represents the ordered list of JSON " "dictionaries of firmware images. Each firmware image " "dictionary consists of three mandatory fields, namely 'url', " "'checksum' and 'component'. These fields represent firmware " "image location URL, md5 checksum of image file and firmware " "component type respectively. The supported firmware URL " "schemes are 'file', 'http', 'https' and 'swift'. The " "supported values for firmware component are 'ilo', 'cpld', " "'power_pic', 'bios' and 'chassis'. The firmware images will " "be applied (in the order given) one by one on the baremetal " "server. For more information, see " "http://docs.openstack.org/developer/ironic/drivers/ilo.html#initiating-firmware-update-as-manual-clean-step" # noqa ), 'required': True } }) @firmware_processor.verify_firmware_update_args
[docs] def update_firmware(self, task, **kwargs): """Updates the firmware. :param task: a TaskManager object. :raises: InvalidParameterValue if update firmware mode is not 'ilo'. Even applicable for invalid input cases. :raises: NodeCleaningFailure, on failure to execute step. """ node = task.node fw_location_objs_n_components = [] firmware_images = kwargs['firmware_images'] # Note(deray): Processing of firmware images happens here. As part # of processing checksum validation is also done for the firmware file. # Processing of firmware file essentially means downloading the file # on the conductor, validating the checksum of the downloaded content, # extracting the raw firmware file from its compact format, if it is, # and hosting the file on a web server or a swift store based on the # need of the baremetal server iLO firmware update method. try: for firmware_image_info in firmware_images: url, checksum, component = ( firmware_processor.get_and_validate_firmware_image_info( firmware_image_info)) LOG.debug("Processing of firmware file: %(firmware_file)s on " "node: %(node)s ... in progress", {'firmware_file': url, 'node': node.uuid}) fw_processor = firmware_processor.FirmwareProcessor(url) fw_location_obj = fw_processor.process_fw_on(node, checksum) fw_location_objs_n_components.append( (fw_location_obj, component)) LOG.debug("Processing of firmware file: %(firmware_file)s on " "node: %(node)s ... done", {'firmware_file': url, 'node': node.uuid}) except exception.IronicException as ilo_exc: # delete all the files extracted so far from the extracted list # and re-raise the exception for fw_loc_obj_n_comp_tup in fw_location_objs_n_components: fw_loc_obj_n_comp_tup[0].remove() LOG.error(_LE("Processing of firmware image: %(firmware_image)s " "on node: %(node)s ... failed"), {'firmware_image': firmware_image_info, 'node': node.uuid}) raise exception.NodeCleaningFailure(node=node.uuid, reason=ilo_exc) # Updating of firmware images happen here. try: for fw_location_obj, component in fw_location_objs_n_components: fw_location = fw_location_obj.fw_image_location LOG.debug("Firmware update for %(firmware_file)s on " "node: %(node)s ... in progress", {'firmware_file': fw_location, 'node': node.uuid}) _execute_ilo_clean_step( node, 'update_firmware', fw_location, component) LOG.debug("Firmware update for %(firmware_file)s on " "node: %(node)s ... done", {'firmware_file': fw_location, 'node': node.uuid}) except exception.NodeCleaningFailure: with excutils.save_and_reraise_exception(): LOG.error(_LE("Firmware update for %(firmware_file)s on " "node: %(node)s failed."), {'firmware_file': fw_location, 'node': node.uuid}) finally: for fw_loc_obj_n_comp_tup in fw_location_objs_n_components: fw_loc_obj_n_comp_tup[0].remove() LOG.info(_LI("All Firmware update operations completed successfully " "for node: %s."), node.uuid)