Clean out agent token even if power is already off

While investigating a very curious report, I discovered that
if somehow the power was *already* turned off to a node, say
through an incorrect BMC *or* human action, and Ironic were
to pick it up (as it does by default, because it checks before
applying the power state, then it would not wipe the token
information, preventing the agent from connecting on the next
action/attempt/operation.

We now remove the token on all calls to conductor
utilities node_power_action method when appropriate, even
if no other work is required.

Change-Id: Ie89e8be9ad2887467f277772445d4bef79fa5ea1
This commit is contained in:
Julia Kreger 2023-02-28 10:18:16 -08:00
parent 75c05be8a7
commit bcf6c12269
3 changed files with 38 additions and 0 deletions

View File

@ -297,6 +297,13 @@ def node_power_action(task, new_state, timeout=None):
node = task.node node = task.node
if _can_skip_state_change(task, new_state): if _can_skip_state_change(task, new_state):
# NOTE(TheJulia): Even if we are not changing the power state,
# we need to wipe the token out, just in case for some reason
# the power was turned off outside of our interaction/management.
if new_state in (states.POWER_OFF, states.SOFT_POWER_OFF,
states.REBOOT, states.SOFT_REBOOT):
wipe_internal_info_on_power_off(node)
node.save()
return return
target_state = _calculate_target_state(new_state) target_state = _calculate_target_state(new_state)

View File

@ -281,6 +281,31 @@ class NodePowerActionTestCase(db_base.DbTestCase):
self.assertNotIn('agent_cached_deploy_steps', self.assertNotIn('agent_cached_deploy_steps',
node['driver_internal_info']) node['driver_internal_info'])
@mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
def test_node_power_action_power_off_already(self, get_power_mock):
"""Test node_power_action to turn node power off, but already off."""
dii = {'agent_secret_token': 'token',
'agent_cached_deploy_steps': ['steps']}
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_OFF
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.assertNotIn('agent_secret_token', node['driver_internal_info'])
self.assertNotIn('agent_cached_deploy_steps',
node['driver_internal_info'])
@mock.patch.object(fake.FakePower, 'get_power_state', autospec=True) @mock.patch.object(fake.FakePower, 'get_power_state', autospec=True)
def test_node_power_action_power_off_pregenerated_token(self, def test_node_power_action_power_off_pregenerated_token(self,
get_power_mock): get_power_mock):

View File

@ -0,0 +1,6 @@
---
fixes:
- |
Fixes an issue where an agent token could be inadvertently orphaned
if a node is already in the target power state when we attempt to turn
the node off.