Set actionplan state to FAILED if any action has failed

Currently, an actionplan state is set to SUCCEEDED once the execution
has finished, but that does not imply that all the actions finished
successfully.

This patch is checking the actual state of all the actions in the plan
after the execution has finished. If any action has status FAILED, it
will set the state of the action plan as FAILED and will apply the
appropiate notification parameters. This is the expected behavior according
to Watcher documentation.

The patch is also fixing the unit test for this to set the expected
action plan state to FAILED and notification parameters.

Closes-Bug: #2106407
Change-Id: I7bfc6759b51cd97c26ec13b3918bd8d3b7ac9d4e
This commit is contained in:
Alfredo Moralejo
2025-05-08 18:13:41 +02:00
parent b36ba8399e
commit 88d81c104e
3 changed files with 37 additions and 6 deletions

View File

@@ -0,0 +1,13 @@
---
fixes:
- |
Previously, if an action failed in an action plan, the state of the
action plan was reported as SUCCEEDED if the execution of the action has
finished regardless of the outcome.
Watcher will now reflect the actual state of all the actions in the plan
after the execution has finished. If any action has status FAILED, it
will set the state of the action plan as FAILED. This is the expected
behavior according to Watcher documentation.
For more info see: https://bugs.launchpad.net/watcher/+bug/2106407

View File

@@ -56,12 +56,30 @@ class DefaultActionPlanHandler(base.BaseActionPlanHandler):
applier = default.DefaultApplier(self.ctx, self.service)
applier.execute(self.action_plan_uuid)
action_plan.state = objects.action_plan.State.SUCCEEDED
# If any action has failed the action plan should be FAILED
# Define default values for successful execution
ap_state = objects.action_plan.State.SUCCEEDED
notification_kwargs = {
'phase': fields.NotificationPhase.END
}
failed_filter = {'action_plan_uuid': self.action_plan_uuid,
'state': objects.action.State.FAILED}
failed_actions = objects.Action.list(
self.ctx, filters=failed_filter, eager=True)
if failed_actions:
ap_state = objects.action_plan.State.FAILED
notification_kwargs = {
'phase': fields.NotificationPhase.ERROR,
'priority': fields.NotificationPriority.ERROR
}
action_plan.state = ap_state
action_plan.save()
notifications.action_plan.send_action_notification(
self.ctx, action_plan,
action=fields.NotificationAction.EXECUTION,
phase=fields.NotificationPhase.END)
**notification_kwargs)
except exception.ActionPlanCancelled as e:
LOG.exception(e)

View File

@@ -142,10 +142,10 @@ class TestDefaultActionPlanHandler(base.DbTestCase):
phase=objects.fields.NotificationPhase.START),
mock.call(self.context, self.action_plan,
action=objects.fields.NotificationAction.EXECUTION,
phase=objects.fields.NotificationPhase.END)]
# (amoralej) the actual action_plan.state should beh FAILED. I am
# setting it to SUCCEEDDED and will change it in the fixing change.
self.assertEqual(ap_objects.State.SUCCEEDED, self.action_plan.state)
priority=objects.fields.NotificationPriority.ERROR,
phase=objects.fields.NotificationPhase.ERROR)]
self.assertEqual(ap_objects.State.FAILED, self.action_plan.state)
self.assertEqual(
expected_calls,
self.m_action_plan_notifications