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