xref: /openbmc/phosphor-fan-presence/control/json/actions/action.hpp (revision 64b5ac203518568ec8b7569d0e785352278f2472)
10c4b1574SMatthew Barth /**
259af8caaSMike Capps  * Copyright © 2022 IBM Corporation
30c4b1574SMatthew Barth  *
40c4b1574SMatthew Barth  * Licensed under the Apache License, Version 2.0 (the "License");
50c4b1574SMatthew Barth  * you may not use this file except in compliance with the License.
60c4b1574SMatthew Barth  * You may obtain a copy of the License at
70c4b1574SMatthew Barth  *
80c4b1574SMatthew Barth  *     http://www.apache.org/licenses/LICENSE-2.0
90c4b1574SMatthew Barth  *
100c4b1574SMatthew Barth  * Unless required by applicable law or agreed to in writing, software
110c4b1574SMatthew Barth  * distributed under the License is distributed on an "AS IS" BASIS,
120c4b1574SMatthew Barth  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
130c4b1574SMatthew Barth  * See the License for the specific language governing permissions and
140c4b1574SMatthew Barth  * limitations under the License.
150c4b1574SMatthew Barth  */
160c4b1574SMatthew Barth #pragma once
170c4b1574SMatthew Barth 
18c9d49a65SMatt Spinler #include "../utils/flight_recorder.hpp"
19d9cb63b6SMatthew Barth #include "../zone.hpp"
20bfd7e1b7SMatthew Barth #include "config_base.hpp"
2112cb125aSMatthew Barth #include "group.hpp"
220c4b1574SMatthew Barth 
230c4b1574SMatthew Barth #include <nlohmann/json.hpp>
24*64b5ac20SAnwaar Hadi #include <phosphor-logging/lg2.hpp>
250c4b1574SMatthew Barth 
26b5811a6dSMatthew Barth #include <algorithm>
27fbf4703fSPatrick Williams #include <format>
280c4b1574SMatthew Barth #include <functional>
290c4b1574SMatthew Barth #include <iterator>
300c4b1574SMatthew Barth #include <map>
310c4b1574SMatthew Barth #include <memory>
320c4b1574SMatthew Barth #include <numeric>
330c4b1574SMatthew Barth 
340c4b1574SMatthew Barth namespace phosphor::fan::control::json
350c4b1574SMatthew Barth {
360c4b1574SMatthew Barth 
370c4b1574SMatthew Barth using json = nlohmann::json;
380c4b1574SMatthew Barth 
390c4b1574SMatthew Barth /**
40bfd7e1b7SMatthew Barth  * @class ActionParseError - A parsing error exception
41bfd7e1b7SMatthew Barth  *
42bfd7e1b7SMatthew Barth  * A parsing error exception that can be used to terminate the application
43bfd7e1b7SMatthew Barth  * due to not being able to successfully parse a configured action.
44bfd7e1b7SMatthew Barth  */
45bfd7e1b7SMatthew Barth class ActionParseError : public std::runtime_error
46bfd7e1b7SMatthew Barth {
47bfd7e1b7SMatthew Barth   public:
48bfd7e1b7SMatthew Barth     ActionParseError() = delete;
49bfd7e1b7SMatthew Barth     ActionParseError(const ActionParseError&) = delete;
50bfd7e1b7SMatthew Barth     ActionParseError(ActionParseError&&) = delete;
51bfd7e1b7SMatthew Barth     ActionParseError& operator=(const ActionParseError&) = delete;
52bfd7e1b7SMatthew Barth     ActionParseError& operator=(ActionParseError&&) = delete;
53bfd7e1b7SMatthew Barth     ~ActionParseError() = default;
54bfd7e1b7SMatthew Barth 
55bfd7e1b7SMatthew Barth     /**
56bfd7e1b7SMatthew Barth      * @brief Action parsing error object
57bfd7e1b7SMatthew Barth      *
58bfd7e1b7SMatthew Barth      * When parsing an action from the JSON configuration, any critical
59bfd7e1b7SMatthew Barth      * attributes that fail to be parsed for an action can throw an
60bfd7e1b7SMatthew Barth      * ActionParseError exception to log the parsing failure details and
61bfd7e1b7SMatthew Barth      * terminate the application.
62bfd7e1b7SMatthew Barth      *
63bfd7e1b7SMatthew Barth      * @param[in] name - Name of the action
64bfd7e1b7SMatthew Barth      * @param[in] details - Additional details of the parsing error
65bfd7e1b7SMatthew Barth      */
66bfd7e1b7SMatthew Barth     ActionParseError(const std::string& name, const std::string& details) :
ActionParseError(const std::string & name,const std::string & details)67bfd7e1b7SMatthew Barth         std::runtime_error(
68fbf4703fSPatrick Williams             std::format("Failed to parse action {} [{}]", name, details)
69bfd7e1b7SMatthew Barth                 .c_str())
70bfd7e1b7SMatthew Barth     {}
71bfd7e1b7SMatthew Barth };
72bfd7e1b7SMatthew Barth 
73bfd7e1b7SMatthew Barth /**
740c4b1574SMatthew Barth  * @brief Function used in creating action objects
750c4b1574SMatthew Barth  *
760c4b1574SMatthew Barth  * @param[in] jsonObj - JSON object for the action
7719c77494SMatthew Barth  * @param[in] groups - Groups of dbus objects the action uses
7819c77494SMatthew Barth  * @param[in] zones - Zones the action runs against
790c4b1574SMatthew Barth  *
8019c77494SMatthew Barth  * Creates an action object given the JSON configuration, list of groups and
8119c77494SMatthew Barth  * sets the zones the action should run against.
820c4b1574SMatthew Barth  */
830c4b1574SMatthew Barth template <typename T>
844fa67aa1SPatrick Williams std::unique_ptr<T> createAction(
createAction(const json & jsonObj,const std::vector<Group> & groups,std::vector<std::reference_wrapper<Zone>> & zones)854fa67aa1SPatrick Williams     const json& jsonObj, const std::vector<Group>& groups,
8619c77494SMatthew Barth     std::vector<std::reference_wrapper<Zone>>& zones)
870c4b1574SMatthew Barth {
8819c77494SMatthew Barth     // Create the action and set its list of zones
8919c77494SMatthew Barth     auto action = std::make_unique<T>(jsonObj, groups);
9019c77494SMatthew Barth     action->setZones(zones);
9119c77494SMatthew Barth     return action;
920c4b1574SMatthew Barth }
930c4b1574SMatthew Barth 
940c4b1574SMatthew Barth /**
950c4b1574SMatthew Barth  * @class ActionBase - Base action object
960c4b1574SMatthew Barth  *
970c4b1574SMatthew Barth  * Base class for fan control's event actions
980c4b1574SMatthew Barth  */
99bfd7e1b7SMatthew Barth class ActionBase : public ConfigBase
1000c4b1574SMatthew Barth {
1010c4b1574SMatthew Barth   public:
1020c4b1574SMatthew Barth     ActionBase() = delete;
1030c4b1574SMatthew Barth     ActionBase(const ActionBase&) = delete;
1040c4b1574SMatthew Barth     ActionBase(ActionBase&&) = delete;
1050c4b1574SMatthew Barth     ActionBase& operator=(const ActionBase&) = delete;
1060c4b1574SMatthew Barth     ActionBase& operator=(ActionBase&&) = delete;
1070c4b1574SMatthew Barth     virtual ~ActionBase() = default;
10841a3408dSMatthew Barth 
10941a3408dSMatthew Barth     /**
11041a3408dSMatthew Barth      * @brief Base action object
11141a3408dSMatthew Barth      *
11219c77494SMatthew Barth      * @param[in] jsonObj - JSON object containing name and any profiles
11319c77494SMatthew Barth      * @param[in] groups - Groups of dbus objects the action uses
11419c77494SMatthew Barth      *
11541a3408dSMatthew Barth      * All actions derived from this base action object must be given a name
116bfd7e1b7SMatthew Barth      * that uniquely identifies the action. Optionally, a configured action can
117bfd7e1b7SMatthew Barth      * have a list of explicit profiles it should be included in, otherwise
118bfd7e1b7SMatthew Barth      * always include the action where no profiles are given.
11941a3408dSMatthew Barth      */
12019c77494SMatthew Barth     ActionBase(const json& jsonObj, const std::vector<Group>& groups) :
ActionBase(const json & jsonObj,const std::vector<Group> & groups)121c9d49a65SMatt Spinler         ConfigBase(jsonObj), _groups(groups),
122c9d49a65SMatt Spinler         _uniqueName(getName() + "-" + std::to_string(_actionCount++))
1230c4b1574SMatthew Barth     {}
1240c4b1574SMatthew Barth 
1250c4b1574SMatthew Barth     /**
126eebde06eSMatthew Barth      * @brief Get the groups configured on the action
127eebde06eSMatthew Barth      *
128eebde06eSMatthew Barth      * @return List of groups
129eebde06eSMatthew Barth      */
130eebde06eSMatthew Barth     inline const auto& getGroups() const
getGroups() const131eebde06eSMatthew Barth     {
132eebde06eSMatthew Barth         return _groups;
133eebde06eSMatthew Barth     }
134eebde06eSMatthew Barth 
135eebde06eSMatthew Barth     /**
13619c77494SMatthew Barth      * @brief Set the zones the action is run against
13719c77494SMatthew Barth      *
13819c77494SMatthew Barth      * @param[in] zones - Zones the action runs against
13919c77494SMatthew Barth      *
14019c77494SMatthew Barth      * By default, the zones are set when the action object is created
14119c77494SMatthew Barth      */
14219c77494SMatthew Barth     virtual void setZones(std::vector<std::reference_wrapper<Zone>>& zones)
setZones(std::vector<std::reference_wrapper<Zone>> & zones)14319c77494SMatthew Barth     {
14419c77494SMatthew Barth         _zones = zones;
14519c77494SMatthew Barth     }
14619c77494SMatthew Barth 
14719c77494SMatthew Barth     /**
148b5811a6dSMatthew Barth      * @brief Add a zone to the list of zones the action is run against if its
149b5811a6dSMatthew Barth      * not already there
150b5811a6dSMatthew Barth      *
151b5811a6dSMatthew Barth      * @param[in] zone - Zone to add
152b5811a6dSMatthew Barth      */
153b5811a6dSMatthew Barth     virtual void addZone(Zone& zone)
addZone(Zone & zone)154b5811a6dSMatthew Barth     {
155dfddd648SPatrick Williams         auto itZone =
156dfddd648SPatrick Williams             std::find_if(_zones.begin(), _zones.end(),
157b5811a6dSMatthew Barth                          [&zone](std::reference_wrapper<Zone>& z) {
158b5811a6dSMatthew Barth                              return z.get().getName() == zone.getName();
159b5811a6dSMatthew Barth                          });
160b5811a6dSMatthew Barth         if (itZone == _zones.end())
161b5811a6dSMatthew Barth         {
162b5811a6dSMatthew Barth             _zones.emplace_back(std::reference_wrapper<Zone>(zone));
163b5811a6dSMatthew Barth         }
164b5811a6dSMatthew Barth     }
165b5811a6dSMatthew Barth 
166b5811a6dSMatthew Barth     /**
16741a3408dSMatthew Barth      * @brief Run the action
1680c4b1574SMatthew Barth      *
16941a3408dSMatthew Barth      * Run the action function associated to the derived action object
1706d2476c9SMatthew Barth      * that performs a specific tasks on a zone configured by a user.
1710c4b1574SMatthew Barth      *
17241a3408dSMatthew Barth      * @param[in] zone - Zone to run the action on
1730c4b1574SMatthew Barth      */
1746d2476c9SMatthew Barth     virtual void run(Zone& zone) = 0;
17519c77494SMatthew Barth 
17619c77494SMatthew Barth     /**
17719c77494SMatthew Barth      * @brief Trigger the action to run against all of its zones
17819c77494SMatthew Barth      *
17919c77494SMatthew Barth      * This is the function used by triggers to run the actions against all the
18019c77494SMatthew Barth      * zones that were configured for the action to run against.
18119c77494SMatthew Barth      */
18219c77494SMatthew Barth     void run()
run()18319c77494SMatthew Barth     {
18419c77494SMatthew Barth         std::for_each(_zones.begin(), _zones.end(),
1856d2476c9SMatthew Barth                       [this](Zone& zone) { this->run(zone); });
18619c77494SMatthew Barth     }
18719c77494SMatthew Barth 
188c9d49a65SMatt Spinler     /**
189c9d49a65SMatt Spinler      * @brief Returns a unique name for the action.
190c9d49a65SMatt Spinler      *
191c9d49a65SMatt Spinler      * @return std::string - The name
192c9d49a65SMatt Spinler      */
193c9d49a65SMatt Spinler     const std::string& getUniqueName() const
getUniqueName() const194c9d49a65SMatt Spinler     {
195c9d49a65SMatt Spinler         return _uniqueName;
196c9d49a65SMatt Spinler     }
197c9d49a65SMatt Spinler 
198e6fc210aSMatt Spinler     /**
199e6fc210aSMatt Spinler      * @brief Set the name of the owning Event.
200e6fc210aSMatt Spinler      *
201e6fc210aSMatt Spinler      * Adds it to the unique name in parentheses. If desired,
202e6fc210aSMatt Spinler      * the action child classes can do something else with it.
203e6fc210aSMatt Spinler      *
204e6fc210aSMatt Spinler      * @param[in] name - The event name
205e6fc210aSMatt Spinler      */
206e6fc210aSMatt Spinler     virtual void setEventName(const std::string& name)
setEventName(const std::string & name)207e6fc210aSMatt Spinler     {
208e6fc210aSMatt Spinler         if (!name.empty())
209e6fc210aSMatt Spinler         {
210e6fc210aSMatt Spinler             _uniqueName += '(' + name + ')';
211e6fc210aSMatt Spinler         }
212e6fc210aSMatt Spinler     }
213e6fc210aSMatt Spinler 
214c3eb7b3cSMatt Spinler     /**
215c3eb7b3cSMatt Spinler      * @brief Dump the action as JSON
216c3eb7b3cSMatt Spinler      *
217c3eb7b3cSMatt Spinler      * For now just dump its group names
218c3eb7b3cSMatt Spinler      *
219c3eb7b3cSMatt Spinler      * @return json
220c3eb7b3cSMatt Spinler      */
221c3eb7b3cSMatt Spinler     json dump() const
dump() const222c3eb7b3cSMatt Spinler     {
223c3eb7b3cSMatt Spinler         json groups = json::array();
224c3eb7b3cSMatt Spinler         std::for_each(_groups.begin(), _groups.end(),
225c3eb7b3cSMatt Spinler                       [&groups](const auto& group) {
226c3eb7b3cSMatt Spinler                           groups.push_back(group.getName());
227c3eb7b3cSMatt Spinler                       });
228c3eb7b3cSMatt Spinler         json output;
229c3eb7b3cSMatt Spinler         output["groups"] = groups;
230c3eb7b3cSMatt Spinler         return output;
231c3eb7b3cSMatt Spinler     }
232c3eb7b3cSMatt Spinler 
23319c77494SMatthew Barth   protected:
234c9d49a65SMatt Spinler     /**
235c9d49a65SMatt Spinler      * @brief Logs a message to the flight recorder using
236c9d49a65SMatt Spinler      *        the unique name of the action.
237c9d49a65SMatt Spinler      *
238c9d49a65SMatt Spinler      * @param[in] message - The message to log
239c9d49a65SMatt Spinler      */
240c9d49a65SMatt Spinler     void record(const std::string& message) const
record(const std::string & message) const241c9d49a65SMatt Spinler     {
242c9d49a65SMatt Spinler         FlightRecorder::instance().log(getUniqueName(), message);
243c9d49a65SMatt Spinler     }
244c9d49a65SMatt Spinler 
24519c77494SMatthew Barth     /* Groups configured on the action */
24619c77494SMatthew Barth     const std::vector<Group> _groups;
24719c77494SMatthew Barth 
24819c77494SMatthew Barth   private:
24919c77494SMatthew Barth     /* Zones configured on the action */
25019c77494SMatthew Barth     std::vector<std::reference_wrapper<Zone>> _zones;
251c9d49a65SMatt Spinler 
252c9d49a65SMatt Spinler     /* Unique name of the action.
253c9d49a65SMatt Spinler      * It's just the name plus _actionCount at the time of action creation. */
254e6fc210aSMatt Spinler     std::string _uniqueName;
255c9d49a65SMatt Spinler 
256c9d49a65SMatt Spinler     /* Running count of all actions */
257c9d49a65SMatt Spinler     static inline size_t _actionCount = 0;
2580c4b1574SMatthew Barth };
2590c4b1574SMatthew Barth 
2600c4b1574SMatthew Barth /**
2610c4b1574SMatthew Barth  * @class ActionFactory - Factory for actions
2620c4b1574SMatthew Barth  *
2630c4b1574SMatthew Barth  * Factory that registers and retrieves actions based on a given name.
2640c4b1574SMatthew Barth  */
2650c4b1574SMatthew Barth class ActionFactory
2660c4b1574SMatthew Barth {
2670c4b1574SMatthew Barth   public:
2680c4b1574SMatthew Barth     ActionFactory() = delete;
2690c4b1574SMatthew Barth     ActionFactory(const ActionFactory&) = delete;
2700c4b1574SMatthew Barth     ActionFactory(ActionFactory&&) = delete;
2710c4b1574SMatthew Barth     ActionFactory& operator=(const ActionFactory&) = delete;
2720c4b1574SMatthew Barth     ActionFactory& operator=(ActionFactory&&) = delete;
2730c4b1574SMatthew Barth     ~ActionFactory() = default;
2740c4b1574SMatthew Barth 
2750c4b1574SMatthew Barth     /**
2760c4b1574SMatthew Barth      * @brief Registers an action
2770c4b1574SMatthew Barth      *
2780c4b1574SMatthew Barth      * Registers an action as being available for configuration use. The action
2790c4b1574SMatthew Barth      * is registered by its name and a function used to create the action
2800c4b1574SMatthew Barth      * object. An action fails to be registered when another action of the same
2810c4b1574SMatthew Barth      * name has already been registered. Actions with the same name would cause
2820c4b1574SMatthew Barth      * undefined behavior, therefore are not allowed.
2830c4b1574SMatthew Barth      *
2840c4b1574SMatthew Barth      * Actions are registered prior to starting main().
2850c4b1574SMatthew Barth      *
2860c4b1574SMatthew Barth      * @param[in] name - Name of the action to register
2870c4b1574SMatthew Barth      *
2880c4b1574SMatthew Barth      * @return The action was registered, otherwise an exception is thrown.
2890c4b1574SMatthew Barth      */
2900c4b1574SMatthew Barth     template <typename T>
2910c4b1574SMatthew Barth     static bool regAction(const std::string& name)
regAction(const std::string & name)2920c4b1574SMatthew Barth     {
2930c4b1574SMatthew Barth         auto it = actions.find(name);
2940c4b1574SMatthew Barth         if (it == actions.end())
2950c4b1574SMatthew Barth         {
2960c4b1574SMatthew Barth             actions[name] = &createAction<T>;
2970c4b1574SMatthew Barth         }
2980c4b1574SMatthew Barth         else
2990c4b1574SMatthew Barth         {
300*64b5ac20SAnwaar Hadi             lg2::error("Action '{NAME}' is already registered", "NAME", name);
3010c4b1574SMatthew Barth             throw std::runtime_error("Actions with the same name found");
3020c4b1574SMatthew Barth         }
3030c4b1574SMatthew Barth 
3040c4b1574SMatthew Barth         return true;
3050c4b1574SMatthew Barth     }
3060c4b1574SMatthew Barth 
3070c4b1574SMatthew Barth     /**
3080c4b1574SMatthew Barth      * @brief Gets a registered action's object
3090c4b1574SMatthew Barth      *
3100c4b1574SMatthew Barth      * Gets a registered action's object of a given name from the JSON
3110c4b1574SMatthew Barth      * configuration data provided.
3120c4b1574SMatthew Barth      *
3130c4b1574SMatthew Barth      * @param[in] name - Name of the action to create/get
3140c4b1574SMatthew Barth      * @param[in] jsonObj - JSON object for the action
31519c77494SMatthew Barth      * @param[in] groups - Groups of dbus objects the action uses
31619c77494SMatthew Barth      * @param[in] zones - Zones the action runs against
3170c4b1574SMatthew Barth      *
3180c4b1574SMatthew Barth      * @return Pointer to the action object.
3190c4b1574SMatthew Barth      */
3204fa67aa1SPatrick Williams     static std::unique_ptr<ActionBase> getAction(
3214fa67aa1SPatrick Williams         const std::string& name, const json& jsonObj,
getAction(const std::string & name,const json & jsonObj,const std::vector<Group> & groups,std::vector<std::reference_wrapper<Zone>> && zones)32246b34485SMatthew Barth         const std::vector<Group>& groups,
32346b34485SMatthew Barth         std::vector<std::reference_wrapper<Zone>>&& zones)
3240c4b1574SMatthew Barth     {
3250c4b1574SMatthew Barth         auto it = actions.find(name);
3260c4b1574SMatthew Barth         if (it != actions.end())
3270c4b1574SMatthew Barth         {
32819c77494SMatthew Barth             return it->second(jsonObj, groups, zones);
3290c4b1574SMatthew Barth         }
3300c4b1574SMatthew Barth         else
3310c4b1574SMatthew Barth         {
3320c4b1574SMatthew Barth             // Construct list of available actions
333dfddd648SPatrick Williams             auto acts = std::accumulate(
334dfddd648SPatrick Williams                 std::next(actions.begin()), actions.end(),
335dfddd648SPatrick Williams                 actions.begin()->first, [](auto list, auto act) {
3360c4b1574SMatthew Barth                     return std::move(list) + ", " + act.first;
3370c4b1574SMatthew Barth                 });
338*64b5ac20SAnwaar Hadi             lg2::error(
339*64b5ac20SAnwaar Hadi                 "Action '{NAME}' is not registered. Available actions are {AVAILABLE_ACTIONS}",
340*64b5ac20SAnwaar Hadi                 "NAME", name, "AVAILABLE_ACTIONS", acts);
3410c4b1574SMatthew Barth             throw std::runtime_error("Unsupported action name given");
3420c4b1574SMatthew Barth         }
3430c4b1574SMatthew Barth     }
3440c4b1574SMatthew Barth 
3450c4b1574SMatthew Barth   private:
3460c4b1574SMatthew Barth     /* Map to store the available actions and their creation functions */
34719c77494SMatthew Barth     static inline std::map<std::string,
34819c77494SMatthew Barth                            std::function<std::unique_ptr<ActionBase>(
34919c77494SMatthew Barth                                const json&, const std::vector<Group>&,
35019c77494SMatthew Barth                                std::vector<std::reference_wrapper<Zone>>&)>>
3510c4b1574SMatthew Barth         actions;
3520c4b1574SMatthew Barth };
3530c4b1574SMatthew Barth 
3540c4b1574SMatthew Barth /**
3550c4b1574SMatthew Barth  * @class ActionRegister - Registers an action class
3560c4b1574SMatthew Barth  *
3570c4b1574SMatthew Barth  * Base action registration class that is extended by an action object so
3580c4b1574SMatthew Barth  * that action is registered and available for use.
3590c4b1574SMatthew Barth  */
3600c4b1574SMatthew Barth template <typename T>
3610c4b1574SMatthew Barth class ActionRegister
3620c4b1574SMatthew Barth {
3630c4b1574SMatthew Barth   public:
3640c4b1574SMatthew Barth     ActionRegister(const ActionRegister&) = delete;
3650c4b1574SMatthew Barth     ActionRegister(ActionRegister&&) = delete;
3660c4b1574SMatthew Barth     ActionRegister& operator=(const ActionRegister&) = delete;
3670c4b1574SMatthew Barth     ActionRegister& operator=(ActionRegister&&) = delete;
3680c4b1574SMatthew Barth     virtual ~ActionRegister() = default;
3690c4b1574SMatthew Barth     ActionRegister()
3700c4b1574SMatthew Barth     {
ActionRegister()3710c4b1574SMatthew Barth         // Templates instantiated when used, need to assign a value
3720c4b1574SMatthew Barth         // here so the compiler doesnt remove it
3730c4b1574SMatthew Barth         registered = true;
3740c4b1574SMatthew Barth     }
3750c4b1574SMatthew Barth 
3760c4b1574SMatthew Barth   private:
3770c4b1574SMatthew Barth     /* Register actions in the factory */
3780c4b1574SMatthew Barth     static inline bool registered = ActionFactory::regAction<T>(T::name);
3790c4b1574SMatthew Barth };
3800c4b1574SMatthew Barth 
3810c4b1574SMatthew Barth } // namespace phosphor::fan::control::json
382