From 2d4d375358c8eb923a0a3bcee211ac53b991e54b Mon Sep 17 00:00:00 2001 From: Dmitry Tantsur Date: Wed, 8 Jul 2020 14:03:36 +0200 Subject: [PATCH] Wipe agent token during reboot or power off Otherwise a reboot during fast-track will leave the newly booted agent without an ability to request a token. Change-Id: I963276efae5599bfed6cbb4df18e8dd3bd1b9839 --- ironic/conductor/utils.py | 7 +++++ ironic/tests/unit/conductor/test_utils.py | 29 ++++++++++++++++++- .../notes/token-reboot-b48b5981a58a30ae.yaml | 5 ++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/token-reboot-b48b5981a58a30ae.yaml diff --git a/ironic/conductor/utils.py b/ironic/conductor/utils.py index fac2f7f3eb..60bc4a5427 100644 --- a/ironic/conductor/utils.py +++ b/ironic/conductor/utils.py @@ -284,6 +284,13 @@ def node_power_action(task, new_state, timeout=None): driver_internal_info = node.driver_internal_info driver_internal_info['last_power_state_change'] = str( timeutils.utcnow().isoformat()) + # NOTE(dtantsur): wipe token on shutting down, otherwise a reboot in + # fast-track (or an accidentally booted agent) will cause subsequent + # actions to fail. + if target_state in (states.POWER_OFF, states.SOFT_POWER_OFF, + states.REBOOT, states.SOFT_REBOOT): + if not is_agent_token_pregenerated(node): + driver_internal_info.pop('agent_secret_token', False) node.driver_internal_info = driver_internal_info node.save() diff --git a/ironic/tests/unit/conductor/test_utils.py b/ironic/tests/unit/conductor/test_utils.py index bb8027046c..98eb0b9f4e 100644 --- a/ironic/tests/unit/conductor/test_utils.py +++ b/ironic/tests/unit/conductor/test_utils.py @@ -225,10 +225,12 @@ class NodePowerActionTestCase(db_base.DbTestCase): @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True) def test_node_power_action_power_off(self, get_power_mock): """Test node_power_action to turn node power off.""" + dii = {'agent_secret_token': 'token'} node = obj_utils.create_test_node(self.context, uuid=uuidutils.generate_uuid(), driver='fake-hardware', - power_state=states.POWER_ON) + power_state=states.POWER_ON, + driver_internal_info=dii) task = task_manager.TaskManager(self.context, node.uuid) get_power_mock.return_value = states.POWER_ON @@ -240,6 +242,31 @@ class NodePowerActionTestCase(db_base.DbTestCase): self.assertEqual(states.POWER_OFF, node['power_state']) self.assertIsNone(node['target_power_state']) self.assertIsNone(node['last_error']) + self.assertNotIn('agent_secret_token', node['driver_internal_info']) + + @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True) + def test_node_power_action_power_off_pregenerated_token(self, + get_power_mock): + dii = {'agent_secret_token': 'token', + 'agent_secret_token_pregenerated': True} + node = obj_utils.create_test_node(self.context, + uuid=uuidutils.generate_uuid(), + driver='fake-hardware', + power_state=states.POWER_ON, + driver_internal_info=dii) + task = task_manager.TaskManager(self.context, node.uuid) + + get_power_mock.return_value = states.POWER_ON + + conductor_utils.node_power_action(task, states.POWER_OFF) + + node.refresh() + get_power_mock.assert_called_once_with(mock.ANY, mock.ANY) + self.assertEqual(states.POWER_OFF, node['power_state']) + self.assertIsNone(node['target_power_state']) + self.assertIsNone(node['last_error']) + self.assertEqual('token', + node['driver_internal_info']['agent_secret_token']) @mock.patch.object(fake.FakePower, 'reboot', autospec=True) @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True) diff --git a/releasenotes/notes/token-reboot-b48b5981a58a30ae.yaml b/releasenotes/notes/token-reboot-b48b5981a58a30ae.yaml new file mode 100644 index 0000000000..9ee2ca70a3 --- /dev/null +++ b/releasenotes/notes/token-reboot-b48b5981a58a30ae.yaml @@ -0,0 +1,5 @@ +--- +fixes: + - | + Removes stale agent tokens when rebooting nodes using API. This prevents + lookup failures for nodes that get rebooted between fast-track operations.