
Make iLO BIOS interface clean steps asynchronous to ensure the BIOS settings on the node are consistent with the settings stored in the database. Node goes through the power cycle after the settings are applied. Once the node reboots, ironic gets the current settings from the node, caches the settings in the database, checks for any errors from the status message and marks the clean step as failed or success accordingly. Change-Id: I00deaa2f2b68d2a34f8592ea20c36349a87d6c23 Story: #2004066 Task: #27074
240 lines
9.4 KiB
Python
240 lines
9.4 KiB
Python
# Copyright 2018 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 BIOS Interface
|
|
"""
|
|
|
|
from ironic_lib import metrics_utils
|
|
from oslo_log import log as logging
|
|
from oslo_utils import importutils
|
|
|
|
from ironic.common import exception
|
|
from ironic.common.i18n import _
|
|
from ironic.common import states
|
|
from ironic.conductor import utils as manager_utils
|
|
from ironic.drivers import base
|
|
from ironic.drivers.modules import deploy_utils
|
|
from ironic.drivers.modules.ilo import common as ilo_common
|
|
from ironic import objects
|
|
|
|
LOG = logging.getLogger(__name__)
|
|
METRICS = metrics_utils.get_metrics_logger(__name__)
|
|
|
|
ilo_error = importutils.try_import('proliantutils.exception')
|
|
|
|
|
|
class IloBIOS(base.BIOSInterface):
|
|
|
|
def get_properties(self):
|
|
return ilo_common.REQUIRED_PROPERTIES
|
|
|
|
@METRICS.timer('IloBIOS.validate')
|
|
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)
|
|
|
|
def _execute_pre_boot_bios_step(self, task, step, data=None):
|
|
"""Perform operations required prior to the reboot.
|
|
|
|
Depending on the clean step, it executes the operations required
|
|
and moves the node to CLEANWAIT state prior to reboot.
|
|
:param task: a task from TaskManager.
|
|
:param step: name of the clean step to be performed
|
|
:param data: if the clean step is apply_configuration it holds
|
|
the settings data.
|
|
:raises: NodeCleaningFailure if it fails any conditions expected
|
|
"""
|
|
node = task.node
|
|
|
|
if step not in ('apply_configuration', 'factory_reset'):
|
|
errmsg = _('Could not find the step %(step)s for the '
|
|
'node %(node)s.')
|
|
raise exception.NodeCleaningFailure(
|
|
errmsg % {'step': step, 'node': node.uuid})
|
|
|
|
try:
|
|
ilo_object = ilo_common.get_ilo_object(node)
|
|
ilo_object.set_bios_settings(data) if step == (
|
|
'apply_configuration') else ilo_object.reset_bios_to_default()
|
|
except (exception.MissingParameterValue,
|
|
exception.InvalidParameterValue,
|
|
ilo_error.IloError,
|
|
ilo_error.IloCommandNotSupportedError) as ir_exception:
|
|
errmsg = _('Clean step %(step)s failed '
|
|
'on the node %(node)s with error: %(err)s')
|
|
raise exception.NodeCleaningFailure(
|
|
errmsg % {'step': step, 'node': node.uuid,
|
|
'err': ir_exception})
|
|
|
|
deploy_opts = deploy_utils.build_agent_options(node)
|
|
task.driver.boot.prepare_ramdisk(task, deploy_opts)
|
|
manager_utils.node_power_action(task, states.REBOOT)
|
|
|
|
driver_internal_info = node.driver_internal_info
|
|
driver_internal_info['cleaning_reboot'] = True
|
|
driver_internal_info['skip_current_clean_step'] = False
|
|
|
|
if step == 'apply_configuration':
|
|
driver_internal_info['apply_bios'] = True
|
|
else:
|
|
driver_internal_info['reset_bios'] = True
|
|
|
|
node.driver_internal_info = driver_internal_info
|
|
node.save()
|
|
return states.CLEANWAIT
|
|
|
|
def _execute_post_boot_bios_step(self, task, step):
|
|
"""Perform operations required after the reboot.
|
|
|
|
Caches BIOS settings in the database and clear the flags assocated
|
|
with the clean step post reboot.
|
|
:param task: a task from TaskManager.
|
|
:param step: name of the clean step to be performed
|
|
:raises: NodeCleaningFailure if it fails any conditions expected
|
|
"""
|
|
node = task.node
|
|
|
|
driver_internal_info = node.driver_internal_info
|
|
driver_internal_info.pop('apply_bios', None)
|
|
driver_internal_info.pop('reset_bios', None)
|
|
task.node.driver_internal_info = driver_internal_info
|
|
task.node.save()
|
|
|
|
if step not in ('apply_configuration', 'factory_reset'):
|
|
errmsg = _('Could not find the step %(step)s for the '
|
|
'node %(node)s.')
|
|
raise exception.NodeCleaningFailure(
|
|
errmsg % {'step': step, 'node': node.uuid})
|
|
|
|
try:
|
|
ilo_object = ilo_common.get_ilo_object(node)
|
|
status = ilo_object.get_bios_settings_result()
|
|
except (exception.MissingParameterValue,
|
|
exception.InvalidParameterValue,
|
|
ilo_error.IloError,
|
|
ilo_error.IloCommandNotSupportedError) as ir_exception:
|
|
|
|
errmsg = _('Clean step %(step)s failed '
|
|
'on the node %(node)s with error: %(err)s')
|
|
raise exception.NodeCleaningFailure(
|
|
errmsg % {'step': step, 'node': node.uuid,
|
|
'err': ir_exception})
|
|
|
|
if status.get('status') == 'failed':
|
|
errmsg = _('Clean step %(step)s failed '
|
|
'on the node %(node)s with error: %(err)s')
|
|
raise exception.NodeCleaningFailure(
|
|
errmsg % {'step': step, 'node': node.uuid,
|
|
'err': status.get('results')})
|
|
|
|
@METRICS.timer('IloBIOS.apply_configuration')
|
|
@base.clean_step(priority=0, abortable=False, argsinfo={
|
|
'settings': {
|
|
'description': "Dictionary with current BIOS configuration.",
|
|
'required': True
|
|
}
|
|
})
|
|
def apply_configuration(self, task, settings):
|
|
"""Applies the provided configuration on the node.
|
|
|
|
:param task: a TaskManager instance.
|
|
:param settings: Settings intended to be applied on the node.
|
|
:raises: NodeCleaningFailure when applying the configuration on
|
|
the node fails.
|
|
|
|
"""
|
|
node = task.node
|
|
driver_internal_info = node.driver_internal_info
|
|
data = {}
|
|
for setting in settings:
|
|
data.update({setting['name']: setting['value']})
|
|
if not driver_internal_info.get('apply_bios'):
|
|
return self._execute_pre_boot_bios_step(
|
|
task, 'apply_configuration', data)
|
|
else:
|
|
return self._execute_post_boot_bios_step(
|
|
task, 'apply_configuration')
|
|
|
|
@METRICS.timer('IloBIOS.factory_reset')
|
|
@base.clean_step(priority=0, abortable=False)
|
|
def factory_reset(self, task):
|
|
"""Reset the BIOS settings to factory configuration.
|
|
|
|
:param task: a TaskManager instance.
|
|
:raises: NodeCleaningFailure when IloError or any other exception
|
|
is caught.
|
|
|
|
"""
|
|
node = task.node
|
|
driver_internal_info = node.driver_internal_info
|
|
|
|
if not driver_internal_info.get('reset_bios'):
|
|
return self._execute_pre_boot_bios_step(task, 'factory_reset')
|
|
else:
|
|
return self._execute_post_boot_bios_step(task, 'factory_reset')
|
|
|
|
@METRICS.timer('IloBIOS.cache_bios_settings')
|
|
def cache_bios_settings(self, task):
|
|
"""Store the BIOS settings in the database.
|
|
|
|
:param task: a TaskManager instance.
|
|
:raises: NodeCleaningFailure when IloError or any other exception
|
|
is caught.
|
|
|
|
"""
|
|
node = task.node
|
|
nodeid = node.id
|
|
|
|
errmsg = _("Caching BIOS settings failed "
|
|
"on node %(node)s with error: %(err)s")
|
|
try:
|
|
ilo_object = ilo_common.get_ilo_object(node)
|
|
bios_settings = ilo_object.get_current_bios_settings()
|
|
|
|
except (exception.MissingParameterValue,
|
|
exception.InvalidParameterValue,
|
|
ilo_error.IloError,
|
|
ilo_error.IloCommandNotSupportedError) as ir_exception:
|
|
raise exception.NodeCleaningFailure(
|
|
errmsg % {'node': node.uuid, 'err': ir_exception})
|
|
|
|
fmt_bios_settings = []
|
|
|
|
for setting in bios_settings:
|
|
fmt_bios_settings.append({"name": setting,
|
|
"value": bios_settings[setting]})
|
|
|
|
create_list, update_list, delete_list, nochange_list = (
|
|
objects.BIOSSettingList.sync_node_setting(task.context,
|
|
nodeid,
|
|
fmt_bios_settings))
|
|
if len(create_list) > 0:
|
|
objects.BIOSSettingList.create(task.context, nodeid, create_list)
|
|
if len(update_list) > 0:
|
|
objects.BIOSSettingList.save(task.context, nodeid, update_list)
|
|
if len(delete_list) > 0:
|
|
delete_name_list = [delete_name.get(
|
|
"name") for delete_name in delete_list]
|
|
objects.BIOSSettingList.delete(
|
|
task.context, nodeid, delete_name_list)
|