Make iLO BIOS interface clean steps asynchronous

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
This commit is contained in:
vmud213 2018-09-13 15:30:53 +05:30
parent 680e5b5687
commit 335f6b4fd1
3 changed files with 468 additions and 148 deletions

View File

@ -21,12 +21,14 @@ from oslo_utils import importutils
from ironic.common import exception from ironic.common import exception
from ironic.common.i18n import _ 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 import base
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.ilo import common as ilo_common from ironic.drivers.modules.ilo import common as ilo_common
from ironic import objects from ironic import objects
LOG = logging.getLogger(__name__) LOG = logging.getLogger(__name__)
METRICS = metrics_utils.get_metrics_logger(__name__) METRICS = metrics_utils.get_metrics_logger(__name__)
ilo_error = importutils.try_import('proliantutils.exception') ilo_error = importutils.try_import('proliantutils.exception')
@ -51,6 +53,100 @@ class IloBIOS(base.BIOSInterface):
""" """
ilo_common.parse_driver_info(task.node) 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') @METRICS.timer('IloBIOS.apply_configuration')
@base.clean_step(priority=0, abortable=False, argsinfo={ @base.clean_step(priority=0, abortable=False, argsinfo={
'settings': { 'settings': {
@ -67,24 +163,17 @@ class IloBIOS(base.BIOSInterface):
the node fails. the node fails.
""" """
node = task.node
driver_internal_info = node.driver_internal_info
data = {} data = {}
for setting in settings: for setting in settings:
data.update({setting['name']: setting['value']}) data.update({setting['name']: setting['value']})
if not driver_internal_info.get('apply_bios'):
node = task.node return self._execute_pre_boot_bios_step(
task, 'apply_configuration', data)
errmsg = _("Clean step \"apply_configuration\" failed " else:
"on node %(node)s with error: %(err)s") return self._execute_post_boot_bios_step(
task, 'apply_configuration')
try:
ilo_object = ilo_common.get_ilo_object(node)
ilo_object.set_bios_settings(data)
except (exception.MissingParameterValue,
exception.InvalidParameterValue,
ilo_error.IloError,
ilo_error.IloCommandNotSupportedError) as ir_exception:
raise exception.NodeCleaningFailure(
errmsg % {'node': node.uuid, 'err': ir_exception})
@METRICS.timer('IloBIOS.factory_reset') @METRICS.timer('IloBIOS.factory_reset')
@base.clean_step(priority=0, abortable=False) @base.clean_step(priority=0, abortable=False)
@ -97,19 +186,12 @@ class IloBIOS(base.BIOSInterface):
""" """
node = task.node node = task.node
driver_internal_info = node.driver_internal_info
errmsg = _("Clean step \"factory_reset\" failed " if not driver_internal_info.get('reset_bios'):
"on node %(node)s with error: %(err)s") return self._execute_pre_boot_bios_step(task, 'factory_reset')
else:
try: return self._execute_post_boot_bios_step(task, 'factory_reset')
ilo_object = ilo_common.get_ilo_object(node)
ilo_object.reset_bios_to_default()
except (exception.MissingParameterValue,
exception.InvalidParameterValue,
ilo_error.IloError,
ilo_error.IloCommandNotSupportedError) as ir_exception:
raise exception.NodeCleaningFailure(
errmsg % {'node': node.uuid, 'err': ir_exception})
@METRICS.timer('IloBIOS.cache_bios_settings') @METRICS.timer('IloBIOS.cache_bios_settings')
def cache_bios_settings(self, task): def cache_bios_settings(self, task):
@ -127,7 +209,7 @@ class IloBIOS(base.BIOSInterface):
"on node %(node)s with error: %(err)s") "on node %(node)s with error: %(err)s")
try: try:
ilo_object = ilo_common.get_ilo_object(node) ilo_object = ilo_common.get_ilo_object(node)
bios_settings = ilo_object.get_pending_bios_settings() bios_settings = ilo_object.get_current_bios_settings()
except (exception.MissingParameterValue, except (exception.MissingParameterValue,
exception.InvalidParameterValue, exception.InvalidParameterValue,

View File

@ -21,6 +21,10 @@ from oslo_utils import importutils
from ironic.common import exception from ironic.common import exception
from ironic.conductor import task_manager from ironic.conductor import task_manager
from ironic.conductor import utils as manager_utils
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.ilo import bios as ilo_bios
from ironic.drivers.modules.ilo import boot as ilo_boot
from ironic.drivers.modules.ilo import common as ilo_common from ironic.drivers.modules.ilo import common as ilo_common
from ironic import objects from ironic import objects
from ironic.tests.unit.db import utils as db_utils from ironic.tests.unit.db import utils as db_utils
@ -48,145 +52,356 @@ class IloBiosTestCase(test_common.BaseIloTest):
task.driver.bios.validate(task) task.driver.bios.validate(task)
mock_drvinfo.assert_called_once_with(task.node) mock_drvinfo.assert_called_once_with(task.node)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True, def _test_ilo_error(self, exc_cls,
autospec=True) test_methods_not_called,
def _test_ilo_error(self, error_type, test_methods_called,
test_methods_not_called, method_details, ilo_mock): method_details, exception_mock):
error_dict = { exception_mock.side_effect = exc_cls('error')
"missing_parameter": exception.MissingParameterValue,
"invalid_parameter": exception.InvalidParameterValue
}
exc = error_dict.get(error_type)('error')
ilo_mock.side_effect = exc
method = method_details.get("name") method = method_details.get("name")
args = method_details.get("args") args = method_details.get("args")
self.assertRaises(exception.NodeCleaningFailure, self.assertRaises(exception.NodeCleaningFailure,
method, method,
*args) *args)
for test_method in test_methods_not_called: for test_method in test_methods_not_called:
eval("ilo_mock.return_value.%s.assert_not_called()" % ( test_method.assert_not_called()
test_method)) for called_method in test_methods_called:
called_method["name"].assert_called_once_with(
*called_method["args"])
@mock.patch.object(ilo_bios.IloBIOS, '_execute_post_boot_bios_step',
autospec=True)
@mock.patch.object(ilo_bios.IloBIOS, '_execute_pre_boot_bios_step',
autospec=True)
def test_apply_configuration_pre_boot(self, exe_pre_boot_mock,
exe_post_boot_mock):
settings = [
{
"name": "SET_A", "value": "VAL_A",
},
{
"name": "SET_B", "value": "VAL_B",
},
{
"name": "SET_C", "value": "VAL_C",
},
{
"name": "SET_D", "value": "VAL_D",
}
]
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
driver_internal_info = task.node.driver_internal_info
driver_internal_info.pop('apply_bios', None)
task.node.driver_internal_info = driver_internal_info
task.node.save()
actual_settings = {'SET_A': 'VAL_A', 'SET_B': 'VAL_B',
'SET_C': 'VAL_C', 'SET_D': 'VAL_D'}
task.driver.bios.apply_configuration(task, settings)
exe_pre_boot_mock.assert_called_once_with(
task.driver.bios, task, 'apply_configuration', actual_settings)
self.assertFalse(exe_post_boot_mock.called)
@mock.patch.object(ilo_bios.IloBIOS, '_execute_post_boot_bios_step',
autospec=True)
@mock.patch.object(ilo_bios.IloBIOS, '_execute_pre_boot_bios_step',
autospec=True)
def test_apply_configuration_post_boot(self, exe_pre_boot_mock,
exe_post_boot_mock):
settings = [
{
"name": "SET_A", "value": "VAL_A",
},
{
"name": "SET_B", "value": "VAL_B",
},
{
"name": "SET_C", "value": "VAL_C",
},
{
"name": "SET_D", "value": "VAL_D",
}
]
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
driver_internal_info = task.node.driver_internal_info
driver_internal_info['apply_bios'] = True
task.node.driver_internal_info = driver_internal_info
task.node.save()
task.driver.bios.apply_configuration(task, settings)
exe_post_boot_mock.assert_called_once_with(
task.driver.bios, task, 'apply_configuration')
self.assertFalse(exe_pre_boot_mock.called)
@mock.patch.object(ilo_boot.IloVirtualMediaBoot, 'prepare_ramdisk',
spec_set=True, autospec=True)
@mock.patch.object(manager_utils, 'node_power_action', spec_set=True,
autospec=True)
@mock.patch.object(deploy_utils, 'build_agent_options', spec_set=True,
autospec=True)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True, @mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True) autospec=True)
def test_apply_configuration(self, get_ilo_object_mock): def test__execute_pre_boot_bios_step_apply_configuration(
self, get_ilo_object_mock, build_agent_mock,
node_power_mock, prepare_mock):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
ilo_object_mock = get_ilo_object_mock.return_value ilo_object_mock = get_ilo_object_mock.return_value
data = [ data = {
{
"name": "SET_A", "value": "VAL_A",
},
{
"name": "SET_B", "value": "VAL_B",
},
{
"name": "SET_C", "value": "VAL_C",
},
{
"name": "SET_D", "value": "VAL_D",
}
]
task.driver.bios.apply_configuration(task, data)
expected = {
"SET_A": "VAL_A", "SET_A": "VAL_A",
"SET_B": "VAL_B", "SET_B": "VAL_B",
"SET_C": "VAL_C", "SET_C": "VAL_C",
"SET_D": "VAL_D" "SET_D": "VAL_D"
} }
ilo_object_mock.set_bios_settings.assert_called_once_with(expected) step = 'apply_configuration'
task.driver.bios._execute_pre_boot_bios_step(task, step, data)
def test_apply_configuration_missing_parameter(self): driver_info = task.node.driver_internal_info
with task_manager.acquire(self.context, self.node.uuid, self.assertTrue(
shared=True) as task: all(x in driver_info for x in (
mdobj = { 'apply_bios', 'cleaning_reboot',
"name": task.driver.bios.apply_configuration, 'skip_current_clean_step')))
"args": (task, []) ilo_object_mock.set_bios_settings.assert_called_once_with(data)
} self.assertFalse(ilo_object_mock.reset_bios_to_default.called)
self._test_ilo_error("missing_parameter", ["set_bios_settings"], build_agent_mock.assert_called_once_with(task.node)
mdobj) self.assertTrue(prepare_mock.called)
self.assertTrue(node_power_mock.called)
def test_apply_configuration_invalid_parameter(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mdobj = {
"name": task.driver.bios.apply_configuration,
"args": (task, [])
}
self._test_ilo_error("invalid_parameter", ["set_bios_settings"],
mdobj)
@mock.patch.object(ilo_boot.IloVirtualMediaBoot, 'prepare_ramdisk',
spec_set=True, autospec=True)
@mock.patch.object(manager_utils, 'node_power_action', spec_set=True,
autospec=True)
@mock.patch.object(deploy_utils, 'build_agent_options', spec_set=True,
autospec=True)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True, @mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True) autospec=True)
def test_apply_configuration_with_ilo_error(self, get_ilo_object_mock): def test__execute_pre_boot_bios_step_factory_reset(
self, get_ilo_object_mock, build_agent_mock,
node_power_mock, prepare_mock):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
ilo_object_mock = get_ilo_object_mock.return_value ilo_object_mock = get_ilo_object_mock.return_value
data = [ data = {
{ "SET_A": "VAL_A",
"name": "SET_A", "value": "VAL_A", "SET_B": "VAL_B",
}, "SET_C": "VAL_C",
{ "SET_D": "VAL_D"
"name": "SET_B", "value": "VAL_B", }
}, step = 'factory_reset'
] task.driver.bios._execute_pre_boot_bios_step(task, step, data)
exc = ilo_error.IloError('error') driver_info = task.node.driver_internal_info
ilo_object_mock.set_bios_settings.side_effect = exc self.assertTrue(
self.assertRaises(exception.NodeCleaningFailure, all(x in driver_info for x in (
task.driver.bios.apply_configuration, 'reset_bios', 'cleaning_reboot',
task, data) 'skip_current_clean_step')))
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test_factory_reset(self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
ilo_object_mock = get_ilo_object_mock.return_value
task.driver.bios.factory_reset(task)
ilo_object_mock.reset_bios_to_default.assert_called_once_with() ilo_object_mock.reset_bios_to_default.assert_called_once_with()
self.assertFalse(ilo_object_mock.set_bios_settings.called)
def test_factory_reset_missing_parameter(self): build_agent_mock.assert_called_once_with(task.node)
with task_manager.acquire(self.context, self.node.uuid, self.assertTrue(prepare_mock.called)
shared=True) as task: self.assertTrue(node_power_mock.called)
mdobj = {
"name": task.driver.bios.factory_reset,
"args": (task,)
}
self._test_ilo_error("missing_parameter",
["reset_bios_to_default"], mdobj)
def test_factory_reset_invalid_parameter(self):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
mdobj = {
"name": task.driver.bios.factory_reset,
"args": (task,)
}
self._test_ilo_error("invalid_parameter",
["reset_bios_to_default"], mdobj)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True, @mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True) autospec=True)
def test_factory_reset_with_ilo_error(self, get_ilo_object_mock): def test__execute_pre_boot_bios_step_invalid(
self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
ilo_object_mock = get_ilo_object_mock.return_value data = {
exc = ilo_error.IloError('error') "SET_A": "VAL_A",
ilo_object_mock.reset_bios_to_default.side_effect = exc "SET_B": "VAL_B",
"SET_C": "VAL_C",
"SET_D": "VAL_D"
}
step = 'invalid_step'
self.assertRaises(exception.NodeCleaningFailure, self.assertRaises(exception.NodeCleaningFailure,
task.driver.bios.factory_reset, task) task.driver.bios._execute_pre_boot_bios_step,
task, step, data)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True, @mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True) autospec=True)
def test_factory_reset_with_unknown_error(self, get_ilo_object_mock): def test__execute_pre_boot_bios_step_iloobj_failed(
self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
ilo_object_mock = get_ilo_object_mock.return_value data = {
exc = ilo_error.IloCommandNotSupportedError('error') "SET_A": "VAL_A",
ilo_object_mock.reset_bios_to_default.side_effect = exc "SET_B": "VAL_B",
"SET_C": "VAL_C",
"SET_D": "VAL_D"
}
get_ilo_object_mock.side_effect = exception.MissingParameterValue(
'err')
step = 'apply_configuration'
self.assertRaises(exception.NodeCleaningFailure, self.assertRaises(exception.NodeCleaningFailure,
task.driver.bios.factory_reset, task) task.driver.bios._execute_pre_boot_bios_step,
task, step, data)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test__execute_pre_boot_bios_step_set_bios_failed(
self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
data = {
"SET_A": "VAL_A",
"SET_B": "VAL_B",
"SET_C": "VAL_C",
"SET_D": "VAL_D"
}
ilo_object_mock = get_ilo_object_mock.return_value
ilo_object_mock.set_bios_settings.side_effect = ilo_error.IloError(
'err')
step = 'apply_configuration'
self.assertRaises(exception.NodeCleaningFailure,
task.driver.bios._execute_pre_boot_bios_step,
task, step, data)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test__execute_pre_boot_bios_step_reset_bios_failed(
self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
data = {
"SET_A": "VAL_A",
"SET_B": "VAL_B",
"SET_C": "VAL_C",
"SET_D": "VAL_D"
}
ilo_object_mock = get_ilo_object_mock.return_value
ilo_object_mock.reset_bios_to_default.side_effect = (
ilo_error.IloError('err'))
step = 'factory_reset'
self.assertRaises(exception.NodeCleaningFailure,
task.driver.bios._execute_pre_boot_bios_step,
task, step, data)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test__execute_post_boot_bios_step_apply_configuration(
self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
driver_info = task.node.driver_internal_info
driver_info.update({'apply_bios': True})
task.node.driver_internal_info = driver_info
task.node.save()
ilo_object_mock = get_ilo_object_mock.return_value
step = 'apply_configuration'
task.driver.bios._execute_post_boot_bios_step(task, step)
driver_info = task.node.driver_internal_info
self.assertTrue('apply_bios' not in driver_info)
ilo_object_mock.get_bios_settings_result.assert_called_once_with()
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test__execute_post_boot_bios_step_factory_reset(
self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
driver_info = task.node.driver_internal_info
driver_info.update({'reset_bios': True})
task.node.driver_internal_info = driver_info
task.node.save()
ilo_object_mock = get_ilo_object_mock.return_value
step = 'factory_reset'
task.driver.bios._execute_post_boot_bios_step(task, step)
driver_info = task.node.driver_internal_info
self.assertTrue('reset_bios' not in driver_info)
ilo_object_mock.get_bios_settings_result.assert_called_once_with()
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test__execute_post_boot_bios_step_invalid(
self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
driver_info = task.node.driver_internal_info
driver_info.update({'apply_bios': True})
task.node.driver_internal_info = driver_info
task.node.save()
step = 'invalid_step'
self.assertRaises(exception.NodeCleaningFailure,
task.driver.bios._execute_post_boot_bios_step,
task, step)
self.assertTrue(
'apply_bios' not in task.node.driver_internal_info)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test__execute_post_boot_bios_step_iloobj_failed(
self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
driver_info = task.node.driver_internal_info
driver_info.update({'apply_bios': True})
task.node.driver_internal_info = driver_info
task.node.save()
get_ilo_object_mock.side_effect = exception.MissingParameterValue(
'err')
step = 'apply_configuration'
self.assertRaises(exception.NodeCleaningFailure,
task.driver.bios._execute_post_boot_bios_step,
task, step)
self.assertTrue(
'apply_bios' not in task.node.driver_internal_info)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test__execute_post_boot_bios_get_settings_error(
self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
driver_info = task.node.driver_internal_info
driver_info.update({'apply_bios': True})
task.node.driver_internal_info = driver_info
task.node.save()
ilo_object_mock = get_ilo_object_mock.return_value
step = 'apply_configuration'
mdobj = {
"name": task.driver.bios._execute_post_boot_bios_step,
"args": (task, step,)
}
self._test_ilo_error(ilo_error.IloCommandNotSupportedError,
[],
[], mdobj,
ilo_object_mock.get_bios_settings_result)
self.assertTrue(
'apply_bios' not in task.node.driver_internal_info)
@mock.patch.object(ilo_common, 'get_ilo_object', spec_set=True,
autospec=True)
def test__execute_post_boot_bios_get_settings_failed(
self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task:
driver_info = task.node.driver_internal_info
driver_info.update({'reset_bios': True})
task.node.driver_internal_info = driver_info
task.node.save()
ilo_object_mock = get_ilo_object_mock.return_value
ilo_object_mock.get_bios_settings_result.return_value = (
{'status': 'failed', 'message': 'Some data'})
step = 'factory_reset'
self.assertRaises(exception.NodeCleaningFailure,
task.driver.bios._execute_post_boot_bios_step,
task, step)
self.assertTrue(
'reset_bios' not in task.node.driver_internal_info)
@mock.patch.object(objects.BIOSSettingList, 'create') @mock.patch.object(objects.BIOSSettingList, 'create')
@mock.patch.object(objects.BIOSSettingList, 'save') @mock.patch.object(objects.BIOSSettingList, 'save')
@ -205,7 +420,7 @@ class IloBiosTestCase(test_common.BaseIloTest):
"SET_D": True "SET_D": True
} }
ilo_object_mock.get_pending_bios_settings.return_value = settings ilo_object_mock.get_current_bios_settings.return_value = settings
expected_bios_settings = [ expected_bios_settings = [
{"name": "SET_A", "value": True}, {"name": "SET_A", "value": True},
{"name": "SET_B", "value": True}, {"name": "SET_B", "value": True},
@ -230,7 +445,7 @@ class IloBiosTestCase(test_common.BaseIloTest):
) )
sync_node_mock.return_value = all_settings sync_node_mock.return_value = all_settings
task.driver.bios.cache_bios_settings(task) task.driver.bios.cache_bios_settings(task)
ilo_object_mock.get_pending_bios_settings.assert_called_once_with() ilo_object_mock.get_current_bios_settings.assert_called_once_with()
actual_arg = sorted(sync_node_mock.call_args[0][2], actual_arg = sorted(sync_node_mock.call_args[0][2],
key=lambda x: x.get("name")) key=lambda x: x.get("name"))
expected_arg = sorted(expected_bios_settings, expected_arg = sorted(expected_bios_settings,
@ -244,25 +459,29 @@ class IloBiosTestCase(test_common.BaseIloTest):
delete_mock.assert_called_once_with( delete_mock.assert_called_once_with(
self.context, task.node.id, del_names) self.context, task.node.id, del_names)
def test_cache_bios_settings_missing_parameter(self): @mock.patch.object(ilo_common, 'get_ilo_object', autospec=True)
def test_cache_bios_settings_missing_parameter(self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mdobj = { mdobj = {
"name": task.driver.bios.cache_bios_settings, "name": task.driver.bios.cache_bios_settings,
"args": (task,) "args": (task,)
} }
self._test_ilo_error("missing_parameter", self._test_ilo_error(exception.MissingParameterValue,
["get_pending_bios_settings"], mdobj) [],
[], mdobj, get_ilo_object_mock)
def test_cache_bios_settings_invalid_parameter(self): @mock.patch.object(ilo_common, 'get_ilo_object', autospec=True)
def test_cache_bios_settings_invalid_parameter(self, get_ilo_object_mock):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
mdobj = { mdobj = {
"name": task.driver.bios.cache_bios_settings, "name": task.driver.bios.cache_bios_settings,
"args": (task,) "args": (task,)
} }
self._test_ilo_error("invalid_parameter", self._test_ilo_error(exception.InvalidParameterValue,
["get_pending_bios_settings"], mdobj) [],
[], mdobj, get_ilo_object_mock)
@mock.patch.object(ilo_common, 'get_ilo_object', autospec=True) @mock.patch.object(ilo_common, 'get_ilo_object', autospec=True)
def test_cache_bios_settings_with_ilo_error(self, get_ilo_object_mock): def test_cache_bios_settings_with_ilo_error(self, get_ilo_object_mock):
@ -270,10 +489,15 @@ class IloBiosTestCase(test_common.BaseIloTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
ilo_object_mock = get_ilo_object_mock.return_value ilo_object_mock = get_ilo_object_mock.return_value
exc = ilo_error.IloError('error') mdobj = {
ilo_object_mock.get_pending_bios_settings.side_effect = exc "name": task.driver.bios.cache_bios_settings,
self.assertRaises(exception.NodeCleaningFailure, "args": (task,)
task.driver.bios.cache_bios_settings, task) }
self._test_ilo_error(ilo_error.IloError,
[],
[],
mdobj,
ilo_object_mock.get_current_bios_settings)
@mock.patch.object(ilo_common, 'get_ilo_object', autospec=True) @mock.patch.object(ilo_common, 'get_ilo_object', autospec=True)
def test_cache_bios_settings_with_unknown_error(self, get_ilo_object_mock): def test_cache_bios_settings_with_unknown_error(self, get_ilo_object_mock):
@ -281,7 +505,13 @@ class IloBiosTestCase(test_common.BaseIloTest):
with task_manager.acquire(self.context, self.node.uuid, with task_manager.acquire(self.context, self.node.uuid,
shared=True) as task: shared=True) as task:
ilo_object_mock = get_ilo_object_mock.return_value ilo_object_mock = get_ilo_object_mock.return_value
exc = ilo_error.IloCommandNotSupportedError('error')
ilo_object_mock.get_pending_bios_settings.side_effect = exc mdobj = {
self.assertRaises(exception.NodeCleaningFailure, "name": task.driver.bios.cache_bios_settings,
task.driver.bios.cache_bios_settings, task) "args": (task,)
}
self._test_ilo_error(ilo_error.IloCommandNotSupportedError,
[],
[],
mdobj,
ilo_object_mock.get_current_bios_settings)

View File

@ -0,0 +1,8 @@
---
fixes:
- |
Makes all ``ilo`` driver BIOS interface clean steps as asynchronous. This is
required to ensure the settings on the baremetal node are consistent with
the settings stored in the database irrespective of the node clean step
status. Refer bug `2004066
<https://storyboard.openstack.org/#!/story/2004066>`_ for details.