When boot option is not persisted, set boot on next power on
If the server ipmi does not support the command 'chassis bootdev' with 'persistent' option', the boot device cannot be set persistently and will not be set for the 2nd boot. This change proposes to add a new parameter in node's driver_info to force set the boot device for each power on. Change-Id: I8d70c6292e3e013ccaf90f9ce4a616c8c916fe64 Closes-Bug: #1407820
This commit is contained in:
parent
ea09be5605
commit
cf9466d9ac
|
@ -37,6 +37,7 @@ from ironic.common import utils
|
|||
from ironic.conductor import task_manager
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import console_utils
|
||||
from ironic.drivers import utils as driver_utils
|
||||
|
||||
pyghmi = importutils.try_import('pyghmi')
|
||||
if pyghmi:
|
||||
|
@ -68,7 +69,16 @@ LOG = logging.getLogger(__name__)
|
|||
REQUIRED_PROPERTIES = {'ipmi_address': _("IP of the node's BMC. Required."),
|
||||
'ipmi_password': _("IPMI password. Required."),
|
||||
'ipmi_username': _("IPMI username. Required.")}
|
||||
COMMON_PROPERTIES = REQUIRED_PROPERTIES
|
||||
OPTIONAL_PROPERTIES = {
|
||||
'ipmi_force_boot_device': _("Whether Ironic should specify the boot "
|
||||
"device to the BMC each time the server "
|
||||
"is turned on, eg. because the BMC is not "
|
||||
"capable of remembering the selected boot "
|
||||
"device across power cycles; default value "
|
||||
"is False. Optional.")
|
||||
}
|
||||
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
|
||||
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
|
||||
CONSOLE_PROPERTIES = {
|
||||
'ipmi_terminal_port': _("node's UDP port to connect to. Only required for "
|
||||
"console access.")
|
||||
|
@ -102,6 +112,7 @@ def _parse_driver_info(node):
|
|||
bmc_info['address'] = info.get('ipmi_address')
|
||||
bmc_info['username'] = info.get('ipmi_username')
|
||||
bmc_info['password'] = info.get('ipmi_password')
|
||||
bmc_info['force_boot_device'] = info.get('ipmi_force_boot_device', False)
|
||||
|
||||
# get additional info
|
||||
bmc_info['uuid'] = node.uuid
|
||||
|
@ -337,6 +348,7 @@ class NativeIPMIPower(base.PowerInterface):
|
|||
driver_info = _parse_driver_info(task.node)
|
||||
|
||||
if pstate == states.POWER_ON:
|
||||
driver_utils.ensure_next_boot_device(task, driver_info)
|
||||
_power_on(driver_info)
|
||||
elif pstate == states.POWER_OFF:
|
||||
_power_off(driver_info)
|
||||
|
@ -358,6 +370,7 @@ class NativeIPMIPower(base.PowerInterface):
|
|||
"""
|
||||
|
||||
driver_info = _parse_driver_info(task.node)
|
||||
driver_utils.ensure_next_boot_device(task, driver_info)
|
||||
_reboot(driver_info)
|
||||
|
||||
|
||||
|
@ -409,6 +422,15 @@ class NativeIPMIManagement(base.ManagementInterface):
|
|||
if device not in self.get_supported_boot_devices():
|
||||
raise exception.InvalidParameterValue(_(
|
||||
"Invalid boot device %s specified.") % device)
|
||||
|
||||
if task.node.driver_info.get('ipmi_force_boot_device', False):
|
||||
driver_utils.force_persistent_boot(task,
|
||||
device,
|
||||
persistent)
|
||||
# Reset persistent to False, in case of BMC does not support
|
||||
# persistent or we do not have admin rights.
|
||||
persistent = False
|
||||
|
||||
driver_info = _parse_driver_info(task.node)
|
||||
try:
|
||||
ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
|
||||
|
@ -439,8 +461,19 @@ class NativeIPMIManagement(base.ManagementInterface):
|
|||
future boots or not, None if it is unknown.
|
||||
|
||||
"""
|
||||
driver_info = task.node.driver_info
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
if (driver_info.get('ipmi_force_boot_device', False) and
|
||||
driver_internal_info.get('persistent_boot_device') and
|
||||
driver_internal_info.get('is_next_boot_persistent', True)):
|
||||
return {
|
||||
'boot_device': driver_internal_info['persistent_boot_device'],
|
||||
'persistent': True
|
||||
}
|
||||
|
||||
driver_info = _parse_driver_info(task.node)
|
||||
response = {'boot_device': None}
|
||||
|
||||
try:
|
||||
ipmicmd = ipmi_command.Command(bmc=driver_info['address'],
|
||||
userid=driver_info['username'],
|
||||
|
|
|
@ -51,6 +51,7 @@ from ironic.common import utils
|
|||
from ironic.conductor import task_manager
|
||||
from ironic.drivers import base
|
||||
from ironic.drivers.modules import console_utils
|
||||
from ironic.drivers import utils as driver_utils
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
@ -92,6 +93,12 @@ OPTIONAL_PROPERTIES = {
|
|||
"to \"single\" or \"dual\". Optional."),
|
||||
'ipmi_protocol_version': _('the version of the IPMI protocol; default '
|
||||
'is "2.0". One of "1.5", "2.0". Optional.'),
|
||||
'ipmi_force_boot_device': _("Whether Ironic should specify the boot "
|
||||
"device to the BMC each time the server "
|
||||
"is turned on, eg. because the BMC is not "
|
||||
"capable of remembering the selected boot "
|
||||
"device across power cycles; default value "
|
||||
"is False. Optional.")
|
||||
}
|
||||
COMMON_PROPERTIES = REQUIRED_PROPERTIES.copy()
|
||||
COMMON_PROPERTIES.update(OPTIONAL_PROPERTIES)
|
||||
|
@ -247,6 +254,7 @@ def _parse_driver_info(node):
|
|||
target_channel = info.get('ipmi_target_channel')
|
||||
target_address = info.get('ipmi_target_address')
|
||||
protocol_version = str(info.get('ipmi_protocol_version', '2.0'))
|
||||
force_boot_device = info.get('ipmi_force_boot_device', False)
|
||||
|
||||
if protocol_version not in VALID_PROTO_VERSIONS:
|
||||
valid_versions = ', '.join(VALID_PROTO_VERSIONS)
|
||||
|
@ -322,6 +330,7 @@ def _parse_driver_info(node):
|
|||
'target_channel': target_channel,
|
||||
'target_address': target_address,
|
||||
'protocol_version': protocol_version,
|
||||
'force_boot_device': force_boot_device,
|
||||
}
|
||||
|
||||
|
||||
|
@ -722,6 +731,7 @@ class IPMIPower(base.PowerInterface):
|
|||
driver_info = _parse_driver_info(task.node)
|
||||
|
||||
if pstate == states.POWER_ON:
|
||||
driver_utils.ensure_next_boot_device(task, driver_info)
|
||||
state = _power_on(driver_info)
|
||||
elif pstate == states.POWER_OFF:
|
||||
state = _power_off(driver_info)
|
||||
|
@ -746,6 +756,7 @@ class IPMIPower(base.PowerInterface):
|
|||
"""
|
||||
driver_info = _parse_driver_info(task.node)
|
||||
_power_off(driver_info)
|
||||
driver_utils.ensure_next_boot_device(task, driver_info)
|
||||
state = _power_on(driver_info)
|
||||
|
||||
if state != states.POWER_ON:
|
||||
|
@ -820,6 +831,14 @@ class IPMIManagement(base.ManagementInterface):
|
|||
timeout_disable = "0x00 0x08 0x03 0x08"
|
||||
send_raw(task, timeout_disable)
|
||||
|
||||
if task.node.driver_info.get('ipmi_force_boot_device', False):
|
||||
driver_utils.force_persistent_boot(task,
|
||||
device,
|
||||
persistent)
|
||||
# Reset persistent to False, in case of BMC does not support
|
||||
# persistent or we do not have admin rights.
|
||||
persistent = False
|
||||
|
||||
cmd = "chassis bootdev %s" % device
|
||||
if persistent:
|
||||
cmd = cmd + " options=persistent"
|
||||
|
@ -852,9 +871,21 @@ class IPMIManagement(base.ManagementInterface):
|
|||
future boots or not, None if it is unknown.
|
||||
|
||||
"""
|
||||
driver_info = task.node.driver_info
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
|
||||
if (driver_info.get('ipmi_force_boot_device', False) and
|
||||
driver_internal_info.get('persistent_boot_device') and
|
||||
driver_internal_info.get('is_next_boot_persistent', True)):
|
||||
return {
|
||||
'boot_device': driver_internal_info['persistent_boot_device'],
|
||||
'persistent': True
|
||||
}
|
||||
|
||||
cmd = "chassis bootparam get 5"
|
||||
driver_info = _parse_driver_info(task.node)
|
||||
response = {'boot_device': None, 'persistent': None}
|
||||
|
||||
try:
|
||||
out, err = _exec_ipmitool(driver_info, cmd)
|
||||
except (exception.PasswordFileFailedToCreate,
|
||||
|
|
|
@ -17,6 +17,7 @@ from oslo_log import log as logging
|
|||
from ironic.common import exception
|
||||
from ironic.common.i18n import _
|
||||
from ironic.common.i18n import _LW
|
||||
from ironic.conductor import utils
|
||||
from ironic.drivers import base
|
||||
|
||||
|
||||
|
@ -170,3 +171,48 @@ def add_node_capability(task, capability, value):
|
|||
properties['capabilities'] = capabilities
|
||||
node.properties = properties
|
||||
node.save()
|
||||
|
||||
|
||||
def ensure_next_boot_device(task, driver_info):
|
||||
"""Ensure boot from correct device if persistent is True
|
||||
|
||||
If ipmi_force_boot_device is True and is_next_boot_persistent, set to
|
||||
boot from correct device, else unset is_next_boot_persistent field.
|
||||
|
||||
:param task: Node object.
|
||||
:param driver_info: Node driver_info.
|
||||
"""
|
||||
|
||||
if driver_info.get('force_boot_device', False):
|
||||
driver_internal_info = task.node.driver_internal_info
|
||||
if driver_internal_info.get('is_next_boot_persistent') is False:
|
||||
driver_internal_info.pop('is_next_boot_persistent', None)
|
||||
task.node.driver_internal_info = driver_internal_info
|
||||
task.node.save()
|
||||
else:
|
||||
boot_device = driver_internal_info.get('persistent_boot_device')
|
||||
if boot_device:
|
||||
utils.node_set_boot_device(task, boot_device)
|
||||
|
||||
|
||||
def force_persistent_boot(task, device, persistent):
|
||||
"""Set persistent boot device to driver_internal_info
|
||||
|
||||
If persistent is True set 'persistent_boot_device' field to the
|
||||
boot device and reset persistent to False, else set
|
||||
'is_next_boot_persistent' to False.
|
||||
|
||||
:param task: Task object.
|
||||
:param device: Boot device.
|
||||
:param persistent: Whether next boot is persistent or not.
|
||||
"""
|
||||
|
||||
node = task.node
|
||||
driver_internal_info = node.driver_internal_info
|
||||
if persistent:
|
||||
driver_internal_info['persistent_boot_device'] = device
|
||||
else:
|
||||
driver_internal_info['is_next_boot_persistent'] = False
|
||||
|
||||
node.driver_internal_info = driver_internal_info
|
||||
node.save()
|
||||
|
|
|
@ -3299,12 +3299,13 @@ class ManagerTestProperties(tests_db_base.DbTestCase):
|
|||
'ipmi_transit_channel', 'ipmi_transit_address',
|
||||
'ipmi_target_channel', 'ipmi_target_address',
|
||||
'ipmi_local_address', 'ipmi_protocol_version',
|
||||
'ipmi_force_boot_device'
|
||||
]
|
||||
self._check_driver_properties("fake_ipmitool", expected)
|
||||
|
||||
def test_driver_properties_fake_ipminative(self):
|
||||
expected = ['ipmi_address', 'ipmi_password', 'ipmi_username',
|
||||
'ipmi_terminal_port']
|
||||
'ipmi_terminal_port', 'ipmi_force_boot_device']
|
||||
self._check_driver_properties("fake_ipminative", expected)
|
||||
|
||||
def test_driver_properties_fake_ssh(self):
|
||||
|
@ -3335,13 +3336,14 @@ class ManagerTestProperties(tests_db_base.DbTestCase):
|
|||
'ipmi_transit_address', 'ipmi_target_channel',
|
||||
'ipmi_target_address', 'ipmi_local_address',
|
||||
'deploy_kernel', 'deploy_ramdisk', 'ipmi_protocol_version',
|
||||
'ipmi_force_boot_device'
|
||||
]
|
||||
self._check_driver_properties("pxe_ipmitool", expected)
|
||||
|
||||
def test_driver_properties_pxe_ipminative(self):
|
||||
expected = ['ipmi_address', 'ipmi_password', 'ipmi_username',
|
||||
'deploy_kernel', 'deploy_ramdisk',
|
||||
'ipmi_terminal_port']
|
||||
'ipmi_terminal_port', 'ipmi_force_boot_device']
|
||||
self._check_driver_properties("pxe_ipminative", expected)
|
||||
|
||||
def test_driver_properties_pxe_ssh(self):
|
||||
|
|
|
@ -30,6 +30,7 @@ from ironic.common import states
|
|||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules import console_utils
|
||||
from ironic.drivers.modules import ipminative
|
||||
from ironic.drivers import utils as driver_utils
|
||||
from ironic.tests.conductor import utils as mgr_utils
|
||||
from ironic.tests.db import base as db_base
|
||||
from ironic.tests.db import utils as db_utils
|
||||
|
@ -54,6 +55,7 @@ class IPMINativePrivateMethodTestCase(db_base.DbTestCase):
|
|||
self.assertIsNotNone(self.info.get('username'))
|
||||
self.assertIsNotNone(self.info.get('password'))
|
||||
self.assertIsNotNone(self.info.get('uuid'))
|
||||
self.assertIsNotNone(self.info.get('force_boot_device'))
|
||||
|
||||
# make sure error is raised when info, eg. username, is missing
|
||||
info = dict(INFO_DICT)
|
||||
|
@ -263,6 +265,18 @@ class IPMINativeDriverTestCase(db_base.DbTestCase):
|
|||
task, states.POWER_ON)
|
||||
power_on_mock.assert_called_once_with(self.info)
|
||||
|
||||
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
|
||||
@mock.patch.object(ipminative, '_power_on', autospec=True)
|
||||
def test_set_power_on_with_next_boot(self, power_on_mock, mock_next_boot):
|
||||
power_on_mock.return_value = states.POWER_ON
|
||||
|
||||
with task_manager.acquire(self.context,
|
||||
self.node.uuid) as task:
|
||||
self.driver.power.set_power_state(
|
||||
task, states.POWER_ON)
|
||||
mock_next_boot.assert_called_once_with(task, self.info)
|
||||
power_on_mock.assert_called_once_with(self.info)
|
||||
|
||||
@mock.patch.object(ipminative, '_power_off', autospec=True)
|
||||
def test_set_power_off_ok(self, power_off_mock):
|
||||
power_off_mock.return_value = states.POWER_OFF
|
||||
|
@ -298,6 +312,40 @@ class IPMINativeDriverTestCase(db_base.DbTestCase):
|
|||
# PXE is converted to 'network' internally by ipminative
|
||||
ipmicmd.set_bootdev.assert_called_once_with('network', persist=False)
|
||||
|
||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
||||
def test_force_set_boot_device_ok(self, ipmi_mock):
|
||||
ipmicmd = ipmi_mock.return_value
|
||||
ipmicmd.set_bootdev.return_value = None
|
||||
|
||||
with task_manager.acquire(self.context,
|
||||
self.node.uuid) as task:
|
||||
task.node.driver_info['ipmi_force_boot_device'] = True
|
||||
self.driver.management.set_boot_device(task, boot_devices.PXE)
|
||||
task.node.refresh()
|
||||
self.assertEqual(
|
||||
False,
|
||||
task.node.driver_internal_info['is_next_boot_persistent']
|
||||
)
|
||||
# PXE is converted to 'network' internally by ipminative
|
||||
ipmicmd.set_bootdev.assert_called_once_with('network', persist=False)
|
||||
|
||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
||||
def test_set_boot_device_with_persistent(self, ipmi_mock):
|
||||
ipmicmd = ipmi_mock.return_value
|
||||
ipmicmd.set_bootdev.return_value = None
|
||||
|
||||
with task_manager.acquire(self.context,
|
||||
self.node.uuid) as task:
|
||||
task.node.driver_info['ipmi_force_boot_device'] = True
|
||||
self.driver.management.set_boot_device(task,
|
||||
boot_devices.PXE,
|
||||
True)
|
||||
self.assertEqual(
|
||||
boot_devices.PXE,
|
||||
task.node.driver_internal_info['persistent_boot_device'])
|
||||
# PXE is converted to 'network' internally by ipminative
|
||||
ipmicmd.set_bootdev.assert_called_once_with('network', persist=False)
|
||||
|
||||
def test_set_boot_device_bad_device(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
|
@ -305,13 +353,15 @@ class IPMINativeDriverTestCase(db_base.DbTestCase):
|
|||
task,
|
||||
'fake-device')
|
||||
|
||||
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
|
||||
@mock.patch.object(ipminative, '_reboot', autospec=True)
|
||||
def test_reboot_ok(self, reboot_mock):
|
||||
def test_reboot_ok(self, reboot_mock, mock_next_boot):
|
||||
reboot_mock.return_value = None
|
||||
|
||||
with task_manager.acquire(self.context,
|
||||
self.node.uuid) as task:
|
||||
self.driver.power.reboot(task)
|
||||
mock_next_boot.assert_called_once_with(task, self.info)
|
||||
reboot_mock.assert_called_once_with(self.info)
|
||||
|
||||
@mock.patch('pyghmi.ipmi.command.Command', autospec=True)
|
||||
|
@ -378,6 +428,14 @@ class IPMINativeDriverTestCase(db_base.DbTestCase):
|
|||
self.assertEqual(expected,
|
||||
self.driver.management.get_boot_device(task))
|
||||
|
||||
def test_get_force_boot_device_persistent(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['ipmi_force_boot_device'] = True
|
||||
task.node.driver_internal_info['persistent_boot_device'] = 'pxe'
|
||||
bootdev = self.driver.management.get_boot_device(task)
|
||||
self.assertEqual('pxe', bootdev['boot_device'])
|
||||
self.assertTrue(bootdev['persistent'])
|
||||
|
||||
def test_management_interface_validate_good(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.management.validate(task)
|
||||
|
|
|
@ -39,6 +39,7 @@ from ironic.common import utils
|
|||
from ironic.conductor import task_manager
|
||||
from ironic.drivers.modules import console_utils
|
||||
from ironic.drivers.modules import ipmitool as ipmi
|
||||
from ironic.drivers import utils as driver_utils
|
||||
from ironic.tests import base
|
||||
from ironic.tests.conductor import utils as mgr_utils
|
||||
from ironic.tests.db import base as db_base
|
||||
|
@ -1205,6 +1206,23 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||
mock_on.assert_called_once_with(self.info)
|
||||
self.assertFalse(mock_off.called)
|
||||
|
||||
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
|
||||
@mock.patch.object(ipmi, '_power_on', autospec=True)
|
||||
@mock.patch.object(ipmi, '_power_off', autospec=True)
|
||||
def test_set_power_on_with_next_boot(self, mock_off, mock_on,
|
||||
mock_next_boot):
|
||||
self.config(retry_timeout=0, group='ipmi')
|
||||
|
||||
mock_on.return_value = states.POWER_ON
|
||||
with task_manager.acquire(self.context,
|
||||
self.node['uuid']) as task:
|
||||
self.driver.power.set_power_state(task,
|
||||
states.POWER_ON)
|
||||
mock_next_boot.assert_called_once_with(task, self.info)
|
||||
|
||||
mock_on.assert_called_once_with(self.info)
|
||||
self.assertFalse(mock_off.called)
|
||||
|
||||
@mock.patch.object(ipmi, '_power_on', autospec=True)
|
||||
@mock.patch.object(ipmi, '_power_off', autospec=True)
|
||||
def test_set_power_off_ok(self, mock_off, mock_on):
|
||||
|
@ -1296,9 +1314,10 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||
self.driver.vendor.bmc_reset,
|
||||
task, 'POST')
|
||||
|
||||
@mock.patch.object(driver_utils, 'ensure_next_boot_device', autospec=True)
|
||||
@mock.patch.object(ipmi, '_power_off', spec_set=types.FunctionType)
|
||||
@mock.patch.object(ipmi, '_power_on', spec_set=types.FunctionType)
|
||||
def test_reboot_ok(self, mock_on, mock_off):
|
||||
def test_reboot_ok(self, mock_on, mock_off, mock_next_boot):
|
||||
manager = mock.MagicMock()
|
||||
# NOTE(rloo): if autospec is True, then manager.mock_calls is empty
|
||||
mock_on.return_value = states.POWER_ON
|
||||
|
@ -1310,6 +1329,7 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||
with task_manager.acquire(self.context,
|
||||
self.node['uuid']) as task:
|
||||
self.driver.power.reboot(task)
|
||||
mock_next_boot.assert_called_once_with(task, self.info)
|
||||
|
||||
self.assertEqual(manager.mock_calls, expected)
|
||||
|
||||
|
@ -1501,6 +1521,42 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||
mock.call(self.info, "chassis bootdev pxe")]
|
||||
mock_exec.assert_has_calls(mock_calls)
|
||||
|
||||
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
|
||||
def test_management_interface_force_set_boot_device_ok(self, mock_exec):
|
||||
mock_exec.return_value = [None, None]
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['ipmi_force_boot_device'] = True
|
||||
self.info['force_boot_device'] = True
|
||||
self.driver.management.set_boot_device(task, boot_devices.PXE)
|
||||
task.node.refresh()
|
||||
self.assertEqual(
|
||||
False,
|
||||
task.node.driver_internal_info['is_next_boot_persistent']
|
||||
)
|
||||
|
||||
mock_calls = [mock.call(self.info, "raw 0x00 0x08 0x03 0x08"),
|
||||
mock.call(self.info, "chassis bootdev pxe")]
|
||||
mock_exec.assert_has_calls(mock_calls)
|
||||
|
||||
@mock.patch.object(ipmi, '_exec_ipmitool', autospec=True)
|
||||
def test_management_interface_set_boot_device_persistent(self, mock_exec):
|
||||
mock_exec.return_value = [None, None]
|
||||
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['ipmi_force_boot_device'] = True
|
||||
self.info['force_boot_device'] = True
|
||||
self.driver.management.set_boot_device(task,
|
||||
boot_devices.PXE,
|
||||
True)
|
||||
self.assertEqual(
|
||||
boot_devices.PXE,
|
||||
task.node.driver_internal_info['persistent_boot_device'])
|
||||
|
||||
mock_calls = [mock.call(self.info, "raw 0x00 0x08 0x03 0x08"),
|
||||
mock.call(self.info, "chassis bootdev pxe")]
|
||||
mock_exec.assert_has_calls(mock_calls)
|
||||
|
||||
def test_management_interface_set_boot_device_bad_device(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
self.assertRaises(exception.InvalidParameterValue,
|
||||
|
@ -1598,6 +1654,14 @@ class IPMIToolDriverTestCase(db_base.DbTestCase):
|
|||
mock_exec.assert_called_with(mock.ANY,
|
||||
"chassis bootparam get 5")
|
||||
|
||||
def test_get_force_boot_device_persistent(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.node.driver_info['ipmi_force_boot_device'] = True
|
||||
task.node.driver_internal_info['persistent_boot_device'] = 'pxe'
|
||||
bootdev = self.driver.management.get_boot_device(task)
|
||||
self.assertEqual('pxe', bootdev['boot_device'])
|
||||
self.assertTrue(bootdev['persistent'])
|
||||
|
||||
def test_management_interface_validate_good(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid) as task:
|
||||
task.driver.management.validate(task)
|
||||
|
|
|
@ -18,6 +18,7 @@ import mock
|
|||
from ironic.common import driver_factory
|
||||
from ironic.common import exception
|
||||
from ironic.conductor import task_manager
|
||||
from ironic.conductor import utils as manager_utils
|
||||
from ironic.drivers.modules import fake
|
||||
from ironic.drivers import utils as driver_utils
|
||||
from ironic.tests.conductor import utils as mgr_utils
|
||||
|
@ -114,3 +115,48 @@ class UtilsTestCase(db_base.DbTestCase):
|
|||
driver_utils.add_node_capability(task, 'a', 'b')
|
||||
self.assertEqual('a:b,c:d,a:b',
|
||||
task.node.properties['capabilities'])
|
||||
|
||||
@mock.patch.object(manager_utils, 'node_set_boot_device', autospec=True)
|
||||
def test_ensure_next_boot_device(self, node_set_boot_device_mock):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.node.driver_internal_info['persistent_boot_device'] = 'pxe'
|
||||
driver_utils.ensure_next_boot_device(
|
||||
task,
|
||||
{'force_boot_device': True}
|
||||
)
|
||||
node_set_boot_device_mock.assert_called_once_with(task, 'pxe')
|
||||
|
||||
def test_ensure_next_boot_device_clears_is_next_boot_persistent(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.node.driver_internal_info['persistent_boot_device'] = 'pxe'
|
||||
task.node.driver_internal_info['is_next_boot_persistent'] = False
|
||||
driver_utils.ensure_next_boot_device(
|
||||
task,
|
||||
{'force_boot_device': True}
|
||||
)
|
||||
task.node.refresh()
|
||||
self.assertNotIn('is_next_boot_persistent',
|
||||
task.node.driver_internal_info)
|
||||
|
||||
def test_force_persistent_boot_true(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
task.node.driver_info['ipmi_force_boot_device'] = True
|
||||
ret = driver_utils.force_persistent_boot(task, 'pxe', True)
|
||||
self.assertEqual(None, ret)
|
||||
task.node.refresh()
|
||||
self.assertIn('persistent_boot_device',
|
||||
task.node.driver_internal_info)
|
||||
|
||||
def test_force_persistent_boot_false(self):
|
||||
with task_manager.acquire(self.context, self.node.uuid,
|
||||
shared=False) as task:
|
||||
ret = driver_utils.force_persistent_boot(task, 'pxe', False)
|
||||
self.assertEqual(None, ret)
|
||||
task.node.refresh()
|
||||
self.assertEqual(
|
||||
False,
|
||||
task.node.driver_internal_info.get('is_next_boot_persistent')
|
||||
)
|
||||
|
|
Loading…
Reference in New Issue