blob: 06759cce89f74ab54826ed72f5d6f05d006fcd69 [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
77 *
78 * Creates an action object given the JSON configuration
79 */
80template <typename T>
81std::unique_ptr<T> createAction(const json& jsonObj)
82{
83 return std::make_unique<T>(jsonObj);
84}
85
86/**
87 * @class ActionBase - Base action object
88 *
89 * Base class for fan control's event actions
90 */
Matthew Barthbfd7e1b2021-02-04 14:25:47 -060091class ActionBase : public ConfigBase
Matthew Barth0c4b1572020-10-22 14:39:46 -050092{
93 public:
94 ActionBase() = delete;
95 ActionBase(const ActionBase&) = delete;
96 ActionBase(ActionBase&&) = delete;
97 ActionBase& operator=(const ActionBase&) = delete;
98 ActionBase& operator=(ActionBase&&) = delete;
99 virtual ~ActionBase() = default;
Matthew Barth41a34082021-01-27 15:31:48 -0600100
101 /**
102 * @brief Base action object
103 *
104 * All actions derived from this base action object must be given a name
Matthew Barthbfd7e1b2021-02-04 14:25:47 -0600105 * that uniquely identifies the action. Optionally, a configured action can
106 * have a list of explicit profiles it should be included in, otherwise
107 * always include the action where no profiles are given.
Matthew Barth41a34082021-01-27 15:31:48 -0600108 *
Matthew Barthbfd7e1b2021-02-04 14:25:47 -0600109 * @param[in] jsonObj - JSON object containing name and any profiles
Matthew Barth41a34082021-01-27 15:31:48 -0600110 */
Matthew Barthbfd7e1b2021-02-04 14:25:47 -0600111 explicit ActionBase(const json& jsonObj) : ConfigBase(jsonObj)
Matthew Barth0c4b1572020-10-22 14:39:46 -0500112 {}
113
114 /**
Matthew Barth41a34082021-01-27 15:31:48 -0600115 * @brief Run the action
Matthew Barth0c4b1572020-10-22 14:39:46 -0500116 *
Matthew Barth41a34082021-01-27 15:31:48 -0600117 * Run the action function associated to the derived action object
118 * that performs a specific task against a group of dbus objects on a zone
119 * configured by a user.
Matthew Barth0c4b1572020-10-22 14:39:46 -0500120 *
Matthew Barth41a34082021-01-27 15:31:48 -0600121 * @param[in] zone - Zone to run the action on
122 * @param[in] group - Group of dbus objects the action runs against
Matthew Barth0c4b1572020-10-22 14:39:46 -0500123 */
Matthew Barth41a34082021-01-27 15:31:48 -0600124 virtual void run(Zone& zone, const Group& group) = 0;
Matthew Barth0c4b1572020-10-22 14:39:46 -0500125};
126
127/**
128 * @class ActionFactory - Factory for actions
129 *
130 * Factory that registers and retrieves actions based on a given name.
131 */
132class ActionFactory
133{
134 public:
135 ActionFactory() = delete;
136 ActionFactory(const ActionFactory&) = delete;
137 ActionFactory(ActionFactory&&) = delete;
138 ActionFactory& operator=(const ActionFactory&) = delete;
139 ActionFactory& operator=(ActionFactory&&) = delete;
140 ~ActionFactory() = default;
141
142 /**
143 * @brief Registers an action
144 *
145 * Registers an action as being available for configuration use. The action
146 * is registered by its name and a function used to create the action
147 * object. An action fails to be registered when another action of the same
148 * name has already been registered. Actions with the same name would cause
149 * undefined behavior, therefore are not allowed.
150 *
151 * Actions are registered prior to starting main().
152 *
153 * @param[in] name - Name of the action to register
154 *
155 * @return The action was registered, otherwise an exception is thrown.
156 */
157 template <typename T>
158 static bool regAction(const std::string& name)
159 {
160 auto it = actions.find(name);
161 if (it == actions.end())
162 {
163 actions[name] = &createAction<T>;
164 }
165 else
166 {
167 log<level::ERR>(
168 fmt::format("Action '{}' is already registered", name).c_str());
169 throw std::runtime_error("Actions with the same name found");
170 }
171
172 return true;
173 }
174
175 /**
176 * @brief Gets a registered action's object
177 *
178 * Gets a registered action's object of a given name from the JSON
179 * configuration data provided.
180 *
181 * @param[in] name - Name of the action to create/get
182 * @param[in] jsonObj - JSON object for the action
183 *
184 * @return Pointer to the action object.
185 */
186 static std::unique_ptr<ActionBase> getAction(const std::string& name,
187 const json& jsonObj)
188 {
189 auto it = actions.find(name);
190 if (it != actions.end())
191 {
192 return it->second(jsonObj);
193 }
194 else
195 {
196 // Construct list of available actions
197 auto acts = std::accumulate(
198 std::next(actions.begin()), actions.end(),
199 actions.begin()->first, [](auto list, auto act) {
200 return std::move(list) + ", " + act.first;
201 });
202 log<level::ERR>(
203 fmt::format("Action '{}' is not registered", name).c_str(),
204 entry("AVAILABLE_ACTIONS=%s", acts.c_str()));
205 throw std::runtime_error("Unsupported action name given");
206 }
207 }
208
209 private:
210 /* Map to store the available actions and their creation functions */
211 static inline std::map<
212 std::string, std::function<std::unique_ptr<ActionBase>(const json&)>>
213 actions;
214};
215
216/**
217 * @class ActionRegister - Registers an action class
218 *
219 * Base action registration class that is extended by an action object so
220 * that action is registered and available for use.
221 */
222template <typename T>
223class ActionRegister
224{
225 public:
226 ActionRegister(const ActionRegister&) = delete;
227 ActionRegister(ActionRegister&&) = delete;
228 ActionRegister& operator=(const ActionRegister&) = delete;
229 ActionRegister& operator=(ActionRegister&&) = delete;
230 virtual ~ActionRegister() = default;
231 ActionRegister()
232 {
233 // Templates instantiated when used, need to assign a value
234 // here so the compiler doesnt remove it
235 registered = true;
236 }
237
238 private:
239 /* Register actions in the factory */
240 static inline bool registered = ActionFactory::regAction<T>(T::name);
241};
242
243} // namespace phosphor::fan::control::json