blob: cfee7af3cde87606e0b938ec52f697ba33d537b6 [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
18#include "types.hpp"
Matthew Barth41a34082021-01-27 15:31:48 -060019#include "zone.hpp"
Matthew Barth0c4b1572020-10-22 14:39:46 -050020
21#include <fmt/format.h>
22
23#include <nlohmann/json.hpp>
24#include <phosphor-logging/log.hpp>
25
26#include <functional>
27#include <iterator>
28#include <map>
29#include <memory>
30#include <numeric>
31
32namespace phosphor::fan::control::json
33{
34
35using json = nlohmann::json;
36using namespace phosphor::logging;
37
38/**
39 * @brief Function used in creating action objects
40 *
41 * @param[in] jsonObj - JSON object for the action
42 *
43 * Creates an action object given the JSON configuration
44 */
45template <typename T>
46std::unique_ptr<T> createAction(const json& jsonObj)
47{
48 return std::make_unique<T>(jsonObj);
49}
50
51/**
52 * @class ActionBase - Base action object
53 *
54 * Base class for fan control's event actions
55 */
56class ActionBase
57{
58 public:
59 ActionBase() = delete;
60 ActionBase(const ActionBase&) = delete;
61 ActionBase(ActionBase&&) = delete;
62 ActionBase& operator=(const ActionBase&) = delete;
63 ActionBase& operator=(ActionBase&&) = delete;
64 virtual ~ActionBase() = default;
Matthew Barth41a34082021-01-27 15:31:48 -060065
66 /**
67 * @brief Base action object
68 *
69 * All actions derived from this base action object must be given a name
70 * that uniquely identifies the action.
71 *
72 * @param[in] name - Unique name of the action
73 */
Matthew Barth0c4b1572020-10-22 14:39:46 -050074 explicit ActionBase(const std::string& name) : _name(name)
75 {}
76
77 /**
Matthew Barth41a34082021-01-27 15:31:48 -060078 * @brief Run the action
Matthew Barth0c4b1572020-10-22 14:39:46 -050079 *
Matthew Barth41a34082021-01-27 15:31:48 -060080 * Run the action function associated to the derived action object
81 * that performs a specific task against a group of dbus objects on a zone
82 * configured by a user.
Matthew Barth0c4b1572020-10-22 14:39:46 -050083 *
Matthew Barth41a34082021-01-27 15:31:48 -060084 * @param[in] zone - Zone to run the action on
85 * @param[in] group - Group of dbus objects the action runs against
Matthew Barth0c4b1572020-10-22 14:39:46 -050086 */
Matthew Barth41a34082021-01-27 15:31:48 -060087 virtual void run(Zone& zone, const Group& group) = 0;
Matthew Barth0c4b1572020-10-22 14:39:46 -050088
89 /**
90 * @brief Get the action's name
91 *
92 * @return Name of the action
93 */
94 inline const auto& getName() const
95 {
96 return _name;
97 }
98
99 private:
100 /* The action's name that is used within the JSON configuration */
101 const std::string _name;
102};
103
104/**
105 * @class ActionFactory - Factory for actions
106 *
107 * Factory that registers and retrieves actions based on a given name.
108 */
109class ActionFactory
110{
111 public:
112 ActionFactory() = delete;
113 ActionFactory(const ActionFactory&) = delete;
114 ActionFactory(ActionFactory&&) = delete;
115 ActionFactory& operator=(const ActionFactory&) = delete;
116 ActionFactory& operator=(ActionFactory&&) = delete;
117 ~ActionFactory() = default;
118
119 /**
120 * @brief Registers an action
121 *
122 * Registers an action as being available for configuration use. The action
123 * is registered by its name and a function used to create the action
124 * object. An action fails to be registered when another action of the same
125 * name has already been registered. Actions with the same name would cause
126 * undefined behavior, therefore are not allowed.
127 *
128 * Actions are registered prior to starting main().
129 *
130 * @param[in] name - Name of the action to register
131 *
132 * @return The action was registered, otherwise an exception is thrown.
133 */
134 template <typename T>
135 static bool regAction(const std::string& name)
136 {
137 auto it = actions.find(name);
138 if (it == actions.end())
139 {
140 actions[name] = &createAction<T>;
141 }
142 else
143 {
144 log<level::ERR>(
145 fmt::format("Action '{}' is already registered", name).c_str());
146 throw std::runtime_error("Actions with the same name found");
147 }
148
149 return true;
150 }
151
152 /**
153 * @brief Gets a registered action's object
154 *
155 * Gets a registered action's object of a given name from the JSON
156 * configuration data provided.
157 *
158 * @param[in] name - Name of the action to create/get
159 * @param[in] jsonObj - JSON object for the action
160 *
161 * @return Pointer to the action object.
162 */
163 static std::unique_ptr<ActionBase> getAction(const std::string& name,
164 const json& jsonObj)
165 {
166 auto it = actions.find(name);
167 if (it != actions.end())
168 {
169 return it->second(jsonObj);
170 }
171 else
172 {
173 // Construct list of available actions
174 auto acts = std::accumulate(
175 std::next(actions.begin()), actions.end(),
176 actions.begin()->first, [](auto list, auto act) {
177 return std::move(list) + ", " + act.first;
178 });
179 log<level::ERR>(
180 fmt::format("Action '{}' is not registered", name).c_str(),
181 entry("AVAILABLE_ACTIONS=%s", acts.c_str()));
182 throw std::runtime_error("Unsupported action name given");
183 }
184 }
185
186 private:
187 /* Map to store the available actions and their creation functions */
188 static inline std::map<
189 std::string, std::function<std::unique_ptr<ActionBase>(const json&)>>
190 actions;
191};
192
193/**
194 * @class ActionRegister - Registers an action class
195 *
196 * Base action registration class that is extended by an action object so
197 * that action is registered and available for use.
198 */
199template <typename T>
200class ActionRegister
201{
202 public:
203 ActionRegister(const ActionRegister&) = delete;
204 ActionRegister(ActionRegister&&) = delete;
205 ActionRegister& operator=(const ActionRegister&) = delete;
206 ActionRegister& operator=(ActionRegister&&) = delete;
207 virtual ~ActionRegister() = default;
208 ActionRegister()
209 {
210 // Templates instantiated when used, need to assign a value
211 // here so the compiler doesnt remove it
212 registered = true;
213 }
214
215 private:
216 /* Register actions in the factory */
217 static inline bool registered = ActionFactory::regAction<T>(T::name);
218};
219
220} // namespace phosphor::fan::control::json