"Fossies" - the Fresh Open Source Software Archive

Member "ironic-16.0.3/ironic/drivers/modules/ilo/management.py" (18 Jan 2021, 43061 Bytes) of package /linux/misc/openstack/ironic-16.0.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "management.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 16.0.2_vs_16.0.3.

    1 # Copyright 2014 Hewlett-Packard Development Company, L.P.
    2 #
    3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
    4 # not use this file except in compliance with the License. You may obtain
    5 # a copy of the License at
    6 #
    7 #      http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 # Unless required by applicable law or agreed to in writing, software
   10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 # License for the specific language governing permissions and limitations
   13 # under the License.
   14 """
   15 iLO Management Interface
   16 """
   17 
   18 from urllib import parse as urlparse
   19 
   20 from ironic_lib import metrics_utils
   21 from oslo_log import log as logging
   22 from oslo_service import loopingcall
   23 from oslo_utils import excutils
   24 from oslo_utils import importutils
   25 
   26 from ironic.common import boot_devices
   27 from ironic.common import boot_modes
   28 from ironic.common import exception
   29 from ironic.common.i18n import _
   30 from ironic.common import states
   31 from ironic.conductor import task_manager
   32 from ironic.conductor import utils as manager_utils
   33 from ironic.conf import CONF
   34 from ironic.drivers import base
   35 from ironic.drivers.modules import agent_base
   36 from ironic.drivers.modules import deploy_utils
   37 from ironic.drivers.modules.ilo import common as ilo_common
   38 from ironic.drivers.modules.ilo import firmware_processor
   39 from ironic.drivers.modules import ipmitool
   40 from ironic.drivers import utils as driver_utils
   41 from ironic.objects import volume_target
   42 
   43 LOG = logging.getLogger(__name__)
   44 
   45 METRICS = metrics_utils.get_metrics_logger(__name__)
   46 
   47 ilo_error = importutils.try_import('proliantutils.exception')
   48 
   49 BOOT_DEVICE_MAPPING_TO_ILO = {
   50     boot_devices.PXE: 'NETWORK',
   51     boot_devices.DISK: 'HDD',
   52     boot_devices.CDROM: 'CDROM',
   53     boot_devices.ISCSIBOOT: 'ISCSI'
   54 }
   55 BOOT_DEVICE_ILO_TO_GENERIC = {
   56     v: k for k, v in BOOT_DEVICE_MAPPING_TO_ILO.items()}
   57 
   58 MANAGEMENT_PROPERTIES = ilo_common.REQUIRED_PROPERTIES.copy()
   59 MANAGEMENT_PROPERTIES.update(ilo_common.CLEAN_PROPERTIES)
   60 
   61 _ACTIVATE_ILO_LICENSE_ARGSINFO = {
   62     'ilo_license_key': {
   63         'description': (
   64             'The HPE iLO Advanced license key to activate enterprise '
   65             'features.'
   66         ),
   67         'required': True
   68     }
   69 }
   70 
   71 _RESET_ILO_CREDENTIALS_ARGSINFO = {
   72     'ilo_password': {
   73         'description': (
   74             'Password string for iLO user with administrative privileges '
   75             'being set in the driver_info property "ilo_username".'
   76         ),
   77         'required': True
   78     }
   79 }
   80 
   81 _FIRMWARE_UPDATE_ARGSINFO = {
   82     'firmware_update_mode': {
   83         'description': (
   84             "This argument indicates the mode (or mechanism) of firmware "
   85             "update procedure. Supported value is 'ilo'."
   86         ),
   87         'required': True
   88     },
   89     'firmware_images': {
   90         'description': (
   91             "This argument represents the ordered list of JSON "
   92             "dictionaries of firmware images. Each firmware image "
   93             "dictionary consists of three mandatory fields, namely 'url', "
   94             "'checksum' and 'component'. These fields represent firmware "
   95             "image location URL, md5 checksum of image file and firmware "
   96             "component type respectively. The supported firmware URL "
   97             "schemes are 'file', 'http', 'https' and 'swift'. The "
   98             "supported values for firmware component are 'ilo', 'cpld', "
   99             "'power_pic', 'bios' and 'chassis'. The firmware images will "
  100             "be applied (in the order given) one by one on the baremetal "
  101             "server. For more information, see "
  102             "https://docs.openstack.org/ironic/latest/admin/drivers/ilo.html#initiating-firmware-update-as-manual-clean-step"  # noqa
  103         ),
  104         'required': True
  105     }
  106 }
  107 
  108 _FIRMWARE_UPDATE_SUM_ARGSINFO = {
  109     'url': {
  110         'description': (
  111             "The image location for SPP (Service Pack for Proliant) ISO."
  112         ),
  113         'required': True
  114     },
  115     'checksum': {
  116         'description': (
  117             "The md5 checksum of the SPP image file."
  118         ),
  119         'required': True
  120     },
  121     'components': {
  122         'description': (
  123             "The list of firmware component filenames. If not specified, "
  124             "SUM updates all the firmware components."
  125         ),
  126         'required': False
  127     }
  128 }
  129 
  130 
  131 def _execute_ilo_step(node, step, *args, **kwargs):
  132     """Executes a particular deploy or clean step.
  133 
  134     :param node: an Ironic node object.
  135     :param step: a step to be executed.
  136     :param args: The args to be passed to the step.
  137     :param kwargs: The kwargs to be passed to the step.
  138     :raises: NodeCleaningFailure, on failure to execute the clean step.
  139     :raises: InstanceDeployFailure, on failure to execute the deploy step.
  140     """
  141     ilo_object = ilo_common.get_ilo_object(node)
  142 
  143     try:
  144         step_method = getattr(ilo_object, step)
  145     except AttributeError:
  146         # The specified clean/deploy step is not present in the proliantutils
  147         # package. Raise exception to update the proliantutils package
  148         # to newer version.
  149         msg = (_("Step '%s' not found. 'proliantutils' package needs to be "
  150                  "updated.") % step)
  151         if node.clean_step:
  152             raise exception.NodeCleaningFailure(msg)
  153         raise exception.InstanceDeployFailure(msg)
  154     try:
  155         step_method(*args, **kwargs)
  156     except ilo_error.IloCommandNotSupportedError:
  157         # This step is not supported on Gen8 and below servers.
  158         # Log the failure and continue with cleaning or deployment.
  159         LOG.warning("'%(step)s' step is not supported on node "
  160                     "%(uuid)s. Skipping the step.",
  161                     {'step': step, 'uuid': node.uuid})
  162     except ilo_error.IloError as ilo_exception:
  163         msg = (_("Step %(step)s failed on node %(node)s with "
  164                  "error: %(err)s") %
  165                {'node': node.uuid, 'step': step, 'err': ilo_exception})
  166         if node.clean_step:
  167             raise exception.NodeCleaningFailure(msg)
  168         raise exception.InstanceDeployFailure(msg)
  169 
  170 
  171 def _should_collect_logs(command):
  172     """Returns boolean to check whether logs need to collected or not."""
  173     return ((CONF.agent.deploy_logs_collect == 'on_failure'
  174              and command['command_status'] == 'FAILED')
  175             or CONF.agent.deploy_logs_collect == 'always')
  176 
  177 
  178 class IloManagement(base.ManagementInterface):
  179 
  180     def get_properties(self):
  181         return MANAGEMENT_PROPERTIES
  182 
  183     @METRICS.timer('IloManagement.validate')
  184     def validate(self, task):
  185         """Check that 'driver_info' contains required ILO credentials.
  186 
  187         Validates whether the 'driver_info' property of the supplied
  188         task's node contains the required credentials information.
  189 
  190         :param task: a task from TaskManager.
  191         :raises: InvalidParameterValue if required iLO parameters
  192             are not valid.
  193         :raises: MissingParameterValue if a required parameter is missing.
  194 
  195         """
  196         ilo_common.parse_driver_info(task.node)
  197 
  198     @METRICS.timer('IloManagement.get_supported_boot_devices')
  199     def get_supported_boot_devices(self, task):
  200         """Get a list of the supported boot devices.
  201 
  202         :param task: a task from TaskManager.
  203         :returns: A list with the supported boot devices defined
  204                   in :mod:`ironic.common.boot_devices`.
  205 
  206         """
  207         return list(BOOT_DEVICE_MAPPING_TO_ILO)
  208 
  209     @METRICS.timer('IloManagement.get_boot_device')
  210     def get_boot_device(self, task):
  211         """Get the current boot device for a node.
  212 
  213         Returns the current boot device of the node.
  214 
  215         :param task: a task from TaskManager.
  216         :raises: MissingParameterValue if a required iLO parameter is missing.
  217         :raises: IloOperationError on an error from IloClient library.
  218         :returns: a dictionary containing:
  219 
  220             :boot_device:
  221                 the boot device, one of the supported devices listed in
  222                 :mod:`ironic.common.boot_devices` or None if it is unknown.
  223             :persistent:
  224                 Whether the boot device will persist to all future boots or
  225                 not, None if it is unknown.
  226 
  227         """
  228         ilo_object = ilo_common.get_ilo_object(task.node)
  229         persistent = False
  230 
  231         try:
  232             # Return one time boot device if set, else return
  233             # the persistent boot device
  234             next_boot = ilo_object.get_one_time_boot()
  235             if next_boot == 'Normal':
  236                 # One time boot is not set. Check for persistent boot.
  237                 persistent = True
  238                 next_boot = ilo_object.get_persistent_boot_device()
  239 
  240         except ilo_error.IloError as ilo_exception:
  241             operation = _("Get boot device")
  242             raise exception.IloOperationError(operation=operation,
  243                                               error=ilo_exception)
  244 
  245         boot_device = BOOT_DEVICE_ILO_TO_GENERIC.get(next_boot, None)
  246 
  247         if boot_device is None:
  248             persistent = None
  249 
  250         return {'boot_device': boot_device, 'persistent': persistent}
  251 
  252     @METRICS.timer('IloManagement.set_boot_device')
  253     @task_manager.require_exclusive_lock
  254     def set_boot_device(self, task, device, persistent=False):
  255         """Set the boot device for a node.
  256 
  257         Set the boot device to use on next reboot of the node.
  258 
  259         :param task: a task from TaskManager.
  260         :param device: the boot device, one of the supported devices
  261                        listed in :mod:`ironic.common.boot_devices`.
  262         :param persistent: Boolean value. True if the boot device will
  263                            persist to all future boots, False if not.
  264                            Default: False.
  265         :raises: InvalidParameterValue if an invalid boot device is
  266                  specified.
  267         :raises: MissingParameterValue if a required parameter is missing.
  268         :raises: IloOperationError on an error from IloClient library.
  269         """
  270 
  271         try:
  272             boot_device = BOOT_DEVICE_MAPPING_TO_ILO[device]
  273         except KeyError:
  274             raise exception.InvalidParameterValue(_(
  275                 "Invalid boot device %s specified.") % device)
  276         try:
  277             ilo_object = ilo_common.get_ilo_object(task.node)
  278 
  279             if not persistent:
  280                 ilo_object.set_one_time_boot(boot_device)
  281             else:
  282                 ilo_object.update_persistent_boot([boot_device])
  283 
  284         except ilo_error.IloError as ilo_exception:
  285             operation = _("Setting %s as boot device") % device
  286             raise exception.IloOperationError(operation=operation,
  287                                               error=ilo_exception)
  288 
  289         LOG.debug("Node %(uuid)s set to boot from %(device)s.",
  290                   {'uuid': task.node.uuid, 'device': device})
  291 
  292     @METRICS.timer('IloManagement.get_sensors_data')
  293     def get_sensors_data(self, task):
  294         """Get sensors data.
  295 
  296         :param task: a TaskManager instance.
  297         :raises: FailedToGetSensorData when getting the sensor data fails.
  298         :raises: FailedToParseSensorData when parsing sensor data fails.
  299         :raises: InvalidParameterValue if required ipmi parameters
  300                  are missing.
  301         :raises: MissingParameterValue if a required parameter is missing.
  302         :returns: returns a dict of sensor data group by sensor type.
  303 
  304         """
  305         ilo_common.update_ipmi_properties(task)
  306         ipmi_management = ipmitool.IPMIManagement()
  307         return ipmi_management.get_sensors_data(task)
  308 
  309     @METRICS.timer('IloManagement.reset_ilo')
  310     @base.deploy_step(priority=0)
  311     @base.clean_step(priority=CONF.ilo.clean_priority_reset_ilo)
  312     def reset_ilo(self, task):
  313         """Resets the iLO.
  314 
  315         :param task: a task from TaskManager.
  316         :raises: NodeCleaningFailure, on failure to execute of clean step.
  317         :raises: InstanceDeployFailure, on failure to execute of deploy step.
  318         """
  319         node = task.node
  320         _execute_ilo_step(node, 'reset_ilo')
  321 
  322         # Reset iLO ejects virtual media
  323         # Re-create the environment for agent boot, if required
  324         task.driver.boot.clean_up_ramdisk(task)
  325         deploy_opts = deploy_utils.build_agent_options(node)
  326         task.driver.boot.prepare_ramdisk(task, deploy_opts)
  327 
  328     @METRICS.timer('IloManagement.reset_ilo_credential')
  329     @base.deploy_step(priority=0, argsinfo=_RESET_ILO_CREDENTIALS_ARGSINFO)
  330     @base.clean_step(priority=CONF.ilo.clean_priority_reset_ilo_credential)
  331     def reset_ilo_credential(self, task, change_password=None):
  332         """Resets the iLO password.
  333 
  334         :param task: a task from TaskManager.
  335         :param change_password: Value for password to update on iLO.
  336         :raises: NodeCleaningFailure, on failure to execute of clean step.
  337         :raises: InstanceDeployFailure, on failure to execute of deploy step.
  338         """
  339         info = task.node.driver_info
  340         password = change_password
  341         if not password:
  342             password = info.pop('ilo_change_password', None)
  343 
  344         if not password:
  345             LOG.info("Missing 'ilo_change_password' parameter in "
  346                      "driver_info. Step 'reset_ilo_credential' is "
  347                      "not performed on node %s.", task.node.uuid)
  348             return
  349 
  350         _execute_ilo_step(task.node, 'reset_ilo_credential', password)
  351 
  352         info['ilo_password'] = password
  353         task.node.driver_info = info
  354         task.node.save()
  355 
  356     @METRICS.timer('IloManagement.reset_bios_to_default')
  357     @base.deploy_step(priority=0)
  358     @base.clean_step(priority=CONF.ilo.clean_priority_reset_bios_to_default)
  359     def reset_bios_to_default(self, task):
  360         """Resets the BIOS settings to default values.
  361 
  362         Resets BIOS to default settings. This operation is currently supported
  363         only on HP Proliant Gen9 and above servers.
  364 
  365         :param task: a task from TaskManager.
  366         :raises: NodeCleaningFailure, on failure to execute of clean step.
  367         :raises: InstanceDeployFailure, on failure to execute of deploy step.
  368         """
  369         return _execute_ilo_step(task.node, 'reset_bios_to_default')
  370 
  371     @METRICS.timer('IloManagement.reset_secure_boot_keys_to_default')
  372     @base.deploy_step(priority=0)
  373     @base.clean_step(priority=CONF.ilo.
  374                      clean_priority_reset_secure_boot_keys_to_default)
  375     def reset_secure_boot_keys_to_default(self, task):
  376         """Reset secure boot keys to manufacturing defaults.
  377 
  378         Resets the secure boot keys to manufacturing defaults. This
  379         operation is supported only on HP Proliant Gen9 and above servers.
  380 
  381         :param task: a task from TaskManager.
  382         :raises: NodeCleaningFailure, on failure to execute of clean step.
  383         :raises: InstanceDeployFailure, on failure to execute of deploy step.
  384         """
  385         return _execute_ilo_step(task.node, 'reset_secure_boot_keys')
  386 
  387     @METRICS.timer('IloManagement.clear_secure_boot_keys')
  388     @base.deploy_step(priority=0)
  389     @base.clean_step(priority=CONF.ilo.clean_priority_clear_secure_boot_keys)
  390     def clear_secure_boot_keys(self, task):
  391         """Clear all secure boot keys.
  392 
  393         Clears all the secure boot keys. This operation is supported only
  394         on HP Proliant Gen9 and above servers.
  395 
  396         :param task: a task from TaskManager.
  397         :raises: NodeCleaningFailure, on failure to execute of clean step.
  398         :raises: InstanceDeployFailure, on failure to execute of deploy step.
  399         """
  400         return _execute_ilo_step(task.node, 'clear_secure_boot_keys')
  401 
  402     @METRICS.timer('IloManagement.activate_license')
  403     @base.clean_step(priority=0, abortable=False, argsinfo={
  404         'ilo_license_key': {
  405             'description': (
  406                 'The HPE iLO Advanced license key to activate enterprise '
  407                 'features.'
  408             ),
  409             'required': True
  410         }
  411     })
  412     def activate_license(self, task, **kwargs):
  413         """Activates iLO Advanced license.
  414 
  415         :param task: a TaskManager object.
  416         :raises: InvalidParameterValue, if any of the arguments are invalid.
  417         :raises: NodeCleaningFailure, on failure to execute of clean step.
  418         """
  419         ilo_license_key = kwargs.get('ilo_license_key')
  420         node = task.node
  421 
  422         if not isinstance(ilo_license_key, str):
  423             msg = (_("Value of 'ilo_license_key' must be a string instead of "
  424                      "'%(value)s'. Step 'activate_license' is not executed "
  425                      "for %(node)s.")
  426                    % {'value': ilo_license_key, 'node': node.uuid})
  427             LOG.error(msg)
  428             raise exception.InvalidParameterValue(msg)
  429 
  430         LOG.debug("Activating iLO license for node %(node)s ...",
  431                   {'node': node.uuid})
  432         _execute_ilo_step(node, 'activate_license', ilo_license_key)
  433         LOG.info("iLO license activated for node %s.", node.uuid)
  434 
  435     @METRICS.timer('IloManagement.update_firmware')
  436     @base.deploy_step(priority=0, argsinfo=_FIRMWARE_UPDATE_ARGSINFO)
  437     @base.clean_step(priority=0, abortable=False,
  438                      argsinfo=_FIRMWARE_UPDATE_ARGSINFO)
  439     @firmware_processor.verify_firmware_update_args
  440     def update_firmware(self, task, **kwargs):
  441         """Updates the firmware.
  442 
  443         :param task: a TaskManager object.
  444         :raises: InvalidParameterValue if update firmware mode is not 'ilo'.
  445                  Even applicable for invalid input cases.
  446         :raises: NodeCleaningFailure, on failure to execute of clean step.
  447         :raises: InstanceDeployFailure, on failure to execute of deploy step.
  448         """
  449         node = task.node
  450         fw_location_objs_n_components = []
  451         firmware_images = kwargs['firmware_images']
  452         # Note(deray): Processing of firmware images happens here. As part
  453         # of processing checksum validation is also done for the firmware file.
  454         # Processing of firmware file essentially means downloading the file
  455         # on the conductor, validating the checksum of the downloaded content,
  456         # extracting the raw firmware file from its compact format, if it is,
  457         # and hosting the file on a web server or a swift store based on the
  458         # need of the baremetal server iLO firmware update method.
  459         try:
  460             for firmware_image_info in firmware_images:
  461                 url, checksum, component = (
  462                     firmware_processor.get_and_validate_firmware_image_info(
  463                         firmware_image_info, kwargs['firmware_update_mode']))
  464                 LOG.debug("Processing of firmware file: %(firmware_file)s on "
  465                           "node: %(node)s ... in progress",
  466                           {'firmware_file': url, 'node': node.uuid})
  467 
  468                 fw_processor = firmware_processor.FirmwareProcessor(url)
  469                 fw_location_obj = fw_processor.process_fw_on(node, checksum)
  470                 fw_location_objs_n_components.append(
  471                     (fw_location_obj, component))
  472 
  473                 LOG.debug("Processing of firmware file: %(firmware_file)s on "
  474                           "node: %(node)s ... done",
  475                           {'firmware_file': url, 'node': node.uuid})
  476         except exception.IronicException as ilo_exc:
  477             # delete all the files extracted so far from the extracted list
  478             # and re-raise the exception
  479             for fw_loc_obj_n_comp_tup in fw_location_objs_n_components:
  480                 fw_loc_obj_n_comp_tup[0].remove()
  481             LOG.error("Processing of firmware image: %(firmware_image)s "
  482                       "on node: %(node)s ... failed",
  483                       {'firmware_image': firmware_image_info,
  484                        'node': node.uuid})
  485             if node.clean_step:
  486                 raise exception.NodeCleaningFailure(node=node.uuid,
  487                                                     reason=ilo_exc)
  488             raise exception.InstanceDeployFailure(reason=ilo_exc)
  489 
  490         # Updating of firmware images happen here.
  491         try:
  492             for fw_location_obj, component in fw_location_objs_n_components:
  493                 fw_location = fw_location_obj.fw_image_location
  494                 LOG.debug("Firmware update for %(firmware_file)s on "
  495                           "node: %(node)s ... in progress",
  496                           {'firmware_file': fw_location, 'node': node.uuid})
  497 
  498                 _execute_ilo_step(
  499                     node, 'update_firmware', fw_location, component)
  500 
  501                 LOG.debug("Firmware update for %(firmware_file)s on "
  502                           "node: %(node)s ... done",
  503                           {'firmware_file': fw_location, 'node': node.uuid})
  504         except (exception.NodeCleaningFailure,
  505                 exception.InstanceDeployFailure):
  506             with excutils.save_and_reraise_exception():
  507                 LOG.error("Firmware update for %(firmware_file)s on "
  508                           "node: %(node)s failed.",
  509                           {'firmware_file': fw_location, 'node': node.uuid})
  510         finally:
  511             for fw_loc_obj_n_comp_tup in fw_location_objs_n_components:
  512                 fw_loc_obj_n_comp_tup[0].remove()
  513 
  514         # Firmware might have ejected the virtual media, if it was used.
  515         # Re-create the environment for agent boot, if required
  516         task.driver.boot.clean_up_ramdisk(task)
  517         deploy_opts = deploy_utils.build_agent_options(node)
  518         task.driver.boot.prepare_ramdisk(task, deploy_opts)
  519 
  520         LOG.info("All Firmware update operations completed successfully "
  521                  "for node: %s.", node.uuid)
  522 
  523     @METRICS.timer('IloManagement.update_firmware_sum')
  524     @base.clean_step(priority=0, abortable=False,
  525                      argsinfo=_FIRMWARE_UPDATE_SUM_ARGSINFO)
  526     def update_firmware_sum(self, task, **kwargs):
  527         """Clean step to update the firmware using Smart Update Manager (SUM)
  528 
  529         :param task: a TaskManager object.
  530         :raises: NodeCleaningFailure, on failure to execute of clean step.
  531         :returns: states.CLEANWAIT to signify the step will be completed async
  532         """
  533         return self._do_update_firmware_sum(task, **kwargs)
  534 
  535     @METRICS.timer('IloManagement.update_firmware_sum')
  536     @base.deploy_step(priority=0, argsinfo=_FIRMWARE_UPDATE_SUM_ARGSINFO)
  537     def flash_firmware_sum(self, task, **kwargs):
  538         """Deploy step to Update the firmware using Smart Update Manager (SUM).
  539 
  540         :param task: a TaskManager object.
  541         :raises: InstanceDeployFailure, on failure to execute of deploy step.
  542         :returns: states.DEPLOYWAIT to signify the step will be completed
  543             async
  544         """
  545         return self._do_update_firmware_sum(task, **kwargs)
  546 
  547     def _do_update_firmware_sum(self, task, **kwargs):
  548         """Update the firmware using Smart Update Manager (SUM).
  549 
  550         :param task: a TaskManager object.
  551         :raises: NodeCleaningFailure or InstanceDeployFailure, on failure to
  552             execute of clean or deploy step respectively.
  553         :returns: states.CLEANWAIT or states.DEPLOYWAIT to signify the step
  554             will be completed async for clean or deploy step respectively.
  555         """
  556         node = task.node
  557         if node.provision_state == states.DEPLOYING:
  558             step = node.deploy_step
  559             step_type = 'deploy'
  560         else:
  561             step = node.clean_step
  562             step_type = 'clean'
  563 
  564         # The arguments are validated and sent to the ProliantHardwareManager
  565         # to perform SUM based firmware update clean step.
  566         firmware_processor.get_and_validate_firmware_image_info(kwargs,
  567                                                                 'sum')
  568 
  569         url = kwargs['url']
  570         if urlparse.urlparse(url).scheme == 'swift':
  571             url = firmware_processor.get_swift_url(urlparse.urlparse(url))
  572             step['args']['url'] = url
  573 
  574         # Insert SPP ISO into virtual media CDROM
  575         ilo_common.attach_vmedia(node, 'CDROM', url)
  576 
  577         return agent_base.execute_step(task, step, step_type)
  578 
  579     @staticmethod
  580     @agent_base.post_deploy_step_hook(
  581         interface='management', step='flash_firmware_sum')
  582     @agent_base.post_clean_step_hook(
  583         interface='management', step='update_firmware_sum')
  584     def _update_firmware_sum_final(task, command):
  585         """Deploy/Clean step hook after SUM based firmware update operation.
  586 
  587         This method is invoked as a post deploy/clean step hook by the Ironic
  588         conductor once firmware update operaion is completed. The deploy/clean
  589         logs are collected and stored according to the configured storage
  590         backend when the node is configured to collect the logs.
  591 
  592         :param task: a TaskManager instance.
  593         :param command: A command result structure of the SUM based firmware
  594             update operation returned from agent ramdisk on query of the
  595             status of command(s).
  596         """
  597         if not _should_collect_logs(command):
  598             return
  599 
  600         if task.node.provision_state == states.DEPLOYWAIT:
  601             log_data = command['command_result']['deploy_result']['Log Data']
  602             label = command['command_result']['deploy_step']['step']
  603         else:
  604             log_data = command['command_result']['clean_result']['Log Data']
  605             label = command['command_result']['clean_step']['step']
  606 
  607         node = task.node
  608         try:
  609             driver_utils.store_ramdisk_logs(node, log_data, label=label)
  610         except exception.SwiftOperationError as e:
  611             LOG.error('Failed to store the logs from the node %(node)s '
  612                       'for "update_firmware_sum" clean step in Swift. '
  613                       'Error: %(error)s',
  614                       {'node': node.uuid, 'error': e})
  615         except EnvironmentError as e:
  616             LOG.exception('Failed to store the logs from the node %(node)s '
  617                           'for "update_firmware_sum" clean step due to a '
  618                           'file-system related error. Error: %(error)s',
  619                           {'node': node.uuid, 'error': e})
  620         except Exception as e:
  621             LOG.exception('Unknown error when storing logs from the node '
  622                           '%(node)s for "update_firmware_sum" clean step. '
  623                           'Error: %(error)s',
  624                           {'node': node.uuid, 'error': e})
  625 
  626     @METRICS.timer('IloManagement.set_iscsi_boot_target')
  627     def set_iscsi_boot_target(self, task):
  628         """Set iSCSI details of the system in UEFI boot mode.
  629 
  630         The initiator is set with the target details like
  631         IQN, LUN, IP, Port etc.
  632         :param task: a task from TaskManager.
  633         :raises: MissingParameterValue if a required parameter is missing.
  634         :raises: IloCommandNotSupportedInBiosError if system in BIOS boot mode.
  635         :raises: IloError on an error from iLO.
  636         """
  637         # Getting target info
  638         node = task.node
  639         macs = [port['address'] for port in task.ports]
  640         boot_volume = node.driver_internal_info.get('boot_from_volume')
  641         volume = volume_target.VolumeTarget.get_by_uuid(task.context,
  642                                                         boot_volume)
  643         properties = volume.properties
  644         username = properties.get('auth_username')
  645         password = properties.get('auth_password')
  646         try:
  647             portal = properties['target_portal']
  648             iqn = properties['target_iqn']
  649             lun = properties['target_lun']
  650             host, port = portal.split(':')
  651         except KeyError as e:
  652             raise exception.MissingParameterValue(
  653                 _('Failed to get iSCSI target info for node '
  654                   '%(node)s. Error: %(error)s') % {'node': task.node.uuid,
  655                                                    'error': e})
  656         ilo_object = ilo_common.get_ilo_object(task.node)
  657         try:
  658             auth_method = 'CHAP' if username else None
  659             ilo_object.set_iscsi_info(
  660                 iqn, lun, host, port, auth_method=auth_method,
  661                 username=username, password=password, macs=macs)
  662         except ilo_error.IloCommandNotSupportedInBiosError as ilo_exception:
  663             operation = (_("Setting of target IQN %(target_iqn)s for node "
  664                            "%(node)s")
  665                          % {'target_iqn': iqn, 'node': node.uuid})
  666             raise exception.IloOperationNotSupported(operation=operation,
  667                                                      error=ilo_exception)
  668         except ilo_error.IloError as ilo_exception:
  669             operation = (_("Setting of target IQN %(target_iqn)s for node "
  670                            "%(node)s")
  671                          % {'target_iqn': iqn, 'node': node.uuid})
  672             raise exception.IloOperationError(operation=operation,
  673                                               error=ilo_exception)
  674 
  675     @METRICS.timer('IloManagement.clear_iscsi_boot_target')
  676     def clear_iscsi_boot_target(self, task):
  677         """Unset iSCSI details of the system in UEFI boot mode.
  678 
  679         :param task: a task from TaskManager.
  680         :raises: IloCommandNotSupportedInBiosError if system in BIOS boot mode.
  681         :raises: IloError on an error from iLO.
  682         """
  683         ilo_object = ilo_common.get_ilo_object(task.node)
  684         try:
  685             macs = [port['address'] for port in task.ports]
  686             ilo_object.unset_iscsi_info(macs=macs)
  687         except ilo_error.IloCommandNotSupportedInBiosError as ilo_exception:
  688             operation = (_("Unsetting of iSCSI target for node %(node)s")
  689                          % {'node': task.node.uuid})
  690             raise exception.IloOperationNotSupported(operation=operation,
  691                                                      error=ilo_exception)
  692         except ilo_error.IloError as ilo_exception:
  693             operation = (_("Unsetting of iSCSI target for node %(node)s")
  694                          % {'node': task.node.uuid})
  695             raise exception.IloOperationError(operation=operation,
  696                                               error=ilo_exception)
  697 
  698     @METRICS.timer('IloManagement.inject_nmi')
  699     @task_manager.require_exclusive_lock
  700     def inject_nmi(self, task):
  701         """Inject NMI, Non Maskable Interrupt.
  702 
  703         Inject NMI (Non Maskable Interrupt) for a node immediately.
  704 
  705         :param task: A TaskManager instance containing the node to act on.
  706         :raises: IloCommandNotSupportedError if system does not support
  707             NMI injection.
  708         :raises: IloError on an error from iLO.
  709         :returns: None
  710         """
  711         node = task.node
  712         ilo_object = ilo_common.get_ilo_object(node)
  713         try:
  714             operation = (_("Injecting NMI for node %(node)s")
  715                          % {'node': node.uuid})
  716             ilo_object.inject_nmi()
  717         except ilo_error.IloCommandNotSupportedError as ilo_exception:
  718             raise exception.IloOperationNotSupported(operation=operation,
  719                                                      error=ilo_exception)
  720         except ilo_error.IloError as ilo_exception:
  721             raise exception.IloOperationError(operation=operation,
  722                                               error=ilo_exception)
  723 
  724     def get_supported_boot_modes(self, task):
  725         """Get a list of the supported boot devices.
  726 
  727         :param task: a task from TaskManager.
  728         :raises: IloOperationError if any exception happens in proliantutils
  729         :returns: A list with the supported boot devices defined
  730                   in :mod:`ironic.common.boot_devices`.
  731         """
  732         node = task.node
  733         ilo_object = ilo_common.get_ilo_object(node)
  734         try:
  735             modes = ilo_object.get_supported_boot_mode()
  736             if modes == ilo_common.SUPPORTED_BOOT_MODE_LEGACY_BIOS_ONLY:
  737                 return [boot_modes.LEGACY_BIOS]
  738             elif modes == ilo_common.SUPPORTED_BOOT_MODE_UEFI_ONLY:
  739                 return [boot_modes.UEFI]
  740             elif modes == ilo_common.SUPPORTED_BOOT_MODE_LEGACY_BIOS_AND_UEFI:
  741                 return [boot_modes.UEFI, boot_modes.LEGACY_BIOS]
  742         except ilo_error.IloError as ilo_exception:
  743             operation = _("Get supported boot modes")
  744             raise exception.IloOperationError(operation=operation,
  745                                               error=ilo_exception)
  746 
  747     @task_manager.require_exclusive_lock
  748     def set_boot_mode(self, task, mode):
  749         """Set the boot mode for a node.
  750 
  751         Set the boot mode to use on next reboot of the node.
  752 
  753         :param task: A task from TaskManager.
  754         :param mode: The boot mode, one of
  755                      :mod:`ironic.common.boot_modes`.
  756         :raises: InvalidParameterValue if an invalid boot mode is
  757                  specified.
  758         :raises: IloOperationError if setting boot mode failed.
  759         """
  760         if mode not in self.get_supported_boot_modes(task):
  761             raise exception.InvalidParameterValue(_(
  762                 "The given boot mode '%s' is not supported.") % mode)
  763         ilo_common.set_boot_mode(task.node, mode)
  764 
  765     def get_boot_mode(self, task):
  766         """Get the current boot mode for a node.
  767 
  768         Provides the current boot mode of the node.
  769 
  770         :param task: A task from TaskManager.
  771         :raises: IloOperationError on an error from IloClient library.
  772         :returns: The boot mode, one of :mod:`ironic.common.boot_mode` or
  773                   None if it is unknown.
  774         """
  775         return ilo_common.get_current_boot_mode(task.node)
  776 
  777 
  778 class Ilo5Management(IloManagement):
  779 
  780     def _set_driver_internal_value(self, task, value, *keys):
  781         driver_internal_info = task.node.driver_internal_info
  782         for key in keys:
  783             driver_internal_info[key] = value
  784         task.node.driver_internal_info = driver_internal_info
  785         task.node.save()
  786 
  787     def _pop_driver_internal_values(self, task, *keys):
  788         driver_internal_info = task.node.driver_internal_info
  789         for key in keys:
  790             driver_internal_info.pop(key, None)
  791         task.node.driver_internal_info = driver_internal_info
  792         task.node.save()
  793 
  794     def _wait_for_disk_erase_status(self, node):
  795         """Wait for out-of-band sanitize disk erase to be completed."""
  796         interval = CONF.ilo.oob_erase_devices_job_status_interval
  797         ilo_object = ilo_common.get_ilo_object(node)
  798         time_elps = [0]
  799 
  800         # This will loop indefinitely till disk erase is complete
  801         def _wait():
  802             if ilo_object.has_disk_erase_completed():
  803                 raise loopingcall.LoopingCallDone()
  804 
  805             time_elps[0] += interval
  806             LOG.debug("%(tim)s secs elapsed while waiting for out-of-band "
  807                       "sanitize disk erase to complete for node %(node)s.",
  808                       {'tim': time_elps[0], 'node': node.uuid})
  809 
  810         # Start a timer and wait for the operation to complete.
  811         timer = loopingcall.FixedIntervalLoopingCall(_wait)
  812         timer.start(interval=interval).wait()
  813         return True
  814 
  815     def _validate_erase_pattern(self, erase_pattern, node):
  816         invalid = False
  817         if isinstance(erase_pattern, dict):
  818             for device_type, pattern in erase_pattern.items():
  819                 if device_type == 'hdd' and pattern in (
  820                         'overwrite', 'crypto', 'zero'):
  821                     continue
  822                 elif device_type == 'ssd' and pattern in (
  823                         'block', 'crypto', 'zero'):
  824                     continue
  825                 else:
  826                     invalid = True
  827                     break
  828         else:
  829             invalid = True
  830 
  831         if invalid:
  832             msg = (_("Erase pattern '%(value)s' is invalid. Clean step "
  833                      "'erase_devices' is not executed for %(node)s. Supported "
  834                      "patterns are, for "
  835                      "'hdd': ('overwrite', 'crypto', 'zero') and for "
  836                      "'ssd': ('block', 'crypto', 'zero'). "
  837                      "Ex. {'hdd': 'overwrite', 'ssd': 'block'}")
  838                    % {'value': erase_pattern, 'node': node.uuid})
  839             LOG.error(msg)
  840             raise exception.InvalidParameterValue(msg)
  841 
  842     @METRICS.timer('Ilo5Management.erase_devices')
  843     @base.clean_step(priority=0, abortable=False, argsinfo={
  844         'erase_pattern': {
  845             'description': (
  846                 'Dictionary of disk type and corresponding erase pattern '
  847                 'to be used to perform specific out-of-band sanitize disk '
  848                 'erase. Supported values are, '
  849                 'for "hdd": ("overwrite", "crypto", "zero"), '
  850                 'for "ssd": ("block", "crypto", "zero"). Default pattern is: '
  851                 '{"hdd": "overwrite", "ssd": "block"}.'
  852             ),
  853             'required': False
  854         }
  855     })
  856     def erase_devices(self, task, **kwargs):
  857         """Erase all the drives on the node.
  858 
  859         This method performs out-of-band sanitize disk erase on all the
  860         supported physical drives in the node. This erase cannot be performed
  861         on logical drives.
  862 
  863         :param task: a TaskManager instance.
  864         :raises: InvalidParameterValue, if any of the arguments are invalid.
  865         :raises: IloError on an error from iLO.
  866         """
  867         erase_pattern = kwargs.get('erase_pattern',
  868                                    {'hdd': 'overwrite', 'ssd': 'block'})
  869         node = task.node
  870         self._validate_erase_pattern(erase_pattern, node)
  871         driver_internal_info = node.driver_internal_info
  872         LOG.debug("Calling out-of-band sanitize disk erase for node %(node)s",
  873                   {'node': node.uuid})
  874         try:
  875             ilo_object = ilo_common.get_ilo_object(node)
  876             disk_types = ilo_object.get_available_disk_types()
  877             LOG.info("Disk type detected are: %(disk_types)s. Sanitize disk "
  878                      "erase are now exercised for one after another disk type "
  879                      "for node %(node)s.",
  880                      {'disk_types': disk_types, 'node': node.uuid})
  881 
  882             if disk_types:
  883                 # First disk-erase will execute for HDD's and after reboot only
  884                 # try for SSD, since both share same redfish api and would be
  885                 # overwritten.
  886                 if not driver_internal_info.get(
  887                         'ilo_disk_erase_hdd_check') and ('HDD' in disk_types):
  888                     ilo_object.do_disk_erase('HDD', erase_pattern.get('hdd'))
  889                     self._set_driver_internal_value(
  890                         task, True, 'cleaning_reboot',
  891                         'ilo_disk_erase_hdd_check')
  892                     self._set_driver_internal_value(
  893                         task, False, 'skip_current_clean_step')
  894                     deploy_opts = deploy_utils.build_agent_options(task.node)
  895                     task.driver.boot.prepare_ramdisk(task, deploy_opts)
  896                     manager_utils.node_power_action(task, states.REBOOT)
  897                     return states.CLEANWAIT
  898 
  899                 if not driver_internal_info.get(
  900                         'ilo_disk_erase_ssd_check') and ('SSD' in disk_types):
  901                     ilo_object.do_disk_erase('SSD', erase_pattern.get('ssd'))
  902                     self._set_driver_internal_value(
  903                         task, True, 'ilo_disk_erase_hdd_check',
  904                         'ilo_disk_erase_ssd_check', 'cleaning_reboot')
  905                     self._set_driver_internal_value(
  906                         task, False, 'skip_current_clean_step')
  907                     deploy_opts = deploy_utils.build_agent_options(task.node)
  908                     task.driver.boot.prepare_ramdisk(task, deploy_opts)
  909                     manager_utils.node_power_action(task, states.REBOOT)
  910                     return states.CLEANWAIT
  911 
  912                 # It will wait until disk erase will complete
  913                 if self._wait_for_disk_erase_status(task.node):
  914                     LOG.info("For node %(uuid)s erase_devices clean "
  915                              "step is done.", {'uuid': task.node.uuid})
  916                     self._pop_driver_internal_values(
  917                         task, 'ilo_disk_erase_hdd_check',
  918                         'ilo_disk_erase_ssd_check')
  919             else:
  920                 LOG.info("No drive found to perform out-of-band sanitize "
  921                          "disk erase for node %(node)s", {'node': node.uuid})
  922         except ilo_error.IloError as ilo_exception:
  923             log_msg = ("Out-of-band sanitize disk erase job failed for node "
  924                        "%(node)s. Message: '%(message)s'." %
  925                        {'node': task.node.uuid, 'message': ilo_exception})
  926             self._pop_driver_internal_values(task,
  927                                              'ilo_disk_erase_hdd_check',
  928                                              'ilo_disk_erase_ssd_check',
  929                                              'cleaning_reboot',
  930                                              'skip_current_clean_step')
  931             manager_utils.cleaning_error_handler(task, log_msg,
  932                                                  errmsg=ilo_exception)
  933 
  934     @base.clean_step(priority=0, abortable=False)
  935     def one_button_secure_erase(self, task):
  936         """Erase the whole system securely.
  937 
  938         The One-button secure erase process resets iLO and deletes all licenses
  939         stored there, resets BIOS settings, and deletes all Active Health
  940         System (AHS) and warranty data stored on the system. It also erases
  941         supported non-volatile storage data and deletes any deployment setting
  942         profiles.
  943 
  944         :param task: a TaskManager instance.
  945         :raises: IloError on an error from iLO.
  946         """
  947         node = task.node
  948         LOG.info("Calling one button secure erase for node %(node)s",
  949                  {'node': node.uuid})
  950         try:
  951             ilo_object = ilo_common.get_ilo_object(node)
  952             ilo_object.do_one_button_secure_erase()
  953             manager_utils.node_power_action(task, states.REBOOT)
  954             node.maintenance = True
  955             node.maintenance_reason = (
  956                 "One Button Secure erase clean step has begun, it will wipe "
  957                 "data from drives and any non-volatile/persistent storage, "
  958                 "reset iLO and delete all licenses stored there, reset BIOS "
  959                 "settings, delete  Active Health System (AHS) and warranty "
  960                 "data stored in the system and delete any deployment settings "
  961                 "profiles.")
  962             node.save()
  963             return states.CLEANWAIT
  964         except ilo_error.IloError as ilo_exception:
  965             log_msg = ("One button secure erase job failed for node "
  966                        "%(node)s. Message: '%(message)s'." %
  967                        {'node': task.node.uuid, 'message': ilo_exception})
  968             manager_utils.cleaning_error_handler(task, log_msg,
  969                                                  errmsg=ilo_exception)