"Fossies" - the Fresh Open Source Software Archive

Member "ironic-16.0.3/ironic/drivers/modules/redfish/bios.py" (18 Jan 2021, 14522 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 "bios.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 2018 DMTF. All rights reserved.
    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 from ironic_lib import metrics_utils
   16 from oslo_log import log
   17 from oslo_utils import importutils
   18 
   19 from ironic.common import exception
   20 from ironic.common.i18n import _
   21 from ironic.common import states
   22 from ironic.conductor import task_manager
   23 from ironic.conductor import utils as manager_utils
   24 from ironic.drivers import base
   25 from ironic.drivers.modules import deploy_utils
   26 from ironic.drivers.modules.redfish import utils as redfish_utils
   27 from ironic import objects
   28 
   29 LOG = log.getLogger(__name__)
   30 
   31 METRICS = metrics_utils.get_metrics_logger(__name__)
   32 
   33 sushy = importutils.try_import('sushy')
   34 
   35 
   36 class RedfishBIOS(base.BIOSInterface):
   37 
   38     _APPLY_CONFIGURATION_ARGSINFO = {
   39         'settings': {
   40             'description': (
   41                 'A list of BIOS settings to be applied'
   42             ),
   43             'required': True
   44         }
   45     }
   46 
   47     def __init__(self):
   48         super(RedfishBIOS, self).__init__()
   49         if sushy is None:
   50             raise exception.DriverLoadError(
   51                 driver='redfish',
   52                 reason=_("Unable to import the sushy library"))
   53 
   54     def cache_bios_settings(self, task):
   55         """Store or update the current BIOS settings for the node.
   56 
   57         Get the current BIOS settings and store them in the bios_settings
   58         database table.
   59 
   60         :param task: a TaskManager instance containing the node to act on.
   61         :raises: RedfishConnectionError when it fails to connect to Redfish
   62         :raises: RedfishError on an error from the Sushy library
   63         :raises: UnsupportedDriverExtension if the system does not support BIOS
   64             settings
   65         """
   66 
   67         node_id = task.node.id
   68         system = redfish_utils.get_system(task.node)
   69         try:
   70             attributes = system.bios.attributes
   71         except sushy.exceptions.MissingAttributeError:
   72             error_msg = _('Cannot fetch BIOS attributes for node %s, '
   73                           'BIOS settings are not supported.') % task.node.uuid
   74             LOG.error(error_msg)
   75             raise exception.UnsupportedDriverExtension(error_msg)
   76 
   77         settings = []
   78         # Convert Redfish BIOS attributes to Ironic BIOS settings
   79         if attributes:
   80             settings = [{'name': k, 'value': v} for k, v in attributes.items()]
   81 
   82         LOG.debug('Cache BIOS settings for node %(node_uuid)s',
   83                   {'node_uuid': task.node.uuid})
   84 
   85         create_list, update_list, delete_list, nochange_list = (
   86             objects.BIOSSettingList.sync_node_setting(
   87                 task.context, node_id, settings))
   88 
   89         if create_list:
   90             objects.BIOSSettingList.create(
   91                 task.context, node_id, create_list)
   92         if update_list:
   93             objects.BIOSSettingList.save(
   94                 task.context, node_id, update_list)
   95         if delete_list:
   96             delete_names = [d['name'] for d in delete_list]
   97             objects.BIOSSettingList.delete(
   98                 task.context, node_id, delete_names)
   99 
  100     @base.clean_step(priority=0)
  101     @base.deploy_step(priority=0)
  102     @base.cache_bios_settings
  103     def factory_reset(self, task):
  104         """Reset the BIOS settings of the node to the factory default.
  105 
  106         :param task: a TaskManager instance containing the node to act on.
  107         :raises: RedfishConnectionError when it fails to connect to Redfish
  108         :raises: RedfishError on an error from the Sushy library
  109         """
  110         system = redfish_utils.get_system(task.node)
  111         try:
  112             bios = system.bios
  113         except sushy.exceptions.MissingAttributeError:
  114             error_msg = (_('Redfish BIOS factory reset failed for node '
  115                            '%s, because BIOS settings are not supported.') %
  116                          task.node.uuid)
  117             LOG.error(error_msg)
  118             raise exception.RedfishError(error=error_msg)
  119 
  120         node = task.node
  121         info = node.driver_internal_info
  122         reboot_requested = info.get('post_factory_reset_reboot_requested')
  123         if not reboot_requested:
  124             LOG.debug('Factory reset BIOS configuration for node %(node)s',
  125                       {'node': node.uuid})
  126             try:
  127                 bios.reset_bios()
  128             except sushy.exceptions.SushyError as e:
  129                 error_msg = (_('Redfish BIOS factory reset failed for node '
  130                                '%(node)s. Error: %(error)s') %
  131                              {'node': node.uuid, 'error': e})
  132                 LOG.error(error_msg)
  133                 raise exception.RedfishError(error=error_msg)
  134 
  135             self.post_reset(task)
  136             self._set_reboot(task)
  137             return deploy_utils.get_async_step_return_state(task.node)
  138         else:
  139             current_attrs = bios.attributes
  140             LOG.debug('Post factory reset, BIOS configuration for node '
  141                       '%(node_uuid)s: %(attrs)r',
  142                       {'node_uuid': node.uuid, 'attrs': current_attrs})
  143             self._clear_reboot_requested(task)
  144 
  145     @base.clean_step(priority=0, argsinfo=_APPLY_CONFIGURATION_ARGSINFO)
  146     @base.deploy_step(priority=0, argsinfo=_APPLY_CONFIGURATION_ARGSINFO)
  147     @base.cache_bios_settings
  148     def apply_configuration(self, task, settings):
  149         """Apply the BIOS settings to the node.
  150 
  151         :param task: a TaskManager instance containing the node to act on.
  152         :param settings: a list of BIOS settings to be updated.
  153         :raises: RedfishConnectionError when it fails to connect to Redfish
  154         :raises: RedfishError on an error from the Sushy library
  155         """
  156 
  157         system = redfish_utils.get_system(task.node)
  158         try:
  159             bios = system.bios
  160         except sushy.exceptions.MissingAttributeError:
  161             error_msg = (_('Redfish BIOS factory reset failed for node '
  162                            '%s, because BIOS settings are not supported.') %
  163                          task.node.uuid)
  164             LOG.error(error_msg)
  165             raise exception.RedfishError(error=error_msg)
  166 
  167         # Convert Ironic BIOS settings to Redfish BIOS attributes
  168         attributes = {s['name']: s['value'] for s in settings}
  169 
  170         info = task.node.driver_internal_info
  171         reboot_requested = info.get('post_config_reboot_requested')
  172 
  173         if not reboot_requested:
  174             # Step 1: Apply settings and issue a reboot
  175             LOG.debug('Apply BIOS configuration for node %(node_uuid)s: '
  176                       '%(settings)r', {'node_uuid': task.node.uuid,
  177                                        'settings': settings})
  178 
  179             if bios.supported_apply_times and (
  180                     sushy.APPLY_TIME_ON_RESET in bios.supported_apply_times):
  181                 apply_time = sushy.APPLY_TIME_ON_RESET
  182             else:
  183                 apply_time = None
  184 
  185             try:
  186                 bios.set_attributes(attributes, apply_time=apply_time)
  187             except sushy.exceptions.SushyError as e:
  188                 error_msg = (_('Redfish BIOS apply configuration failed for '
  189                                'node %(node)s. Error: %(error)s') %
  190                              {'node': task.node.uuid, 'error': e})
  191                 LOG.error(error_msg)
  192                 raise exception.RedfishError(error=error_msg)
  193 
  194             self.post_configuration(task, settings)
  195             self._set_reboot_requested(task, attributes)
  196             return deploy_utils.get_async_step_return_state(task.node)
  197         else:
  198             # Step 2: Verify requested BIOS settings applied
  199             requested_attrs = info.get('requested_bios_attrs')
  200             current_attrs = bios.attributes
  201             LOG.debug('Verify BIOS configuration for node %(node_uuid)s: '
  202                       '%(attrs)r', {'node_uuid': task.node.uuid,
  203                                     'attrs': requested_attrs})
  204             self._clear_reboot_requested(task)
  205             self._check_bios_attrs(task, current_attrs, requested_attrs)
  206 
  207     def post_reset(self, task):
  208         """Perform post reset action to apply the BIOS factory reset.
  209 
  210         Extension point to allow vendor implementations to extend this class
  211         and override this method to perform a custom action to apply the BIOS
  212         factory reset to the Redfish service. The default implementation
  213         performs a reboot.
  214 
  215         :param task: a TaskManager instance containing the node to act on.
  216         """
  217         deploy_opts = deploy_utils.build_agent_options(task.node)
  218         task.driver.boot.prepare_ramdisk(task, deploy_opts)
  219         self._reboot(task)
  220 
  221     def post_configuration(self, task, settings):
  222         """Perform post configuration action to store the BIOS settings.
  223 
  224         Extension point to allow vendor implementations to extend this class
  225         and override this method to perform a custom action to write the BIOS
  226         settings to the Redfish service. The default implementation performs
  227         a reboot.
  228 
  229         :param task: a TaskManager instance containing the node to act on.
  230         :param settings: a list of BIOS settings to be updated.
  231         """
  232         deploy_opts = deploy_utils.build_agent_options(task.node)
  233         task.driver.boot.prepare_ramdisk(task, deploy_opts)
  234         self._reboot(task)
  235 
  236     def get_properties(self):
  237         """Return the properties of the interface.
  238 
  239         :returns: dictionary of <property name>:<property description> entries.
  240         """
  241         return redfish_utils.COMMON_PROPERTIES.copy()
  242 
  243     def validate(self, task):
  244         """Validates the driver information needed by the redfish driver.
  245 
  246         :param task: a TaskManager instance containing the node to act on.
  247         :raises: InvalidParameterValue on malformed parameter(s)
  248         :raises: MissingParameterValue on missing parameter(s)
  249         """
  250         redfish_utils.parse_driver_info(task.node)
  251 
  252     def _check_bios_attrs(self, task, current_attrs, requested_attrs):
  253         """Checks that the requested BIOS settings were applied to the service.
  254 
  255         :param task: a TaskManager instance containing the node to act on.
  256         :param current_attrs: the current BIOS attributes from the system.
  257         :param requested_attrs: the requested BIOS attributes to update.
  258         """
  259 
  260         attrs_not_updated = {}
  261         for attr in requested_attrs:
  262             if requested_attrs[attr] != current_attrs.get(attr):
  263                 attrs_not_updated[attr] = requested_attrs[attr]
  264 
  265         if attrs_not_updated:
  266             LOG.debug('BIOS settings %(attrs)s for node %(node_uuid)s '
  267                       'not updated.', {'attrs': attrs_not_updated,
  268                                        'node_uuid': task.node.uuid})
  269             self._set_step_failed(task, attrs_not_updated)
  270         else:
  271             LOG.debug('Verification of BIOS settings for node %(node_uuid)s '
  272                       'successful.', {'node_uuid': task.node.uuid})
  273 
  274     @task_manager.require_exclusive_lock
  275     def _reboot(self, task):
  276         """Reboot the target Redfish service.
  277 
  278         :param task: a TaskManager instance containing the node to act on.
  279         :raises: InvalidParameterValue when the wrong state is specified
  280              or the wrong driver info is specified.
  281         :raises: RedfishError on an error from the Sushy library
  282         """
  283         manager_utils.node_power_action(task, states.REBOOT)
  284 
  285     def _set_reboot(self, task):
  286         """Set driver_internal_info flags for deployment or cleaning reboot.
  287 
  288         :param task: a TaskManager instance containing the node to act on.
  289         """
  290         info = task.node.driver_internal_info
  291         info['post_factory_reset_reboot_requested'] = True
  292         task.node.driver_internal_info = info
  293         task.node.save()
  294         deploy_utils.set_async_step_flags(task.node, reboot=True,
  295                                           skip_current_step=False)
  296 
  297     def _set_reboot_requested(self, task, attributes):
  298         """Set driver_internal_info flags for reboot requested.
  299 
  300         :param task: a TaskManager instance containing the node to act on.
  301         :param attributes: the requested BIOS attributes to update.
  302         """
  303         info = task.node.driver_internal_info
  304         info['post_config_reboot_requested'] = True
  305         info['requested_bios_attrs'] = attributes
  306         task.node.driver_internal_info = info
  307         task.node.save()
  308         deploy_utils.set_async_step_flags(task.node, reboot=True,
  309                                           skip_current_step=False)
  310 
  311     def _clear_reboot_requested(self, task):
  312         """Clear driver_internal_info flags after reboot completed.
  313 
  314         :param task: a TaskManager instance containing the node to act on.
  315         """
  316         info = task.node.driver_internal_info
  317         info.pop('post_config_reboot_requested', None)
  318         info.pop('post_factory_reset_reboot_requested', None)
  319         info.pop('requested_bios_attrs', None)
  320         task.node.driver_internal_info = info
  321         task.node.save()
  322 
  323     def _set_step_failed(self, task, attrs_not_updated):
  324         """Fail the cleaning or deployment step and log the error.
  325 
  326         :param task: a TaskManager instance containing the node to act on.
  327         :param attrs_not_updated: the BIOS attributes that were not updated.
  328         """
  329         error_msg = (_('Redfish BIOS apply_configuration step failed for node '
  330                        '%(node)s. Attributes %(attrs)s are not updated.') %
  331                      {'node': task.node.uuid, 'attrs': attrs_not_updated})
  332         last_error = (_('Redfish BIOS apply_configuration step failed. '
  333                         'Attributes %(attrs)s are not updated.') %
  334                       {'attrs': attrs_not_updated})
  335         if task.node.provision_state in [states.CLEANING, states.CLEANWAIT]:
  336             manager_utils.cleaning_error_handler(task, last_error)
  337         if task.node.provision_state in [states.DEPLOYING, states.DEPLOYWAIT]:
  338             manager_utils.deploying_error_handler(task, error_msg, last_error)