blob: 9f642df647fc0a49da9381988072096e923ba0d1 [file] [log] [blame]
Matthew Barth0c4b1572020-10-22 14:39:46 -05001/**
2 * Copyright © 2020 IBM Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16#pragma once
17
Matthew Barthd9cb63b2021-03-24 14:36:49 -050018#include "../zone.hpp"
Matthew Barthbfd7e1b2021-02-04 14:25:47 -060019#include "config_base.hpp"
Matthew Barth12cb1252021-03-08 16:47:30 -060020#include "group.hpp"
Matthew Barth0c4b1572020-10-22 14:39:46 -050021
22#include <fmt/format.h>
23
24#include <nlohmann/json.hpp>
25#include <phosphor-logging/log.hpp>
26
27#include <functional>
28#include <iterator>
29#include <map>
30#include <memory>
31#include <numeric>
32
33namespace phosphor::fan::control::json
34{
35
36using json = nlohmann::json;
37using namespace phosphor::logging;
38
39/**
Matthew Barthbfd7e1b2021-02-04 14:25:47 -060040 * @class ActionParseError - A parsing error exception
41 *
42 * A parsing error exception that can be used to terminate the application
43 * due to not being able to successfully parse a configured action.
44 */
45class ActionParseError : public std::runtime_error
46{
47 public:
48 ActionParseError() = delete;
49 ActionParseError(const ActionParseError&) = delete;
50 ActionParseError(ActionParseError&&) = delete;
51 ActionParseError& operator=(const ActionParseError&) = delete;
52 ActionParseError& operator=(ActionParseError&&) = delete;
53 ~ActionParseError() = default;
54
55 /**
56 * @brief Action parsing error object
57 *
58 * When parsing an action from the JSON configuration, any critical
59 * attributes that fail to be parsed for an action can throw an
60 * ActionParseError exception to log the parsing failure details and
61 * terminate the application.
62 *
63 * @param[in] name - Name of the action
64 * @param[in] details - Additional details of the parsing error
65 */
66 ActionParseError(const std::string& name, const std::string& details) :
67 std::runtime_error(
68 fmt::format("Failed to parse action {} [{}]", name, details)
69 .c_str())
70 {}
71};
72
73/**
Matthew Barth0c4b1572020-10-22 14:39:46 -050074 * @brief Function used in creating action objects
75 *
76 * @param[in] jsonObj - JSON object for the action
Matthew Barth19c77492021-04-08 10:06:06 -050077 * @param[in] groups - Groups of dbus objects the action uses
78 * @param[in] zones - Zones the action runs against
Matthew Barth0c4b1572020-10-22 14:39:46 -050079 *
Matthew Barth19c77492021-04-08 10:06:06 -050080 * Creates an action object given the JSON configuration, list of groups and
81 * sets the zones the action should run against.
Matthew Barth0c4b1572020-10-22 14:39:46 -050082 */
83template <typename T>
Matthew Barth19c77492021-04-08 10:06:06 -050084std::unique_ptr<T>
85 createAction(const json& jsonObj, const std::vector<Group>& groups,
86 std::vector<std::reference_wrapper<Zone>>& zones)
Matthew Barth0c4b1572020-10-22 14:39:46 -050087{
Matthew Barth19c77492021-04-08 10:06:06 -050088 // Create the action and set its list of zones
89 auto action = std::make_unique<T>(jsonObj, groups);
90 action->setZones(zones);
91 return action;
Matthew Barth0c4b1572020-10-22 14:39:46 -050092}
93
94/**
95 * @class ActionBase - Base action object
96 *
97 * Base class for fan control's event actions
98 */
Matthew Barthbfd7e1b2021-02-04 14:25:47 -060099class ActionBase : public ConfigBase
Matthew Barth0c4b1572020-10-22 14:39:46 -0500100{
101 public:
102 ActionBase() = delete;
103 ActionBase(const ActionBase&) = delete;
104 ActionBase(ActionBase&&) = delete;
105 ActionBase& operator=(const ActionBase&) = delete;
106 ActionBase& operator=(ActionBase&&) = delete;
107 virtual ~ActionBase() = default;
Matthew Barth41a34082021-01-27 15:31:48 -0600108
109 /**
110 * @brief Base action object
111 *
Matthew Barth19c77492021-04-08 10:06:06 -0500112 * @param[in] jsonObj - JSON object containing name and any profiles
113 * @param[in] groups - Groups of dbus objects the action uses
114 *
Matthew Barth41a34082021-01-27 15:31:48 -0600115 * All actions derived from this base action object must be given a name
Matthew Barthbfd7e1b2021-02-04 14:25:47 -0600116 * that uniquely identifies the action. Optionally, a configured action can
117 * have a list of explicit profiles it should be included in, otherwise
118 * always include the action where no profiles are given.
Matthew Barth41a34082021-01-27 15:31:48 -0600119 */
Matthew Barth19c77492021-04-08 10:06:06 -0500120 ActionBase(const json& jsonObj, const std::vector<Group>& groups) :
121 ConfigBase(jsonObj), _groups(groups)
Matthew Barth0c4b1572020-10-22 14:39:46 -0500122 {}
123
124 /**
Matthew Barth19c77492021-04-08 10:06:06 -0500125 * @brief Set the zones the action is run against
126 *
127 * @param[in] zones - Zones the action runs against
128 *
129 * By default, the zones are set when the action object is created
130 */
131 virtual void setZones(std::vector<std::reference_wrapper<Zone>>& zones)
132 {
133 _zones = zones;
134 }
135
136 /**
Matthew Barth41a34082021-01-27 15:31:48 -0600137 * @brief Run the action
Matthew Barth0c4b1572020-10-22 14:39:46 -0500138 *
Matthew Barth41a34082021-01-27 15:31:48 -0600139 * Run the action function associated to the derived action object
Matthew Barth6d2476c2021-04-08 10:48:57 -0500140 * that performs a specific tasks on a zone configured by a user.
Matthew Barth0c4b1572020-10-22 14:39:46 -0500141 *
Matthew Barth41a34082021-01-27 15:31:48 -0600142 * @param[in] zone - Zone to run the action on
Matthew Barth0c4b1572020-10-22 14:39:46 -0500143 */
Matthew Barth6d2476c2021-04-08 10:48:57 -0500144 virtual void run(Zone& zone) = 0;
Matthew Barth19c77492021-04-08 10:06:06 -0500145
146 /**
147 * @brief Trigger the action to run against all of its zones
148 *
149 * This is the function used by triggers to run the actions against all the
150 * zones that were configured for the action to run against.
151 */
152 void run()
153 {
Matthew Barth19c77492021-04-08 10:06:06 -0500154 std::for_each(_zones.begin(), _zones.end(),
Matthew Barth6d2476c2021-04-08 10:48:57 -0500155 [this](Zone& zone) { this->run(zone); });
Matthew Barth19c77492021-04-08 10:06:06 -0500156 }
157
158 protected:
159 /* Groups configured on the action */
160 const std::vector<Group> _groups;
161
162 private:
163 /* Zones configured on the action */
164 std::vector<std::reference_wrapper<Zone>> _zones;
Matthew Barth0c4b1572020-10-22 14:39:46 -0500165};
166
167/**
168 * @class ActionFactory - Factory for actions
169 *
170 * Factory that registers and retrieves actions based on a given name.
171 */
172class ActionFactory
173{
174 public:
175 ActionFactory() = delete;
176 ActionFactory(const ActionFactory&) = delete;
177 ActionFactory(ActionFactory&&) = delete;
178 ActionFactory& operator=(const ActionFactory&) = delete;
179 ActionFactory& operator=(ActionFactory&&) = delete;
180 ~ActionFactory() = default;
181
182 /**
183 * @brief Registers an action
184 *
185 * Registers an action as being available for configuration use. The action
186 * is registered by its name and a function used to create the action
187 * object. An action fails to be registered when another action of the same
188 * name has already been registered. Actions with the same name would cause
189 * undefined behavior, therefore are not allowed.
190 *
191 * Actions are registered prior to starting main().
192 *
193 * @param[in] name - Name of the action to register
194 *
195 * @return The action was registered, otherwise an exception is thrown.
196 */
197 template <typename T>
198 static bool regAction(const std::string& name)
199 {
200 auto it = actions.find(name);
201 if (it == actions.end())
202 {
203 actions[name] = &createAction<T>;
204 }
205 else
206 {
207 log<level::ERR>(
208 fmt::format("Action '{}' is already registered", name).c_str());
209 throw std::runtime_error("Actions with the same name found");
210 }
211
212 return true;
213 }
214
215 /**
216 * @brief Gets a registered action's object
217 *
218 * Gets a registered action's object of a given name from the JSON
219 * configuration data provided.
220 *
221 * @param[in] name - Name of the action to create/get
222 * @param[in] jsonObj - JSON object for the action
Matthew Barth19c77492021-04-08 10:06:06 -0500223 * @param[in] groups - Groups of dbus objects the action uses
224 * @param[in] zones - Zones the action runs against
Matthew Barth0c4b1572020-10-22 14:39:46 -0500225 *
226 * @return Pointer to the action object.
227 */
Matthew Barth46b34482021-04-06 11:27:23 -0500228 static std::unique_ptr<ActionBase>
229 getAction(const std::string& name, const json& jsonObj,
230 const std::vector<Group>& groups,
231 std::vector<std::reference_wrapper<Zone>>&& zones)
Matthew Barth0c4b1572020-10-22 14:39:46 -0500232 {
233 auto it = actions.find(name);
234 if (it != actions.end())
235 {
Matthew Barth19c77492021-04-08 10:06:06 -0500236 return it->second(jsonObj, groups, zones);
Matthew Barth0c4b1572020-10-22 14:39:46 -0500237 }
238 else
239 {
240 // Construct list of available actions
241 auto acts = std::accumulate(
242 std::next(actions.begin()), actions.end(),
243 actions.begin()->first, [](auto list, auto act) {
244 return std::move(list) + ", " + act.first;
245 });
246 log<level::ERR>(
247 fmt::format("Action '{}' is not registered", name).c_str(),
248 entry("AVAILABLE_ACTIONS=%s", acts.c_str()));
249 throw std::runtime_error("Unsupported action name given");
250 }
251 }
252
253 private:
254 /* Map to store the available actions and their creation functions */
Matthew Barth19c77492021-04-08 10:06:06 -0500255 static inline std::map<std::string,
256 std::function<std::unique_ptr<ActionBase>(
257 const json&, const std::vector<Group>&,
258 std::vector<std::reference_wrapper<Zone>>&)>>
Matthew Barth0c4b1572020-10-22 14:39:46 -0500259 actions;
260};
261
262/**
263 * @class ActionRegister - Registers an action class
264 *
265 * Base action registration class that is extended by an action object so
266 * that action is registered and available for use.
267 */
268template <typename T>
269class ActionRegister
270{
271 public:
272 ActionRegister(const ActionRegister&) = delete;
273 ActionRegister(ActionRegister&&) = delete;
274 ActionRegister& operator=(const ActionRegister&) = delete;
275 ActionRegister& operator=(ActionRegister&&) = delete;
276 virtual ~ActionRegister() = default;
277 ActionRegister()
278 {
279 // Templates instantiated when used, need to assign a value
280 // here so the compiler doesnt remove it
281 registered = true;
282 }
283
284 private:
285 /* Register actions in the factory */
286 static inline bool registered = ActionFactory::regAction<T>(T::name);
287};
288
289} // namespace phosphor::fan::control::json