diff --git a/ironic/conductor/servicing.py b/ironic/conductor/servicing.py index 78350eed08..9caba93747 100644 --- a/ironic/conductor/servicing.py +++ b/ironic/conductor/servicing.py @@ -106,6 +106,9 @@ def do_node_service(task, service_steps=None, disable_ramdisk=False): else: LOG.debug('Will proceed with servicing node %(node)s ' 'without booting the ramdisk.', {'node': node.uuid}) + disable_ramdisk = True + node.set_driver_internal_info('service_disable_ramdisk', True) + node.save() do_next_service_step(task, step_index, disable_ramdisk=disable_ramdisk) diff --git a/ironic/tests/unit/conductor/test_servicing.py b/ironic/tests/unit/conductor/test_servicing.py index 1ababbb1fd..0b5b47f47b 100644 --- a/ironic/tests/unit/conductor/test_servicing.py +++ b/ironic/tests/unit/conductor/test_servicing.py @@ -199,6 +199,11 @@ def test__do_node_service_out_of_band(self, mock_prep, self.assertEqual(tgt_prov_state, node.target_provision_state) mock_prep.assert_not_called() mock_validate.assert_called_once_with(mock.ANY, mock.ANY) + self.assertTrue( + node.driver_internal_info.get('service_disable_ramdisk'), + 'service_disable_ramdisk must be True when no step requires ' + 'the ramdisk, so that mid-step reboots do not boot IPA' + ) @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.validate', autospec=True) @@ -227,6 +232,11 @@ def test__do_node_service_requires_ramdisk_fallback(self, mock_prep, self.assertEqual(tgt_prov_state, node.target_provision_state) mock_prep.assert_called_once_with(mock.ANY, mock.ANY) mock_validate.assert_called_once_with(mock.ANY, mock.ANY) + self.assertFalse( + node.driver_internal_info.get('service_disable_ramdisk'), + 'service_disable_ramdisk must not be True when a step requires ' + 'the ramdisk' + ) @mock.patch('ironic.drivers.modules.network.flat.FlatNetwork.validate', autospec=True) @@ -354,13 +364,19 @@ def set_steps(task, disable_ramdisk=None): else: mock_network_valid.assert_called_once_with(mock.ANY, task) + effective_disable = disable_ramdisk or not any( + s.get('requires_ramdisk') for s in service_steps) mock_next_step.assert_called_once_with( - task, 0, disable_ramdisk=disable_ramdisk) + task, 0, disable_ramdisk=effective_disable) mock_steps.assert_called_once_with( task, disable_ramdisk=disable_ramdisk) if service_steps: self.assertEqual(service_steps, node.driver_internal_info['service_steps']) + self.assertEqual( + effective_disable, + node.driver_internal_info.get('service_disable_ramdisk', + False)) self.assertFalse(node.maintenance) self.assertNotIn('agent_secret_token', node.driver_internal_info) diff --git a/releasenotes/notes/fix-ipa-boot-oob-service-steps-b2e4f1a9c3d70845.yaml b/releasenotes/notes/fix-ipa-boot-oob-service-steps-b2e4f1a9c3d70845.yaml new file mode 100644 index 0000000000..f9d816e377 --- /dev/null +++ b/releasenotes/notes/fix-ipa-boot-oob-service-steps-b2e4f1a9c3d70845.yaml @@ -0,0 +1,11 @@ +--- +fixes: + - | + Fixed an unnecessary IPA boot during out-of-band service steps. When all + service steps declare ``requires_ramdisk=False``, the conductor correctly + skips the initial ramdisk boot, but ``service_disable_ramdisk`` in + ``driver_internal_info`` was left as ``False``. Steps that call + ``reboot_to_finish_step()`` internally (e.g. Redfish BIOS + ``apply_configuration``) would then boot IPA before the reboot, adding a + disruptive and unnecessary boot cycle. The flag is now set to ``True`` + whenever all service steps are out-of-band.