summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ironic_python_agent/config.py2
-rw-r--r--ironic_python_agent/hardware.py75
-rw-r--r--ironic_python_agent/tests/functional/test_commands.py4
-rw-r--r--ironic_python_agent/tests/unit/extensions/test_clean.py34
-rw-r--r--ironic_python_agent/tests/unit/test_agent.py19
-rw-r--r--ironic_python_agent/tests/unit/test_hardware.py113
-rw-r--r--ironic_python_agent/tests/unit/test_multi_hardware_clean_steps.py5
-rw-r--r--releasenotes/notes/wait-root-device-504b517c3aec73e2.yaml6
8 files changed, 157 insertions, 101 deletions
diff --git a/ironic_python_agent/config.py b/ironic_python_agent/config.py
index 4a433627..94a255f8 100644
--- a/ironic_python_agent/config.py
+++ b/ironic_python_agent/config.py
@@ -180,7 +180,7 @@ cli_opts = [
default=APARAMS.get('ipa-disk-wait-delay', 3),
help='How much time (in seconds) to wait between attempts '
'to check if at least one suitable disk has appeared '
- 'in inventory. '
+ 'in inventory. Set to zero to disable. '
'Can be supplied as "ipa-disk-wait-delay" '
'kernel parameter.'),
cfg.BoolOpt('insecure',
diff --git a/ironic_python_agent/hardware.py b/ironic_python_agent/hardware.py
index 30549dbc..167e8b88 100644
--- a/ironic_python_agent/hardware.py
+++ b/ironic_python_agent/hardware.py
@@ -354,6 +354,33 @@ class HardwareManager(object):
erase_results[block_device.name] = result
return erase_results
+ def wait_for_disks(self):
+ """Wait for the root disk to appear.
+
+ Wait for at least one suitable disk to show up or a specific disk
+ if any device hint is specified. Otherwise neither inspection
+ not deployment have any chances to succeed.
+
+ """
+ if not CONF.disk_wait_attempts:
+ return
+
+ for attempt in range(CONF.disk_wait_attempts):
+ try:
+ self.get_os_install_device()
+ except errors.DeviceNotFound:
+ LOG.debug('Still waiting for the root device to appear, '
+ 'attempt %d of %d', attempt + 1,
+ CONF.disk_wait_attempts)
+
+ if attempt < CONF.disk_wait_attempts - 1:
+ time.sleep(CONF.disk_wait_delay)
+ else:
+ break
+ else:
+ LOG.warning('The root device was not detected in %d seconds',
+ CONF.disk_wait_delay * CONF.disk_wait_attempts)
+
def list_hardware_info(self):
"""Return full hardware inventory as a serializable dict.
@@ -462,32 +489,9 @@ class GenericHardwareManager(HardwareManager):
def evaluate_hardware_support(self):
# Do some initialization before we declare ourself ready
_check_for_iscsi()
- self._wait_for_disks()
+ self.wait_for_disks()
return HardwareSupport.GENERIC
- def _wait_for_disks(self):
- """Wait for disk to appear
-
- Wait for at least one suitable disk to show up, otherwise neither
- inspection not deployment have any chances to succeed.
-
- """
-
- for attempt in range(CONF.disk_wait_attempts):
- try:
- block_devices = self.list_block_devices()
- utils.guess_root_disk(block_devices)
- except errors.DeviceNotFound:
- LOG.debug('Still waiting for at least one disk to appear, '
- 'attempt %d of %d', attempt + 1,
- CONF.disk_wait_attempts)
- time.sleep(CONF.disk_wait_delay)
- else:
- break
- else:
- LOG.warning('No disks detected in %d seconds',
- CONF.disk_wait_delay * CONF.disk_wait_attempts)
-
def collect_lldp_data(self, interface_names):
"""Collect and convert LLDP info from the node.
@@ -678,10 +682,12 @@ class GenericHardwareManager(HardwareManager):
root_device_hints = None
if cached_node is not None:
root_device_hints = cached_node['properties'].get('root_device')
+ LOG.debug('Looking for a device matching root hints %s',
+ root_device_hints)
block_devices = self.list_block_devices()
if not root_device_hints:
- return utils.guess_root_disk(block_devices).name
+ dev_name = utils.guess_root_disk(block_devices).name
else:
serialized_devs = [dev.serialize() for dev in block_devices]
try:
@@ -702,7 +708,13 @@ class GenericHardwareManager(HardwareManager):
"No suitable device was found for "
"deployment using these hints %s" % root_device_hints)
- return device['name']
+ dev_name = device['name']
+
+ LOG.info('Picked root device %(dev)s for node %(node)s based on '
+ 'root device hints %(hints)s',
+ {'dev': dev_name, 'hints': root_device_hints,
+ 'node': cached_node['uuid'] if cached_node else None})
+ return dev_name
def get_system_vendor_info(self):
product_name = None
@@ -1133,11 +1145,22 @@ def cache_node(node):
Stores the node object in the hardware module to facilitate the
access of a node information in the hardware extensions.
+ If the new node does not match the previously cached one, wait for the
+ expected root device to appear.
+
:param node: Ironic node object
"""
global NODE
+ new_node = NODE is None or NODE['uuid'] != node['uuid']
NODE = node
+ if new_node:
+ LOG.info('Cached node %s, waiting for its root device to appear',
+ node['uuid'])
+ # Root device hints, stored in the new node, can change the expected
+ # root device. So let us wait for it to appear again.
+ dispatch_to_managers('wait_for_disks')
+
def get_cached_node():
"""Guard function around the module variable NODE."""
diff --git a/ironic_python_agent/tests/functional/test_commands.py b/ironic_python_agent/tests/functional/test_commands.py
index c0c6ea91..235b47d5 100644
--- a/ironic_python_agent/tests/functional/test_commands.py
+++ b/ironic_python_agent/tests/functional/test_commands.py
@@ -24,6 +24,8 @@ class TestCommands(base.FunctionalBase):
different test runs.
"""
+ node = {'uuid': '1', 'properties': {}}
+
def step_1_get_empty_commands(self):
response = self.request('get', 'commands')
self.assertEqual({'commands': []}, response)
@@ -34,7 +36,7 @@ class TestCommands(base.FunctionalBase):
# this command succeeds even with an empty node and port. This test's
# success is required for steps 3 and 4 to succeed.
command = {'name': 'clean.get_clean_steps',
- 'params': {'node': {}, 'ports': {}}}
+ 'params': {'node': self.node, 'ports': {}}}
response = self.request('post', 'commands', json=command,
headers={'Content-Type': 'application/json'})
self.assertIsNone(response['command_error'])
diff --git a/ironic_python_agent/tests/unit/extensions/test_clean.py b/ironic_python_agent/tests/unit/extensions/test_clean.py
index 2a949f02..7b9a7f53 100644
--- a/ironic_python_agent/tests/unit/extensions/test_clean.py
+++ b/ironic_python_agent/tests/unit/extensions/test_clean.py
@@ -19,6 +19,7 @@ from ironic_python_agent import errors
from ironic_python_agent.extensions import clean
+@mock.patch('ironic_python_agent.hardware.cache_node', autospec=True)
class TestCleanExtension(test_base.BaseTestCase):
def setUp(self):
super(TestCleanExtension, self).setUp()
@@ -37,7 +38,8 @@ class TestCleanExtension(test_base.BaseTestCase):
'_get_current_clean_version', autospec=True)
@mock.patch('ironic_python_agent.hardware.dispatch_to_all_managers',
autospec=True)
- def test_get_clean_steps(self, mock_dispatch, mock_version):
+ def test_get_clean_steps(self, mock_dispatch, mock_version,
+ mock_cache_node):
mock_version.return_value = self.version
manager_steps = {
@@ -135,12 +137,14 @@ class TestCleanExtension(test_base.BaseTestCase):
# 'priority' in Ironic
self.assertEqual(expected_return,
async_results.join().command_result)
+ mock_cache_node.assert_called_once_with(self.node)
@mock.patch('ironic_python_agent.hardware.dispatch_to_managers',
autospec=True)
@mock.patch('ironic_python_agent.extensions.clean._check_clean_version',
autospec=True)
- def test_execute_clean_step(self, mock_version, mock_dispatch):
+ def test_execute_clean_step(self, mock_version, mock_dispatch,
+ mock_cache_node):
result = 'cleaned'
mock_dispatch.return_value = result
@@ -159,13 +163,14 @@ class TestCleanExtension(test_base.BaseTestCase):
self.step['GenericHardwareManager'][0]['step'],
self.node, self.ports)
self.assertEqual(expected_result, async_result.command_result)
+ mock_cache_node.assert_called_once_with(self.node)
@mock.patch('ironic_python_agent.hardware.dispatch_to_managers',
autospec=True)
@mock.patch('ironic_python_agent.extensions.clean._check_clean_version',
autospec=True)
def test_execute_clean_step_tuple_result(self, mock_version,
- mock_dispatch):
+ mock_dispatch, mock_cache_node):
result = ('stdout', 'stderr')
mock_dispatch.return_value = result
@@ -184,10 +189,11 @@ class TestCleanExtension(test_base.BaseTestCase):
self.step['GenericHardwareManager'][0]['step'],
self.node, self.ports)
self.assertEqual(expected_result, async_result.command_result)
+ mock_cache_node.assert_called_once_with(self.node)
@mock.patch('ironic_python_agent.extensions.clean._check_clean_version',
autospec=True)
- def test_execute_clean_step_no_step(self, mock_version):
+ def test_execute_clean_step_no_step(self, mock_version, mock_cache_node):
async_result = self.agent_extension.execute_clean_step(
step={}, node=self.node, ports=self.ports,
clean_version=self.version)
@@ -195,12 +201,14 @@ class TestCleanExtension(test_base.BaseTestCase):
self.assertEqual('FAILED', async_result.command_status)
mock_version.assert_called_once_with(self.version)
+ mock_cache_node.assert_called_once_with(self.node)
@mock.patch('ironic_python_agent.hardware.dispatch_to_managers',
autospec=True)
@mock.patch('ironic_python_agent.extensions.clean._check_clean_version',
autospec=True)
- def test_execute_clean_step_fail(self, mock_version, mock_dispatch):
+ def test_execute_clean_step_fail(self, mock_version, mock_dispatch,
+ mock_cache_node):
mock_dispatch.side_effect = RuntimeError
async_result = self.agent_extension.execute_clean_step(
@@ -214,13 +222,15 @@ class TestCleanExtension(test_base.BaseTestCase):
mock_dispatch.assert_called_once_with(
self.step['GenericHardwareManager'][0]['step'],
self.node, self.ports)
+ mock_cache_node.assert_called_once_with(self.node)
@mock.patch('ironic_python_agent.hardware.dispatch_to_managers',
autospec=True)
@mock.patch('ironic_python_agent.extensions.clean._check_clean_version',
autospec=True)
def test_execute_clean_step_version_mismatch(self, mock_version,
- mock_dispatch):
+ mock_dispatch,
+ mock_cache_node):
mock_version.side_effect = errors.CleanVersionMismatch(
{'GenericHardwareManager': 1}, {'GenericHardwareManager': 2})
@@ -232,17 +242,19 @@ class TestCleanExtension(test_base.BaseTestCase):
mock_version.assert_called_once_with(self.version)
- @mock.patch('ironic_python_agent.hardware.dispatch_to_all_managers',
- autospec=True)
- def _get_current_clean_version(self, mock_dispatch):
+
+@mock.patch('ironic_python_agent.hardware.dispatch_to_all_managers',
+ autospec=True)
+class TestCleanVersion(test_base.BaseTestCase):
+ version = {'generic': '1', 'specific': '1'}
+
+ def test__get_current_clean_version(self, mock_dispatch):
mock_dispatch.return_value = {'SpecificHardwareManager':
{'name': 'specific', 'version': '1'},
'GenericHardwareManager':
{'name': 'generic', 'version': '1'}}
self.assertEqual(self.version, clean._get_current_clean_version())
- @mock.patch('ironic_python_agent.hardware.dispatch_to_all_managers',
- autospec=True)
def test__check_clean_version_fail(self, mock_dispatch):
mock_dispatch.return_value = {'SpecificHardwareManager':
{'name': 'specific', 'version': '1'}}
diff --git a/ironic_python_agent/tests/unit/test_agent.py b/ironic_python_agent/tests/unit/test_agent.py
index 1f49503b..1b084307 100644
--- a/ironic_python_agent/tests/unit/test_agent.py
+++ b/ironic_python_agent/tests/unit/test_agent.py
@@ -127,7 +127,7 @@ class TestHeartbeater(ironic_agent_base.IronicAgentTest):
self.assertEqual(2.7, self.heartbeater.error_delay)
-@mock.patch.object(hardware.GenericHardwareManager, '_wait_for_disks',
+@mock.patch.object(hardware.GenericHardwareManager, 'wait_for_disks',
lambda self: None)
class TestBaseAgent(ironic_agent_base.IronicAgentTest):
@@ -151,6 +151,7 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
FakeExtension())])
self.sample_nw_iface = hardware.NetworkInterface(
"eth9", "AA:BB:CC:DD:EE:FF", "1.2.3.4", True)
+ hardware.NODE = None
def assertEqualEncoded(self, a, b):
# Evidently JSONEncoder.default() can't handle None (??) so we have to
@@ -204,7 +205,9 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
server_class=simple_server.WSGIServer)
wsgi_server.serve_forever.assert_called_once_with()
mock_wait.assert_called_once_with(mock.ANY)
- mock_dispatch.assert_called_once_with("list_hardware_info")
+ self.assertEqual([mock.call('list_hardware_info'),
+ mock.call('wait_for_disks')],
+ mock_dispatch.call_args_list)
self.agent.heartbeater.start.assert_called_once_with()
@mock.patch.object(hardware, '_check_for_iscsi', mock.Mock())
@@ -252,7 +255,9 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
self.agent.api_client.lookup_node.call_args[1]['node_uuid'])
mock_wait.assert_called_once_with(mock.ANY)
- mock_dispatch.assert_called_once_with("list_hardware_info")
+ self.assertEqual([mock.call('list_hardware_info'),
+ mock.call('wait_for_disks')],
+ mock_dispatch.call_args_list)
self.agent.heartbeater.start.assert_called_once_with()
@mock.patch.object(hardware, '_check_for_iscsi', mock.Mock())
@@ -419,7 +424,9 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_sleep.assert_called_once_with(10)
self.assertTrue(mock_load_managers.called)
self.assertTrue(mock_wait.called)
- mock_dispatch.assert_called_once_with('list_hardware_info')
+ self.assertEqual([mock.call('list_hardware_info'),
+ mock.call('wait_for_disks')],
+ mock_dispatch.call_args_list)
def test_async_command_success(self):
result = base.AsyncCommandResult('foo_command', {'fail': False},
@@ -507,7 +514,7 @@ class TestBaseAgent(ironic_agent_base.IronicAgentTest):
mock_log.warning.assert_called_once()
-@mock.patch.object(hardware.GenericHardwareManager, '_wait_for_disks',
+@mock.patch.object(hardware.GenericHardwareManager, 'wait_for_disks',
lambda self: None)
class TestAgentStandalone(ironic_agent_base.IronicAgentTest):
@@ -563,7 +570,7 @@ class TestAgentStandalone(ironic_agent_base.IronicAgentTest):
@mock.patch.object(hardware, '_check_for_iscsi', lambda: None)
-@mock.patch.object(hardware.GenericHardwareManager, '_wait_for_disks',
+@mock.patch.object(hardware.GenericHardwareManager, 'wait_for_disks',
lambda self: None)
@mock.patch.object(socket, 'gethostbyname', autospec=True)
@mock.patch.object(utils, 'execute', autospec=True)
diff --git a/ironic_python_agent/tests/unit/test_hardware.py b/ironic_python_agent/tests/unit/test_hardware.py
index 901bae2d..a10dd22f 100644
--- a/ironic_python_agent/tests/unit/test_hardware.py
+++ b/ironic_python_agent/tests/unit/test_hardware.py
@@ -670,7 +670,8 @@ class TestGenericHardwareManager(base.IronicAgentTest):
@mock.patch.object(hardware, 'get_cached_node', autospec=True)
def _get_os_install_device_root_device_hints(self, hints, expected_device,
mock_cached_node, mock_dev):
- mock_cached_node.return_value = {'properties': {'root_device': hints}}
+ mock_cached_node.return_value = {'properties': {'root_device': hints},
+ 'uuid': 'node1'}
model = 'fastable sd131 7'
mock_dev.return_value = [
hardware.BlockDevice(name='/dev/sda',
@@ -1614,15 +1615,13 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertEqual('NEC',
self.hardware.get_system_vendor_info().manufacturer)
- @mock.patch.object(hardware.GenericHardwareManager, 'list_block_devices',
- autospec=True)
+ @mock.patch.object(hardware.GenericHardwareManager,
+ 'get_os_install_device', autospec=True)
@mock.patch.object(hardware, '_check_for_iscsi', autospec=True)
@mock.patch.object(time, 'sleep', autospec=True)
- @mock.patch.object(utils, 'guess_root_disk', autospec=True)
- def test_evaluate_hw_waits_for_disks(self, mocked_root_dev, mocked_sleep,
- mocked_check_for_iscsi,
- mocked_block_dev):
- mocked_root_dev.side_effect = [
+ def test_evaluate_hw_waits_for_disks(
+ self, mocked_sleep, mocked_check_for_iscsi, mocked_get_inst_dev):
+ mocked_get_inst_dev.side_effect = [
errors.DeviceNotFound('boom'),
None
]
@@ -1631,19 +1630,32 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.assertTrue(mocked_check_for_iscsi.called)
self.assertEqual(hardware.HardwareSupport.GENERIC, result)
- mocked_root_dev.assert_called_with(mocked_block_dev.return_value)
- self.assertEqual(2, mocked_root_dev.call_count)
+ mocked_get_inst_dev.assert_called_with(mock.ANY)
+ self.assertEqual(2, mocked_get_inst_dev.call_count)
mocked_sleep.assert_called_once_with(CONF.disk_wait_delay)
+ @mock.patch.object(hardware.GenericHardwareManager,
+ 'get_os_install_device', autospec=True)
+ @mock.patch.object(hardware, '_check_for_iscsi', autospec=True)
+ @mock.patch.object(time, 'sleep', autospec=True)
+ def test_evaluate_hw_no_wait_for_disks(
+ self, mocked_sleep, mocked_check_for_iscsi, mocked_get_inst_dev):
+ CONF.set_override('disk_wait_attempts', '0')
+
+ result = self.hardware.evaluate_hardware_support()
+
+ self.assertTrue(mocked_check_for_iscsi.called)
+ self.assertEqual(hardware.HardwareSupport.GENERIC, result)
+ self.assertFalse(mocked_get_inst_dev.called)
+ self.assertFalse(mocked_sleep.called)
+
@mock.patch.object(hardware, '_check_for_iscsi', mock.Mock())
- @mock.patch.object(hardware.GenericHardwareManager, 'list_block_devices',
- autospec=True)
+ @mock.patch.object(hardware.GenericHardwareManager,
+ 'get_os_install_device', autospec=True)
@mock.patch.object(time, 'sleep', autospec=True)
- @mock.patch.object(utils, 'guess_root_disk', autospec=True)
- def test_evaluate_hw_waits_for_disks_nonconfigured(self, mocked_root_dev,
- mocked_sleep,
- mocked_block_dev):
- mocked_root_dev.side_effect = [
+ def test_evaluate_hw_waits_for_disks_nonconfigured(
+ self, mocked_sleep, mocked_get_inst_dev):
+ mocked_get_inst_dev.side_effect = [
errors.DeviceNotFound('boom'),
errors.DeviceNotFound('boom'),
errors.DeviceNotFound('boom'),
@@ -1660,20 +1672,20 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.hardware.evaluate_hardware_support()
- mocked_root_dev.assert_called_with(mocked_block_dev.return_value)
- self.assertEqual(10, mocked_root_dev.call_count)
+ mocked_get_inst_dev.assert_called_with(mock.ANY)
+ self.assertEqual(10, mocked_get_inst_dev.call_count)
+ expected_calls = [mock.call(CONF.disk_wait_delay)] * 9
+ mocked_sleep.assert_has_calls(expected_calls)
@mock.patch.object(hardware, '_check_for_iscsi', mock.Mock())
- @mock.patch.object(hardware.GenericHardwareManager, 'list_block_devices',
- autospec=True)
+ @mock.patch.object(hardware.GenericHardwareManager,
+ 'get_os_install_device', autospec=True)
@mock.patch.object(time, 'sleep', autospec=True)
- @mock.patch.object(utils, 'guess_root_disk', autospec=True)
- def test_evaluate_hw_waits_for_disks_configured(self, mocked_root_dev,
- mocked_sleep,
- mocked_block_dev):
+ def test_evaluate_hw_waits_for_disks_configured(self, mocked_sleep,
+ mocked_get_inst_dev):
CONF.set_override('disk_wait_attempts', '2')
- mocked_root_dev.side_effect = [
+ mocked_get_inst_dev.side_effect = [
errors.DeviceNotFound('boom'),
errors.DeviceNotFound('boom'),
errors.DeviceNotFound('boom'),
@@ -1683,54 +1695,45 @@ class TestGenericHardwareManager(base.IronicAgentTest):
self.hardware.evaluate_hardware_support()
- mocked_root_dev.assert_called_with(mocked_block_dev.return_value)
- self.assertEqual(2, mocked_root_dev.call_count)
+ mocked_get_inst_dev.assert_called_with(mock.ANY)
+ self.assertEqual(2, mocked_get_inst_dev.call_count)
+ expected_calls = [mock.call(CONF.disk_wait_delay)] * 1
+ mocked_sleep.assert_has_calls(expected_calls)
@mock.patch.object(hardware, '_check_for_iscsi', mock.Mock())
- @mock.patch.object(hardware.GenericHardwareManager, 'list_block_devices',
- autospec=True)
+ @mock.patch.object(hardware.GenericHardwareManager,
+ 'get_os_install_device', autospec=True)
@mock.patch.object(time, 'sleep', autospec=True)
- @mock.patch.object(utils, 'guess_root_disk', autospec=True)
- def test_evaluate_hw_disks_timeout_unconfigured(self, mocked_root_dev,
- mocked_sleep,
- mocked_block_dev):
- mocked_root_dev.side_effect = errors.DeviceNotFound('boom')
-
+ def test_evaluate_hw_disks_timeout_unconfigured(self, mocked_sleep,
+ mocked_get_inst_dev):
+ mocked_get_inst_dev.side_effect = errors.DeviceNotFound('boom')
self.hardware.evaluate_hardware_support()
-
mocked_sleep.assert_called_with(3)
@mock.patch.object(hardware, '_check_for_iscsi', mock.Mock())
- @mock.patch.object(hardware.GenericHardwareManager, 'list_block_devices',
- autospec=True)
+ @mock.patch.object(hardware.GenericHardwareManager,
+ 'get_os_install_device', autospec=True)
@mock.patch.object(time, 'sleep', autospec=True)
- @mock.patch.object(utils, 'guess_root_disk', autospec=True)
- def test_evaluate_hw_disks_timeout_configured(self, mocked_root_dev,
- mocked_sleep,
- mocked_block_dev):
+ def test_evaluate_hw_disks_timeout_configured(self, mocked_sleep,
+ mocked_root_dev):
CONF.set_override('disk_wait_delay', '5')
mocked_root_dev.side_effect = errors.DeviceNotFound('boom')
self.hardware.evaluate_hardware_support()
-
mocked_sleep.assert_called_with(5)
- @mock.patch.object(hardware.GenericHardwareManager, 'list_block_devices',
- autospec=True)
+ @mock.patch.object(hardware.GenericHardwareManager,
+ 'get_os_install_device', autospec=True)
@mock.patch.object(hardware, '_check_for_iscsi', autospec=True)
@mock.patch.object(time, 'sleep', autospec=True)
- @mock.patch.object(utils, 'guess_root_disk', autospec=True)
- def test_evaluate_hw_disks_timeout(self, mocked_root_dev, mocked_sleep,
- mocked_check_for_iscsi,
- mocked_block_dev):
- mocked_root_dev.side_effect = errors.DeviceNotFound('boom')
-
+ def test_evaluate_hw_disks_timeout(
+ self, mocked_sleep, mocked_check_for_iscsi, mocked_get_inst_dev):
+ mocked_get_inst_dev.side_effect = errors.DeviceNotFound('boom')
result = self.hardware.evaluate_hardware_support()
-
self.assertEqual(hardware.HardwareSupport.GENERIC, result)
- mocked_root_dev.assert_called_with(mocked_block_dev.return_value)
+ mocked_get_inst_dev.assert_called_with(mock.ANY)
self.assertEqual(CONF.disk_wait_attempts,
- mocked_root_dev.call_count)
+ mocked_get_inst_dev.call_count)
mocked_sleep.assert_called_with(CONF.disk_wait_delay)
@mock.patch.object(utils, 'get_agent_params',
diff --git a/ironic_python_agent/tests/unit/test_multi_hardware_clean_steps.py b/ironic_python_agent/tests/unit/test_multi_hardware_clean_steps.py
index ef47124e..fd46e53b 100644
--- a/ironic_python_agent/tests/unit/test_multi_hardware_clean_steps.py
+++ b/ironic_python_agent/tests/unit/test_multi_hardware_clean_steps.py
@@ -53,6 +53,8 @@ class ZFakeGenericHardwareManager(hardware.HardwareManager):
_build_clean_step('ZHigherPrio', 100)]
+@mock.patch.object(hardware.HardwareManager, 'wait_for_disks',
+ lambda _self: None)
class TestMultipleHardwareManagerCleanSteps(base.IronicAgentTest):
def setUp(self):
super(TestMultipleHardwareManagerCleanSteps, self).setUp()
@@ -79,7 +81,8 @@ class TestMultipleHardwareManagerCleanSteps(base.IronicAgentTest):
hardware._global_managers = None
def test_clean_step_ordering(self):
- as_results = self.agent_extension.get_clean_steps(node={}, ports=[])
+ as_results = self.agent_extension.get_clean_steps(node={'uuid': '1'},
+ ports=[])
results = as_results.join().command_result
expected_steps = {
'clean_steps': {
diff --git a/releasenotes/notes/wait-root-device-504b517c3aec73e2.yaml b/releasenotes/notes/wait-root-device-504b517c3aec73e2.yaml
new file mode 100644
index 00000000..40ccb55e
--- /dev/null
+++ b/releasenotes/notes/wait-root-device-504b517c3aec73e2.yaml
@@ -0,0 +1,6 @@
+---
+fixes:
+ - |
+ If root device hints are provided on the node, wait for the root device
+ instead of the first disk. This fixes deployment when the target disk takes
+ time to load.