summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDao Cong Tien <tiendc@vn.fujitsu.com>2017-01-04 09:22:30 +0700
committerDao Cong Tien <tiendc@vn.fujitsu.com>2017-06-29 10:06:20 +0000
commite7664a161d3a99d5ddd351489910d535bdec3752 (patch)
tree1d4170148036a90fb23bfd6568e5d157f8997a1a
parent352120378f471214d51a71db9fbcb7586b9a5457 (diff)
downloadironic-e7664a161d3a99d5ddd351489910d535bdec3752.tar.gz
Adds clean step 'restore_irmc_bios_config' to iRMC drivers
- Adds new boot interface 'irmc-pxe'. Deprecates 'pxe' boot interface from using with hardware type 'irmc'. - Adds functions backup_bios_config and restore_bios_config to iRMC management interface for implementing the BIOS BACKUP/RESTORE mechanism supporting iRMC S4 hardware. The function backup_bios_config() will be called automatically before deploying. - Adds clean step restore_irmc_bios_config to restore BIOS config for a node during automatic cleaning. Change-Id: I04aa5bc2f5e287e048d0b52fee123e53ae2eaa99 Partial-Bug: #1639688
-rw-r--r--etc/ironic/ironic.conf.sample4
-rw-r--r--ironic/conf/irmc.py3
-rw-r--r--ironic/drivers/irmc.py5
-rw-r--r--ironic/drivers/modules/irmc/boot.py38
-rw-r--r--ironic/drivers/modules/irmc/management.py126
-rw-r--r--ironic/drivers/pxe.py3
-rw-r--r--ironic/tests/unit/drivers/modules/irmc/test_boot.py50
-rw-r--r--ironic/tests/unit/drivers/modules/irmc/test_management.py136
-rw-r--r--ironic/tests/unit/drivers/test_pxe.py3
-rw-r--r--ironic/tests/unit/drivers/third_party_driver_mock_specs.py5
-rw-r--r--ironic/tests/unit/drivers/third_party_driver_mocks.py2
-rw-r--r--releasenotes/notes/irmc-add-clean-step-reset-bios-config-a8bed625670b7fdf.yaml16
-rw-r--r--setup.cfg1
13 files changed, 373 insertions, 19 deletions
diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample
index f69275851..2436e84c5 100644
--- a/etc/ironic/ironic.conf.sample
+++ b/etc/ironic/ironic.conf.sample
@@ -1965,6 +1965,10 @@
# SNMP polling interval in seconds (integer value)
#snmp_polling_interval = 10
+# Priority for restore_irmc_bios_config clean step. (integer
+# value)
+#clean_priority_restore_irmc_bios_config = 0
+
[ironic_lib]
diff --git a/ironic/conf/irmc.py b/ironic/conf/irmc.py
index ac590339b..bc558d03c 100644
--- a/ironic/conf/irmc.py
+++ b/ironic/conf/irmc.py
@@ -69,6 +69,9 @@ opts = [
cfg.IntOpt('snmp_polling_interval',
default=10,
help='SNMP polling interval in seconds'),
+ cfg.IntOpt('clean_priority_restore_irmc_bios_config',
+ default=0,
+ help=_('Priority for restore_irmc_bios_config clean step.')),
]
diff --git a/ironic/drivers/irmc.py b/ironic/drivers/irmc.py
index f721c3109..10fe664a6 100644
--- a/ironic/drivers/irmc.py
+++ b/ironic/drivers/irmc.py
@@ -91,7 +91,10 @@ class IRMCHardware(generic.GenericHardware):
@property
def supported_boot_interfaces(self):
"""List of supported boot interfaces."""
- return [boot.IRMCVirtualMediaBoot, pxe.PXEBoot]
+ # NOTE: Support for pxe boot is deprecated, and will be
+ # removed from the list in the future.
+ return [boot.IRMCVirtualMediaBoot, boot.IRMCPXEBoot,
+ pxe.PXEBoot]
@property
def supported_console_interfaces(self):
diff --git a/ironic/drivers/modules/irmc/boot.py b/ironic/drivers/modules/irmc/boot.py
index 1a8580925..78f9a3253 100644
--- a/ironic/drivers/modules/irmc/boot.py
+++ b/ironic/drivers/modules/irmc/boot.py
@@ -36,6 +36,8 @@ from ironic.conf import CONF
from ironic.drivers import base
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.irmc import common as irmc_common
+from ironic.drivers.modules.irmc import management as irmc_management
+from ironic.drivers.modules import pxe
scci = importutils.try_import('scciclient.irmc.scci')
@@ -587,6 +589,11 @@ class IRMCVirtualMediaBoot(base.BootInterface):
task.node.provision_state != states.CLEANING):
return
+ # NOTE(tiendc): Before deploying, we need to backup BIOS config
+ # as the data will be used later when cleaning.
+ if task.node.provision_state == states.DEPLOYING:
+ irmc_management.backup_bios_config(task)
+
deploy_nic_mac = deploy_utils.get_single_nic_with_vif_port_id(task)
ramdisk_params['BOOTIF'] = deploy_nic_mac
@@ -654,3 +661,34 @@ class IRMCVirtualMediaBoot(base.BootInterface):
task, node.driver_internal_info['irmc_boot_iso'])
manager_utils.node_set_boot_device(task, boot_devices.CDROM,
persistent=True)
+
+
+class IRMCPXEBoot(pxe.PXEBoot):
+ """iRMC PXE boot."""
+
+ @METRICS.timer('IRMCPXEBoot.prepare_ramdisk')
+ def prepare_ramdisk(self, task, ramdisk_params):
+ """Prepares the boot of Ironic ramdisk using PXE.
+
+ This method prepares the boot of the deploy kernel/ramdisk after
+ reading relevant information from the node's driver_info and
+ instance_info.
+
+ :param task: a task from TaskManager.
+ :param ramdisk_params: the parameters to be passed to the ramdisk.
+ pxe driver passes these parameters as kernel command-line
+ arguments.
+ :returns: None
+ :raises: MissingParameterValue, if some information is missing in
+ node's driver_info or instance_info.
+ :raises: InvalidParameterValue, if some information provided is
+ invalid.
+ :raises: IronicException, if some power or set boot device
+ operation failed on the node.
+ """
+ # NOTE(tiendc): Before deploying, we need to backup BIOS config
+ # as the data will be used later when cleaning.
+ if task.node.provision_state == states.DEPLOYING:
+ irmc_management.backup_bios_config(task)
+
+ super(IRMCPXEBoot, self).prepare_ramdisk(task, ramdisk_params)
diff --git a/ironic/drivers/modules/irmc/management.py b/ironic/drivers/modules/irmc/management.py
index 6f774b849..bddb97090 100644
--- a/ironic/drivers/modules/irmc/management.py
+++ b/ironic/drivers/modules/irmc/management.py
@@ -14,6 +14,7 @@
"""
iRMC Management Driver
"""
+
from ironic_lib import metrics_utils
from oslo_log import log as logging
from oslo_utils import importutils
@@ -21,14 +22,19 @@ from oslo_utils import importutils
from ironic.common import boot_devices
from ironic.common import exception
from ironic.common.i18n import _
+from ironic.common import states
from ironic.conductor import task_manager
+from ironic.conductor import utils as manager_utils
+from ironic import conf
+from ironic.drivers import base
from ironic.drivers.modules import ipmitool
from ironic.drivers.modules.irmc import common as irmc_common
from ironic.drivers import utils as driver_utils
-scci = importutils.try_import('scciclient.irmc.scci')
+irmc = importutils.try_import('scciclient.irmc')
LOG = logging.getLogger(__name__)
+CONF = conf.CONF
METRICS = metrics_utils.get_metrics_logger(__name__)
@@ -61,12 +67,12 @@ def _get_sensors_data(task):
try:
report = irmc_common.get_irmc_report(task.node)
- sensor = scci.get_sensor_data(report)
+ sensor = irmc.scci.get_sensor_data(report)
except (exception.InvalidParameterValue,
exception.MissingParameterValue,
- scci.SCCIInvalidInputError,
- scci.SCCIClientError) as e:
+ irmc.scci.SCCIInvalidInputError,
+ irmc.scci.SCCIClientError) as e:
LOG.error("SCCI get sensor data failed for node %(node_id)s "
"with the following error: %(error)s",
{'node_id': task.node.uuid, 'error': e})
@@ -106,6 +112,96 @@ def _get_sensors_data(task):
return sensors_data
+def backup_bios_config(task):
+ """Backup BIOS config from a node.
+
+ :param task: a TaskManager instance containing the node to act on.
+ :raises: IRMCOperationError on failure.
+ """
+ node_uuid = task.node.uuid
+
+ # Skip this operation if the clean step 'restore' is disabled
+ if CONF.irmc.clean_priority_restore_irmc_bios_config == 0:
+ LOG.debug('Skipped the operation backup_BIOS_config for node %s '
+ 'as the clean step restore_BIOS_config is disabled.',
+ node_uuid)
+ return
+
+ irmc_info = irmc_common.parse_driver_info(task.node)
+
+ try:
+ # Backup bios config
+ result = irmc.elcm.backup_bios_config(irmc_info)
+ except irmc.scci.SCCIError as e:
+ LOG.error('Failed to backup BIOS config for node %(node)s. '
+ 'Error: %(error)s', {'node': node_uuid, 'error': e})
+ raise exception.IRMCOperationError(operation='backup BIOS config',
+ error=e)
+
+ # Save bios config into the driver_internal_info
+ internal_info = task.node.driver_internal_info
+ internal_info['irmc_bios_config'] = result['bios_config']
+ task.node.driver_internal_info = internal_info
+ task.node.save()
+
+ LOG.info('BIOS config is backed up successfully for node %s',
+ node_uuid)
+
+ # NOTE(tiendc): When the backup operation done, server is automatically
+ # shutdown. However, this function is called right before the method
+ # task.driver.deploy() that will trigger a reboot. So, we don't need
+ # to power on the server at this point.
+
+
+def _restore_bios_config(task):
+ """Restore BIOS config to a node.
+
+ :param task: a TaskManager instance containing the node to act on.
+ :raises: IRMCOperationError if the operation fails.
+ """
+ node_uuid = task.node.uuid
+
+ # Get bios config stored in the node object
+ bios_config = task.node.driver_internal_info.get('irmc_bios_config')
+ if not bios_config:
+ LOG.info('Skipped operation "restore BIOS config" on node %s '
+ 'as the backup data not found.', node_uuid)
+ return
+
+ def _remove_bios_config(task):
+ """Remove backup bios config from the node."""
+ internal_info = task.node.driver_internal_info
+ internal_info.pop('irmc_bios_config', None)
+ task.node.driver_internal_info = internal_info
+ task.node.save()
+
+ irmc_info = irmc_common.parse_driver_info(task.node)
+
+ try:
+ # Restore bios config
+ irmc.elcm.restore_bios_config(irmc_info, bios_config)
+ except irmc.scci.SCCIError as e:
+ # If the input bios config is not correct or corrupted, then
+ # we should remove it from the node object.
+ if isinstance(e, irmc.scci.SCCIInvalidInputError):
+ _remove_bios_config(task)
+
+ LOG.error('Failed to restore BIOS config on node %(node)s. '
+ 'Error: %(error)s', {'node': node_uuid, 'error': e})
+ raise exception.IRMCOperationError(operation='restore BIOS config',
+ error=e)
+
+ # Remove the backup data after restoring
+ _remove_bios_config(task)
+
+ LOG.info('BIOS config is restored successfully on node %s',
+ node_uuid)
+
+ # Change power state to ON as server is automatically
+ # shutdown after the operation.
+ manager_utils.node_power_action(task, states.POWER_ON)
+
+
class IRMCManagement(ipmitool.IPMIManagement):
def get_properties(self):
@@ -249,9 +345,25 @@ class IRMCManagement(ipmitool.IPMIManagement):
node = task.node
irmc_client = irmc_common.get_irmc_client(node)
try:
- irmc_client(scci.POWER_RAISE_NMI)
- except scci.SCCIClientError as err:
+ irmc_client(irmc.scci.POWER_RAISE_NMI)
+ except irmc.scci.SCCIClientError as err:
LOG.error('iRMC Inject NMI failed for node %(node)s: %(err)s.',
{'node': node.uuid, 'err': err})
raise exception.IRMCOperationError(
- operation=scci.POWER_RAISE_NMI, error=err)
+ operation=irmc.scci.POWER_RAISE_NMI, error=err)
+
+ @METRICS.timer('IRMCManagement.restore_irmc_bios_config')
+ @base.clean_step(
+ priority=CONF.irmc.clean_priority_restore_irmc_bios_config)
+ def restore_irmc_bios_config(self, task):
+ """Restore BIOS config for a node.
+
+ :param task: a task from TaskManager.
+ :raises: NodeCleaningFailure, on failure to execute step.
+ :returns: None.
+ """
+ try:
+ _restore_bios_config(task)
+ except exception.IRMCOperationError as e:
+ raise exception.NodeCleaningFailure(node=task.node.uuid,
+ reason=e)
diff --git a/ironic/drivers/pxe.py b/ironic/drivers/pxe.py
index 596de3ffd..f21db501e 100644
--- a/ironic/drivers/pxe.py
+++ b/ironic/drivers/pxe.py
@@ -34,6 +34,7 @@ from ironic.drivers.modules.ilo import power as ilo_power
from ironic.drivers.modules.ilo import vendor as ilo_vendor
from ironic.drivers.modules import inspector
from ironic.drivers.modules import ipmitool
+from ironic.drivers.modules.irmc import boot as irmc_boot
from ironic.drivers.modules.irmc import inspect as irmc_inspect
from ironic.drivers.modules.irmc import management as irmc_management
from ironic.drivers.modules.irmc import power as irmc_power
@@ -142,7 +143,7 @@ class PXEAndIRMCDriver(base.BaseDriver):
reason=_("Unable to import python-scciclient library"))
self.power = irmc_power.IRMCPower()
self.console = ipmitool.IPMIShellinaboxConsole()
- self.boot = pxe.PXEBoot()
+ self.boot = irmc_boot.IRMCPXEBoot()
self.deploy = iscsi_deploy.ISCSIDeploy()
self.management = irmc_management.IRMCManagement()
self.inspect = irmc_inspect.IRMCInspect()
diff --git a/ironic/tests/unit/drivers/modules/irmc/test_boot.py b/ironic/tests/unit/drivers/modules/irmc/test_boot.py
index d515baa4d..1cbbf1d89 100644
--- a/ironic/tests/unit/drivers/modules/irmc/test_boot.py
+++ b/ironic/tests/unit/drivers/modules/irmc/test_boot.py
@@ -36,12 +36,13 @@ from ironic.conductor import utils as manager_utils
from ironic.drivers.modules import deploy_utils
from ironic.drivers.modules.irmc import boot as irmc_boot
from ironic.drivers.modules.irmc import common as irmc_common
+from ironic.drivers.modules.irmc import management as irmc_management
+from ironic.drivers.modules import pxe
from ironic.tests.unit.conductor import mgr_utils
from ironic.tests.unit.db import base as db_base
from ironic.tests.unit.db import utils as db_utils
from ironic.tests.unit.objects import utils as obj_utils
-
if six.PY3:
import io
file = io.BytesIO
@@ -896,13 +897,16 @@ class IRMCVirtualMediaBootTestCase(db_base.DbTestCase):
validate_prop_mock.assert_called_once_with(
task.context, d_info, ['kernel', 'ramdisk'])
+ @mock.patch.object(irmc_management, 'backup_bios_config', spec_set=True,
+ autospec=True)
@mock.patch.object(irmc_boot, '_setup_deploy_iso',
spec_set=True, autospec=True)
@mock.patch.object(deploy_utils, 'get_single_nic_with_vif_port_id',
spec_set=True, autospec=True)
def _test_prepare_ramdisk(self,
get_single_nic_with_vif_port_id_mock,
- _setup_deploy_iso_mock):
+ _setup_deploy_iso_mock,
+ mock_backup_bios):
instance_info = self.node.instance_info
instance_info['irmc_boot_iso'] = 'glance://abcdef'
instance_info['image_source'] = '6b2f0c0c-79e8-4db6-842e-43c9764204af'
@@ -922,6 +926,9 @@ class IRMCVirtualMediaBootTestCase(db_base.DbTestCase):
task, expected_ramdisk_opts)
self.assertEqual('glance://abcdef',
self.node.instance_info['irmc_boot_iso'])
+ provision_state = task.node.provision_state
+ self.assertEqual(1 if provision_state == states.DEPLOYING else 0,
+ mock_backup_bios.call_count)
def test_prepare_ramdisk_glance_image_deploying(self):
self.node.provision_state = states.DEPLOYING
@@ -1051,3 +1058,42 @@ class IRMCVirtualMediaBootTestCase(db_base.DbTestCase):
cfg.CONF.set_override('remote_image_share_type', 'nfs', 'irmc')
self.assertRaises(ValueError, cfg.CONF.set_override,
'remote_image_share_type', 'fake', 'irmc')
+
+
+class IRMCPXEBootTestCase(db_base.DbTestCase):
+
+ def setUp(self):
+ super(IRMCPXEBootTestCase, self).setUp()
+ mgr_utils.mock_the_extension_manager(driver="pxe_irmc")
+ self.node = obj_utils.create_test_node(
+ self.context, driver='pxe_irmc', driver_info=INFO_DICT)
+
+ @mock.patch.object(irmc_management, 'backup_bios_config', spec_set=True,
+ autospec=True)
+ @mock.patch.object(pxe.PXEBoot, 'prepare_ramdisk', spec_set=True,
+ autospec=True)
+ def test_prepare_ramdisk_with_backup_bios(self, mock_parent_prepare,
+ mock_backup_bios):
+ self.node.provision_state = states.DEPLOYING
+ self.node.save()
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ task.driver.boot.prepare_ramdisk(task, {})
+ mock_backup_bios.assert_called_once_with(task)
+ mock_parent_prepare.assert_called_once_with(
+ task.driver.boot, task, {})
+
+ @mock.patch.object(irmc_management, 'backup_bios_config', spec_set=True,
+ autospec=True)
+ @mock.patch.object(pxe.PXEBoot, 'prepare_ramdisk', spec_set=True,
+ autospec=True)
+ def test_prepare_ramdisk_without_backup_bios(self, mock_parent_prepare,
+ mock_backup_bios):
+ self.node.provision_state = states.CLEANING
+ self.node.save()
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ task.driver.boot.prepare_ramdisk(task, {})
+ self.assertFalse(mock_backup_bios.called)
+ mock_parent_prepare.assert_called_once_with(
+ task.driver.boot, task, {})
diff --git a/ironic/tests/unit/drivers/modules/irmc/test_management.py b/ironic/tests/unit/drivers/modules/irmc/test_management.py
index 48da7ee9e..3a63adc5a 100644
--- a/ironic/tests/unit/drivers/modules/irmc/test_management.py
+++ b/ironic/tests/unit/drivers/modules/irmc/test_management.py
@@ -24,10 +24,13 @@ import mock
from ironic.common import boot_devices
from ironic.common import driver_factory
from ironic.common import exception
+from ironic.common import states
from ironic.conductor import task_manager
+from ironic.conductor import utils as manager_utils
from ironic.drivers.modules import ipmitool
from ironic.drivers.modules.irmc import common as irmc_common
from ironic.drivers.modules.irmc import management as irmc_management
+from ironic.drivers.modules.irmc import power as irmc_power
from ironic.drivers import utils as driver_utils
from ironic.tests.unit.conductor import mgr_utils
from ironic.tests.unit.db import base as db_base
@@ -39,6 +42,116 @@ from ironic.tests.unit.objects import utils as obj_utils
INFO_DICT = db_utils.get_test_irmc_info()
+@mock.patch.object(irmc_management.irmc, 'elcm',
+ spec_set=mock_specs.SCCICLIENT_IRMC_ELCM_SPEC)
+@mock.patch.object(manager_utils, 'node_power_action',
+ specset=True, autospec=True)
+@mock.patch.object(irmc_power.IRMCPower, 'get_power_state',
+ return_value=states.POWER_ON,
+ specset=True, autospec=True)
+class IRMCManagementFunctionsTestCase(db_base.DbTestCase):
+ def setUp(self):
+ super(IRMCManagementFunctionsTestCase, self).setUp()
+ driver_info = INFO_DICT
+
+ mgr_utils.mock_the_extension_manager(driver="fake_irmc")
+ self.driver = driver_factory.get_driver("fake_irmc")
+ self.node = obj_utils.create_test_node(self.context,
+ driver='fake_irmc',
+ driver_info=driver_info)
+ self.info = irmc_common.parse_driver_info(self.node)
+
+ irmc_management.irmc.scci.SCCIError = Exception
+ irmc_management.irmc.scci.SCCIInvalidInputError = ValueError
+
+ def test_backup_bios_config(self, mock_get_power, mock_power_action,
+ mock_elcm):
+ self.config(clean_priority_restore_irmc_bios_config=10, group='irmc')
+ bios_config = {'Server': {'System': {'BiosConfig': {'key1': 'val1'}}}}
+ mock_elcm.backup_bios_config.return_value = {
+ 'bios_config': bios_config}
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ irmc_management.backup_bios_config(task)
+
+ self.assertEqual(bios_config, task.node.driver_internal_info[
+ 'irmc_bios_config'])
+ self.assertEqual(1, mock_elcm.backup_bios_config.call_count)
+
+ def test_backup_bios_config_skipped(self, mock_get_power,
+ mock_power_action, mock_elcm):
+ self.config(clean_priority_restore_irmc_bios_config=0, group='irmc')
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ irmc_management.backup_bios_config(task)
+
+ self.assertNotIn('irmc_bios_config',
+ task.node.driver_internal_info)
+ self.assertFalse(mock_elcm.backup_bios_config.called)
+
+ def test_backup_bios_config_failed(self, mock_get_power,
+ mock_power_action, mock_elcm):
+ self.config(clean_priority_restore_irmc_bios_config=10, group='irmc')
+ mock_elcm.backup_bios_config.side_effect = Exception
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ self.assertRaises(exception.IRMCOperationError,
+ irmc_management.backup_bios_config,
+ task)
+ self.assertNotIn('irmc_bios_config',
+ task.node.driver_internal_info)
+ self.assertEqual(1, mock_elcm.backup_bios_config.call_count)
+
+ def test__restore_bios_config(self, mock_get_power, mock_power_action,
+ mock_elcm):
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ # Set bios data for the node info
+ task.node.driver_internal_info['irmc_bios_config'] = 'data'
+ irmc_management._restore_bios_config(task)
+
+ self.assertEqual(1, mock_elcm.restore_bios_config.call_count)
+
+ def test__restore_bios_config_failed(self, mock_get_power,
+ mock_power_action,
+ mock_elcm):
+ mock_elcm.restore_bios_config.side_effect = Exception
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ # Set bios data for the node info
+ task.node.driver_internal_info['irmc_bios_config'] = 'data'
+
+ self.assertRaises(exception.IRMCOperationError,
+ irmc_management._restore_bios_config,
+ task)
+ # Backed up BIOS config is still in the node object
+ self.assertEqual('data', task.node.driver_internal_info[
+ 'irmc_bios_config'])
+ self.assertTrue(mock_elcm.restore_bios_config.called)
+
+ def test__restore_bios_config_corrupted(self, mock_get_power,
+ mock_power_action,
+ mock_elcm):
+ mock_elcm.restore_bios_config.side_effect = \
+ irmc_management.irmc.scci.SCCIInvalidInputError
+
+ with task_manager.acquire(self.context, self.node.uuid,
+ shared=False) as task:
+ # Set bios data for the node info
+ task.node.driver_internal_info['irmc_bios_config'] = 'data'
+
+ self.assertRaises(exception.IRMCOperationError,
+ irmc_management._restore_bios_config,
+ task)
+ # Backed up BIOS config is removed from the node object
+ self.assertNotIn('irmc_bios_config',
+ task.node.driver_internal_info)
+ self.assertTrue(mock_elcm.restore_bios_config.called)
+
+
class IRMCManagementTestCase(db_base.DbTestCase):
def setUp(self):
super(IRMCManagementTestCase, self).setUp()
@@ -259,7 +372,7 @@ class IRMCManagementTestCase(db_base.DbTestCase):
task,
"unknown")
- @mock.patch.object(irmc_management, 'scci',
+ @mock.patch.object(irmc_management.irmc, 'scci',
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
@mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
autospec=True)
@@ -307,7 +420,7 @@ class IRMCManagementTestCase(db_base.DbTestCase):
}
self.assertEqual(expected, sensor_dict)
- @mock.patch.object(irmc_management, 'scci',
+ @mock.patch.object(irmc_management.irmc, 'scci',
spec_set=mock_specs.SCCICLIENT_IRMC_SCCI_SPEC)
@mock.patch.object(irmc_common, 'get_irmc_report', spec_set=True,
autospec=True)
@@ -350,8 +463,8 @@ class IRMCManagementTestCase(db_base.DbTestCase):
get_irmc_report_mock.side_effect = exception.InvalidParameterValue(
"Fake Error")
- irmc_management.scci.SCCIInvalidInputError = Exception
- irmc_management.scci.SCCIClientError = Exception
+ irmc_management.irmc.scci.SCCIInvalidInputError = Exception
+ irmc_management.irmc.scci.SCCIClientError = Exception
with task_manager.acquire(self.context, self.node.uuid) as task:
task.node.driver_info['irmc_sensor_method'] = 'scci'
@@ -373,7 +486,7 @@ class IRMCManagementTestCase(db_base.DbTestCase):
self.driver.management.inject_nmi(task)
irmc_client.assert_called_once_with(
- irmc_management.scci.POWER_RAISE_NMI)
+ irmc_management.irmc.scci.POWER_RAISE_NMI)
self.assertFalse(mock_log.called)
@mock.patch.object(irmc_management.LOG, 'error', spec_set=True,
@@ -384,7 +497,7 @@ class IRMCManagementTestCase(db_base.DbTestCase):
mock_log):
irmc_client = mock_get_irmc_client.return_value
irmc_client.side_effect = Exception()
- irmc_management.scci.SCCIClientError = Exception
+ irmc_management.irmc.scci.SCCIClientError = Exception
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.IRMCOperationError,
@@ -392,5 +505,14 @@ class IRMCManagementTestCase(db_base.DbTestCase):
task)
irmc_client.assert_called_once_with(
- irmc_management.scci.POWER_RAISE_NMI)
+ irmc_management.irmc.scci.POWER_RAISE_NMI)
self.assertTrue(mock_log.called)
+
+ @mock.patch.object(irmc_management, '_restore_bios_config',
+ spec_set=True, autospec=True)
+ def test_management_interface_restore_irmc_bios_config(self,
+ mock_restore_bios):
+ with task_manager.acquire(self.context, self.node.uuid) as task:
+ result = task.driver.management.restore_irmc_bios_config(task)
+ self.assertIsNone(result)
+ mock_restore_bios.assert_called_once_with(task)
diff --git a/ironic/tests/unit/drivers/test_pxe.py b/ironic/tests/unit/drivers/test_pxe.py
index 4378ab38f..4471cf002 100644
--- a/ironic/tests/unit/drivers/test_pxe.py
+++ b/ironic/tests/unit/drivers/test_pxe.py
@@ -29,6 +29,7 @@ from ironic.drivers.modules.ilo import management as ilo_management
from ironic.drivers.modules.ilo import power as ilo_power
from ironic.drivers.modules.ilo import vendor as ilo_vendor
from ironic.drivers.modules import ipmitool
+from ironic.drivers.modules.irmc import boot as irmc_boot
from ironic.drivers.modules.irmc import management as irmc_management
from ironic.drivers.modules.irmc import power as irmc_power
from ironic.drivers.modules import iscsi_deploy
@@ -107,7 +108,7 @@ class PXEDriversTestCase(testtools.TestCase):
self.assertIsInstance(driver.power, irmc_power.IRMCPower)
self.assertIsInstance(driver.console, ipmitool.IPMIShellinaboxConsole)
- self.assertIsInstance(driver.boot, pxe_module.PXEBoot)
+ self.assertIsInstance(driver.boot, irmc_boot.IRMCPXEBoot)
self.assertIsInstance(driver.deploy, iscsi_deploy.ISCSIDeploy)
self.assertIsInstance(driver.management,
irmc_management.IRMCManagement)
diff --git a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py
index 8a51c89fa..a3e9b0183 100644
--- a/ironic/tests/unit/drivers/third_party_driver_mock_specs.py
+++ b/ironic/tests/unit/drivers/third_party_driver_mock_specs.py
@@ -91,6 +91,7 @@ SCCICLIENT_IRMC_SCCI_SPEC = (
'UNMOUNT_CD',
'MOUNT_FD',
'UNMOUNT_FD',
+ 'SCCIError',
'SCCIClientError',
'SCCIInvalidInputError',
'get_share_type',
@@ -101,6 +102,10 @@ SCCICLIENT_IRMC_SCCI_SPEC = (
'get_virtual_fd_set_params_cmd',
'get_essential_properties',
)
+SCCICLIENT_IRMC_ELCM_SPEC = (
+ 'backup_bios_config',
+ 'restore_bios_config',
+)
ONEVIEWCLIENT_SPEC = (
'client',
diff --git a/ironic/tests/unit/drivers/third_party_driver_mocks.py b/ironic/tests/unit/drivers/third_party_driver_mocks.py
index 0cb3ebcbb..31fec78b7 100644
--- a/ironic/tests/unit/drivers/third_party_driver_mocks.py
+++ b/ironic/tests/unit/drivers/third_party_driver_mocks.py
@@ -164,6 +164,8 @@ if not scciclient:
UNMOUNT_CD=mock.sentinel.UNMOUNT_CD,
MOUNT_FD=mock.sentinel.MOUNT_FD,
UNMOUNT_FD=mock.sentinel.UNMOUNT_FD)
+ sys.modules['scciclient.irmc.elcm'] = mock.MagicMock(
+ spec_set=mock_specs.SCCICLIENT_IRMC_ELCM_SPEC)
# if anything has loaded the iRMC driver yet, reload it now that the
diff --git a/releasenotes/notes/irmc-add-clean-step-reset-bios-config-a8bed625670b7fdf.yaml b/releasenotes/notes/irmc-add-clean-step-reset-bios-config-a8bed625670b7fdf.yaml
new file mode 100644
index 000000000..36325b66f
--- /dev/null
+++ b/releasenotes/notes/irmc-add-clean-step-reset-bios-config-a8bed625670b7fdf.yaml
@@ -0,0 +1,16 @@
+---
+features:
+ - Adds new boot interface named ``irmc-pxe``.
+ - Adds clean step ``restore_irmc_bios_config`` to restore BIOS config
+ for a node during automatic cleaning.
+upgrade:
+ - Adds new configuration option
+ ``[irmc]clean_priority_restore_irmc_bios_config``, which
+ enables setting priority for the clean step. Default value for
+ this option is 0, which means the clean step is disabled.
+deprecations:
+ - Deprecates the boot interface ``pxe`` from using with hardware type
+ ``irmc``. It is recommended for operator to switch to use the boot
+ iterface ``irmc-pxe`` as soon as possible. Using the interface
+ ``pxe`` with ``irmc`` drivers will cause the reset bios feature
+ not to work even the priority option is set to enable.
diff --git a/setup.cfg b/setup.cfg
index 4022041ac..a55e8e287 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -86,6 +86,7 @@ ironic.hardware.interfaces.boot =
fake = ironic.drivers.modules.fake:FakeBoot
ilo-pxe = ironic.drivers.modules.ilo.boot:IloPXEBoot
ilo-virtual-media = ironic.drivers.modules.ilo.boot:IloVirtualMediaBoot
+ irmc-pxe = ironic.drivers.modules.irmc.boot:IRMCPXEBoot
irmc-virtual-media = ironic.drivers.modules.irmc.boot:IRMCVirtualMediaBoot
pxe = ironic.drivers.modules.pxe:PXEBoot