blob: e4c9f8e3d04175b6a7b8704270b3f3a257e4122d [file] [log] [blame]
Matthew Barth3174e722020-09-15 15:13:40 -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#include "event.hpp"
17
Matthew Barth776ca562021-03-31 09:50:58 -050018#include "action.hpp"
Matthew Barthe5578602021-03-30 12:53:24 -050019#include "config_base.hpp"
Matthew Barth391ade02021-01-15 14:33:21 -060020#include "group.hpp"
Matthew Barthe5578602021-03-30 12:53:24 -050021#include "manager.hpp"
Matthew Barth9403a212021-05-17 09:31:50 -050022#include "sdbusplus.hpp"
Matthew Barth54b5a242021-05-21 11:02:52 -050023#include "trigger.hpp"
Matthew Barthe5578602021-03-30 12:53:24 -050024
Matthew Barth3174e722020-09-15 15:13:40 -050025#include <nlohmann/json.hpp>
Anwaar Hadi64b5ac22025-04-04 23:54:53 +000026#include <phosphor-logging/lg2.hpp>
Matthew Barth3174e722020-09-15 15:13:40 -050027#include <sdbusplus/bus.hpp>
28
Matthew Barthe5578602021-03-30 12:53:24 -050029#include <algorithm>
Matthew Barth12bae962021-01-15 16:18:11 -060030#include <optional>
Matthew Barth12bae962021-01-15 16:18:11 -060031
Matthew Barth3174e722020-09-15 15:13:40 -050032namespace phosphor::fan::control::json
33{
34
35using json = nlohmann::json;
Matthew Barth3174e722020-09-15 15:13:40 -050036
Matthew Barth3695ac32021-10-06 14:55:30 -050037std::map<configKey, std::unique_ptr<Group>> Event::allGroups;
38
Matthew Barth9403a212021-05-17 09:31:50 -050039Event::Event(const json& jsonObj, Manager* mgr,
Matthew Barth9f1632e2021-03-31 15:51:50 -050040 std::map<configKey, std::unique_ptr<Zone>>& zones) :
Patrick Williamsdfddd642024-08-16 15:21:51 -040041 ConfigBase(jsonObj), _bus(util::SDBusPlus::getBus()), _manager(mgr),
42 _zones(zones)
Matthew Barth3174e722020-09-15 15:13:40 -050043{
Matthew Barth619dc0f2021-05-20 09:27:02 -050044 // Event groups are optional
45 if (jsonObj.contains("groups"))
Matthew Barth3174e722020-09-15 15:13:40 -050046 {
Matthew Barthe386fbb2021-06-30 14:33:55 -050047 setGroups(jsonObj, _profiles, _groups);
Matthew Barth3174e722020-09-15 15:13:40 -050048 }
Matthew Barth619dc0f2021-05-20 09:27:02 -050049 // Event actions are optional
50 if (jsonObj.contains("actions"))
Matthew Barth3174e722020-09-15 15:13:40 -050051 {
Matthew Barth619dc0f2021-05-20 09:27:02 -050052 setActions(jsonObj);
Matthew Barth3174e722020-09-15 15:13:40 -050053 }
Matthew Barth619dc0f2021-05-20 09:27:02 -050054 setTriggers(jsonObj);
Matthew Barth3174e722020-09-15 15:13:40 -050055}
56
Matthew Barth54b5a242021-05-21 11:02:52 -050057void Event::enable()
58{
Matt Spinlerd1f97f42021-10-29 16:19:24 -050059 for (const auto& [type, trigger] : _triggers)
Matthew Barth54b5a242021-05-21 11:02:52 -050060 {
Matt Spinlerd1f97f42021-10-29 16:19:24 -050061 // Don't call the powerOn or powerOff triggers
62 if (type.find("power") == std::string::npos)
63 {
64 trigger(getName(), _manager, _groups, _actions);
65 }
66 }
67}
68
69void Event::powerOn()
70{
71 for (const auto& [type, trigger] : _triggers)
72 {
73 if (type == "poweron")
74 {
75 trigger(getName(), _manager, _groups, _actions);
76 }
77 }
78}
79
80void Event::powerOff()
81{
82 for (const auto& [type, trigger] : _triggers)
83 {
84 if (type == "poweroff")
85 {
86 trigger(getName(), _manager, _groups, _actions);
87 }
Matthew Barth54b5a242021-05-21 11:02:52 -050088 }
89}
90
Patrick Williams4fa67aa2025-02-03 14:28:47 -050091std::map<configKey, std::unique_ptr<Group>>& Event::getAllGroups(
92 bool loadGroups)
Matthew Barthc8bde4a2021-05-19 15:34:49 -050093{
Matthew Barth3695ac32021-10-06 14:55:30 -050094 if (allGroups.empty() && loadGroups)
95 {
96 allGroups = Manager::getConfig<Group>(true);
97 }
98
99 return allGroups;
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500100}
101
Matthew Barth46b34482021-04-06 11:27:23 -0500102void Event::configGroup(Group& group, const json& jsonObj)
103{
104 if (!jsonObj.contains("interface") || !jsonObj.contains("property") ||
105 !jsonObj["property"].contains("name"))
106 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000107 lg2::error("Missing required group attribute", "JSON", jsonObj.dump());
Matthew Barth46b34482021-04-06 11:27:23 -0500108 throw std::runtime_error("Missing required group attribute");
109 }
110
111 // Get the group members' interface
112 auto intf = jsonObj["interface"].get<std::string>();
113 group.setInterface(intf);
114
115 // Get the group members' property name
116 auto prop = jsonObj["property"]["name"].get<std::string>();
117 group.setProperty(prop);
118
119 // Get the group members' data type
120 if (jsonObj["property"].contains("type"))
121 {
122 std::optional<std::string> type =
123 jsonObj["property"]["type"].get<std::string>();
124 group.setType(type);
125 }
126
127 // Get the group members' expected value
128 if (jsonObj["property"].contains("value"))
129 {
130 std::optional<PropertyVariantType> value =
131 getJsonValue(jsonObj["property"]["value"]);
132 group.setValue(value);
133 }
134}
135
Matthew Barthe386fbb2021-06-30 14:33:55 -0500136void Event::setGroups(const json& jsonObj,
137 const std::vector<std::string>& profiles,
138 std::vector<Group>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500139{
Matthew Barthe386fbb2021-06-30 14:33:55 -0500140 if (jsonObj.contains("groups"))
Matthew Barth3174e722020-09-15 15:13:40 -0500141 {
Matthew Barth3695ac32021-10-06 14:55:30 -0500142 auto& availGroups = getAllGroups();
Matthew Barthe386fbb2021-06-30 14:33:55 -0500143 for (const auto& jsonGrp : jsonObj["groups"])
Matthew Barth3174e722020-09-15 15:13:40 -0500144 {
Matthew Barthe386fbb2021-06-30 14:33:55 -0500145 if (!jsonGrp.contains("name"))
146 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000147 lg2::error("Missing required group name attribute", "JSON",
148 jsonGrp.dump());
149 throw std::runtime_error(
150 "Missing required group name attribute");
Matthew Barthe386fbb2021-06-30 14:33:55 -0500151 }
Matthew Barth12bae962021-01-15 16:18:11 -0600152
Matthew Barthe386fbb2021-06-30 14:33:55 -0500153 configKey eventProfile =
154 std::make_pair(jsonGrp["name"].get<std::string>(), profiles);
Patrick Williamsdfddd642024-08-16 15:21:51 -0400155 auto grpEntry = std::find_if(
156 availGroups.begin(), availGroups.end(),
157 [&eventProfile](const auto& grp) {
158 return Manager::inConfig(grp.first, eventProfile);
159 });
Matthew Barthe386fbb2021-06-30 14:33:55 -0500160 if (grpEntry != availGroups.end())
161 {
162 auto group = Group(*grpEntry->second);
163 configGroup(group, jsonGrp);
164 groups.emplace_back(group);
165 }
Matthew Barth12bae962021-01-15 16:18:11 -0600166 }
Matthew Barthe5578602021-03-30 12:53:24 -0500167 }
Matthew Barth3174e722020-09-15 15:13:40 -0500168}
169
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500170void Event::setActions(const json& jsonObj)
Matthew Barth3174e722020-09-15 15:13:40 -0500171{
Matthew Barth46b34482021-04-06 11:27:23 -0500172 for (const auto& jsonAct : jsonObj["actions"])
Matthew Barth3174e722020-09-15 15:13:40 -0500173 {
Matthew Barth46b34482021-04-06 11:27:23 -0500174 if (!jsonAct.contains("name"))
Matthew Barth3174e722020-09-15 15:13:40 -0500175 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000176 lg2::error("Missing required event action name", "JSON",
177 jsonAct.dump());
Matthew Barth3174e722020-09-15 15:13:40 -0500178 throw std::runtime_error("Missing required event action name");
179 }
Matthew Barth46b34482021-04-06 11:27:23 -0500180
Matthew Barth46b34482021-04-06 11:27:23 -0500181 // Determine list of zones action should be run against
182 std::vector<std::reference_wrapper<Zone>> actionZones;
Matthew Bartha8ea0912021-05-03 14:33:52 -0500183 if (!jsonAct.contains("zones"))
Matthew Barth46b34482021-04-06 11:27:23 -0500184 {
185 // No zones configured on the action results in the action running
186 // against all zones matching the event's active profiles
187 for (const auto& zone : _zones)
188 {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400189 configKey eventProfile =
190 std::make_pair(zone.second->getName(), _profiles);
191 auto zoneEntry = std::find_if(
192 _zones.begin(), _zones.end(),
193 [&eventProfile](const auto& z) {
194 return Manager::inConfig(z.first, eventProfile);
195 });
Matthew Barth46b34482021-04-06 11:27:23 -0500196 if (zoneEntry != _zones.end())
197 {
198 actionZones.emplace_back(*zoneEntry->second);
199 }
200 }
201 }
202 else
203 {
204 // Zones configured on the action result in the action only running
205 // against those zones if they match the event's active profiles
Matthew Bartha8ea0912021-05-03 14:33:52 -0500206 for (const auto& jsonZone : jsonAct["zones"])
Matthew Barth46b34482021-04-06 11:27:23 -0500207 {
208 configKey eventProfile =
209 std::make_pair(jsonZone.get<std::string>(), _profiles);
Patrick Williamsdfddd642024-08-16 15:21:51 -0400210 auto zoneEntry = std::find_if(
211 _zones.begin(), _zones.end(),
212 [&eventProfile](const auto& z) {
213 return Manager::inConfig(z.first, eventProfile);
214 });
Matthew Barth46b34482021-04-06 11:27:23 -0500215 if (zoneEntry != _zones.end())
216 {
217 actionZones.emplace_back(*zoneEntry->second);
218 }
219 }
220 }
221 if (actionZones.empty())
222 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000223 lg2::debug(
224 "No zones configured for event {EVENT_NAME}'s action {ACTION} "
225 "based on the active profile(s)",
226 "EVENT_NAME", getName(), "ACTION",
227 jsonAct["name"].get<std::string>());
Matthew Barth46b34482021-04-06 11:27:23 -0500228 }
229
Matthew Barth5fb52682021-09-29 13:57:02 -0500230 // Action specific groups, if any given, will override the use of event
231 // groups in the action(s)
232 std::vector<Group> actionGroups;
233 setGroups(jsonAct, _profiles, actionGroups);
234 if (!actionGroups.empty())
Matthew Barth776ca562021-03-31 09:50:58 -0500235 {
Matthew Barth5fb52682021-09-29 13:57:02 -0500236 // Create the action for the event using the action's groups
237 auto actObj = ActionFactory::getAction(
238 jsonAct["name"].get<std::string>(), jsonAct,
239 std::move(actionGroups), std::move(actionZones));
240 if (actObj)
241 {
Matt Spinlere6fc2102022-04-07 14:31:29 -0500242 actObj->setEventName(_name);
Matthew Barth5fb52682021-09-29 13:57:02 -0500243 _actions.emplace_back(std::move(actObj));
244 }
245 }
246 else
247 {
248 // Create the action for the event using the event's groups
249 auto actObj = ActionFactory::getAction(
250 jsonAct["name"].get<std::string>(), jsonAct, _groups,
251 std::move(actionZones));
252 if (actObj)
253 {
Matt Spinlere6fc2102022-04-07 14:31:29 -0500254 actObj->setEventName(_name);
Matthew Barth5fb52682021-09-29 13:57:02 -0500255 _actions.emplace_back(std::move(actObj));
256 }
257 }
258
259 if (actionGroups.empty() && _groups.empty())
260 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000261 lg2::debug(
262 "No groups configured for event {EVENT_NAME}'s action {ACTION} "
263 "based on the active profile(s)",
264 "EVENT_NAME", getName(), "ACTION",
265 jsonAct["name"].get<std::string>());
Matthew Barth776ca562021-03-31 09:50:58 -0500266 }
Matthew Barth3174e722020-09-15 15:13:40 -0500267 }
268}
269
Matthew Barth9f1632e2021-03-31 15:51:50 -0500270void Event::setTriggers(const json& jsonObj)
271{
272 if (!jsonObj.contains("triggers"))
273 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000274 lg2::error("Missing required event triggers list", "JSON",
275 jsonObj.dump());
Matthew Barth9f1632e2021-03-31 15:51:50 -0500276 throw std::runtime_error("Missing required event triggers list");
277 }
Matthew Barth0620be72021-04-14 13:31:12 -0500278 for (const auto& jsonTrig : jsonObj["triggers"])
279 {
280 if (!jsonTrig.contains("class"))
281 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000282 lg2::error("Missing required event trigger class", "JSON",
283 jsonTrig.dump());
Matthew Barth0620be72021-04-14 13:31:12 -0500284 throw std::runtime_error("Missing required event trigger class");
285 }
286 // The class of trigger used to run the event actions
287 auto tClass = jsonTrig["class"].get<std::string>();
288 std::transform(tClass.begin(), tClass.end(), tClass.begin(), tolower);
289 auto trigFunc = trigger::triggers.find(tClass);
290 if (trigFunc != trigger::triggers.end())
291 {
Matthew Barth54b5a242021-05-21 11:02:52 -0500292 _triggers.emplace_back(
Matt Spinlerd1f97f42021-10-29 16:19:24 -0500293 trigFunc->first,
Matthew Barthb6ebac82021-05-21 11:15:33 -0500294 trigFunc->second(jsonTrig, getName(), _actions));
Matthew Barth0620be72021-04-14 13:31:12 -0500295 }
296 else
297 {
298 // Construct list of available triggers
299 auto availTrigs = std::accumulate(
300 std::next(trigger::triggers.begin()), trigger::triggers.end(),
301 trigger::triggers.begin()->first, [](auto list, auto trig) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400302 return std::move(list) + ", " + trig.first;
303 });
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000304 lg2::error(
305 "Trigger '{TRIGGER}' is not recognized. Available triggers are {AVAILABLE_TRIGGERS}",
306 "TRIGGER", tClass, "AVAILABLE_TRIGGERS", availTrigs);
Matthew Barth0620be72021-04-14 13:31:12 -0500307 throw std::runtime_error("Unsupported trigger class name given");
308 }
309 }
Matthew Barth9f1632e2021-03-31 15:51:50 -0500310}
311
Matt Spinlerc3eb7b32022-04-25 15:44:16 -0500312json Event::dump() const
313{
314 json actionData;
315 std::for_each(_actions.begin(), _actions.end(),
316 [&actionData](const auto& action) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400317 actionData[action->getUniqueName()] = action->dump();
318 });
Matt Spinlerc3eb7b32022-04-25 15:44:16 -0500319
320 std::vector<std::string> groupData;
321 std::for_each(_groups.begin(), _groups.end(),
322 [&groupData](const auto& group) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400323 groupData.push_back(group.getName());
324 });
Matt Spinlerc3eb7b32022-04-25 15:44:16 -0500325
326 json eventData;
327 eventData["groups"] = groupData;
328 eventData["actions"] = actionData;
329
330 return eventData;
331}
332
Matthew Barth3174e722020-09-15 15:13:40 -0500333} // namespace phosphor::fan::control::json