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
This commit is contained in:
Dao Cong Tien 2017-01-04 09:22:30 +07:00
parent 352120378f
commit e7664a161d
13 changed files with 373 additions and 19 deletions

View File

@ -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]

View File

@ -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.')),
]

View File

@ -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):

View File

@ -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)

View File

@ -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)

View File

@ -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()

View File

@ -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, {})

View File

@ -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)

View File

@ -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)

View File

@ -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',

View File

@ -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

View File

@ -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.

View File

@ -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