| #ifndef PRODUCTION_SUSHID_SAFEPOWER_AGENT_ACTION_CONTEXT_H_ |
| #define PRODUCTION_SUSHID_SAFEPOWER_AGENT_ACTION_CONTEXT_H_ |
| |
| #include <cstdint> |
| #include <memory> |
| #include <optional> |
| #include <string> |
| #include <utility> |
| |
| #include "action_hash.h" |
| #include "condition.h" |
| #include "safepower_agent.pb.h" |
| #include "state_persistence.pb.h" |
| #include "state_updater.h" |
| #include "absl/base/nullability.h" |
| #include "absl/base/thread_annotations.h" |
| #include "absl/container/flat_hash_map.h" |
| #include "absl/functional/any_invocable.h" |
| #include "absl/status/status.h" |
| #include "absl/status/statusor.h" |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/string_view.h" |
| #include "absl/synchronization/mutex.h" |
| namespace safepower_agent { |
| |
| class ActionContext; |
| |
| // Used to start and manage actions. Handles retrieving persistent state and |
| // scheduling new actions. |
| class ActionContextManager { |
| public: |
| using Action = |
| absl::AnyInvocable<void(const safepower_agent_proto::Action&, |
| absl::AnyInvocable<void(absl::Status) &&>)>; |
| |
| explicit ActionContextManager( |
| std::shared_ptr<StateUpdater<safepower_agent_proto::SystemState>> |
| state_updater) |
| : state_updater_(std::move(state_updater)) {} |
| |
| ActionContextManager(ActionContextManager&& other) = delete; |
| ActionContextManager& operator=(ActionContextManager&& other) = delete; |
| |
| absl::Status RegisterAction(const safepower_agent_proto::Action& action, |
| Action action_impl) |
| ABSL_LOCKS_EXCLUDED(actions_mutex_); |
| |
| absl::Status LoadSavedActions(); |
| |
| absl::StatusOr<ActionContext* > StartAction( |
| safepower_agent_proto::StartActionRequest request) |
| ABSL_LOCKS_EXCLUDED(actions_mutex_); |
| void FinishAction(const safepower_agent_proto::Action& action, |
| Action action_impl) ABSL_LOCKS_EXCLUDED(actions_mutex_); |
| |
| ActionContext* GetActionContext(absl::string_view action_id) |
| ABSL_LOCKS_EXCLUDED(actions_mutex_); |
| |
| void GetSupportedActions( |
| safepower_agent_proto::GetSupportedActionsResponse& response) const |
| ABSL_LOCKS_EXCLUDED(actions_mutex_); |
| |
| const |
| std::shared_ptr<StateUpdater<safepower_agent_proto::SystemState>>& |
| system_state_updater() const { |
| return state_updater_; |
| } |
| |
| // This is just for use by mutex annotations. Since it return const, it |
| // can't be used to acquire the mutex. |
| const absl::Mutex& actions_mutex() const { return actions_mutex_; } |
| |
| private: |
| absl::StatusOr<Action> ReserveAction( |
| const safepower_agent_proto::Action& action) |
| ABSL_EXCLUSIVE_LOCKS_REQUIRED(actions_mutex_); |
| |
| absl::StatusOr< std::unique_ptr<ActionContext>> |
| ReloadActionContext( |
| std::string action_id, |
| safepower_agent_persistence_proto::SavedAction saved_action) |
| ABSL_EXCLUSIVE_LOCKS_REQUIRED(actions_mutex_); |
| |
| std::string NextActionId() ABSL_EXCLUSIVE_LOCKS_REQUIRED(actions_mutex_); |
| |
| std::shared_ptr<StateUpdater<safepower_agent_proto::SystemState>> |
| state_updater_; |
| |
| mutable absl::Mutex actions_mutex_; |
| uint64_t next_action_id_ ABSL_GUARDED_BY(actions_mutex_) = 0; |
| absl::flat_hash_map<safepower_agent_proto::Action, Action, ActionHash, |
| ActionEq> |
| actions_ ABSL_GUARDED_BY(actions_mutex_); |
| // ActionContext owns the key. |
| absl::flat_hash_map<absl::string_view, std::unique_ptr<ActionContext>> |
| running_actions_ ABSL_GUARDED_BY(actions_mutex_); |
| }; |
| |
| // Represents a single action execution. |
| class ActionContext { |
| constexpr static int kMaxStateChangeRetryCount = 4; |
| |
| public: |
| // Only allow ActionContext to be created by ActionContextManager. |
| struct CreationToken { |
| private: |
| CreationToken() = default; |
| friend class ActionContextManager; |
| }; |
| |
| using Action = ActionContextManager::Action; |
| |
| explicit ActionContext( |
| CreationToken token, ActionContextManager& manager, std::string action_id, |
| safepower_agent_proto::StartActionRequest request, |
| Action action_impl = {}, |
| safepower_agent_proto::ActionStateLog initial_state = NewInitialState()); |
| ~ActionContext(); |
| |
| ActionContext(const ActionContext& other) = delete; |
| ActionContext& operator=(const ActionContext& other) = delete; |
| |
| absl::string_view action_id() const { return action_id_; } |
| const safepower_agent_proto::StartActionRequest& request() const { |
| return request_; |
| } |
| |
| |
| std::shared_ptr<StateUpdater<safepower_agent_proto::ActionStateLog>> |
| action_state_updater() const { |
| return action_state_updater_; |
| } |
| |
| absl::Status Activate(); |
| |
| static bool IsFinalState(safepower_agent_proto::ActionState state) { |
| return state == safepower_agent_proto::ACTION_STATE_SUCCESS || |
| state == safepower_agent_proto::ACTION_STATE_ERROR; |
| } |
| |
| private: |
| absl::Status EnterStateInit() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); |
| absl::Status EnterStateRunningAction() ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); |
| absl::Status EnterStateCheckingPrecondition() |
| ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); |
| absl::Status EnterStateValidatingFinalState() |
| ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); |
| |
| void NextStateInit() ABSL_LOCKS_EXCLUDED(mutex_); |
| void NextStatePreconditionMatched(absl::Status status, |
| Condition::MatchList matches) |
| ABSL_LOCKS_EXCLUDED(mutex_); |
| void NextStateActionRan(absl::Status status) ABSL_LOCKS_EXCLUDED(mutex_); |
| void NextStateValidationCompleted(absl::Status status, |
| Condition::MatchList matches) |
| ABSL_LOCKS_EXCLUDED(mutex_); |
| |
| void RunAction() ABSL_LOCKS_EXCLUDED(mutex_); |
| |
| absl::Status StartCheckingCondition( |
| Condition& condition, |
| absl::AnyInvocable<void(absl::Status, Condition::MatchList)> callback) |
| ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); |
| |
| std::string execution_task_name() const { |
| return absl::StrCat(action_id_, ".execution"); |
| } |
| |
| void SetState(safepower_agent_proto::ActionState new_state, |
| safepower_agent_proto::ActionStateChange change_info = {}) |
| ABSL_EXCLUSIVE_LOCKS_REQUIRED(mutex_); |
| void Finish(Action action_impl); |
| |
| absl::Mutex mutex_ ABSL_ACQUIRED_AFTER(manager_.actions_mutex()); |
| ActionContextManager& manager_; |
| const safepower_agent_proto::StartActionRequest request_; |
| Action action_impl_ ABSL_GUARDED_BY(mutex_); |
| std::optional<Condition> precondition_; |
| std::optional<Condition> validation_; |
| |
| std::shared_ptr<StateUpdater<safepower_agent_proto::ActionStateLog>> |
| action_state_updater_; |
| static safepower_agent_proto::ActionStateLog NewInitialState(); |
| |
| const std::string action_id_; |
| }; |
| |
| } // namespace safepower_agent |
| |
| #endif // PRODUCTION_SUSHID_SAFEPOWER_AGENT_ACTION_CONTEXT_H_ |