blob: e129e1c1077e92eb3154a07c084b3cf1aba9a3b0 [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) :
Matt Spinlerc7f07a22025-07-29 15:37:56 -050041 ConfigBase(jsonObj), _manager(mgr), _zones(zones)
Matthew Barth3174e722020-09-15 15:13:40 -050042{
Matthew Barth619dc0f2021-05-20 09:27:02 -050043 // Event groups are optional
44 if (jsonObj.contains("groups"))
Matthew Barth3174e722020-09-15 15:13:40 -050045 {
Matthew Barthe386fbb2021-06-30 14:33:55 -050046 setGroups(jsonObj, _profiles, _groups);
Matthew Barth3174e722020-09-15 15:13:40 -050047 }
Matthew Barth619dc0f2021-05-20 09:27:02 -050048 // Event actions are optional
49 if (jsonObj.contains("actions"))
Matthew Barth3174e722020-09-15 15:13:40 -050050 {
Matthew Barth619dc0f2021-05-20 09:27:02 -050051 setActions(jsonObj);
Matthew Barth3174e722020-09-15 15:13:40 -050052 }
Matthew Barth619dc0f2021-05-20 09:27:02 -050053 setTriggers(jsonObj);
Matthew Barth3174e722020-09-15 15:13:40 -050054}
55
Matthew Barth54b5a242021-05-21 11:02:52 -050056void Event::enable()
57{
Matt Spinlerd1f97f42021-10-29 16:19:24 -050058 for (const auto& [type, trigger] : _triggers)
Matthew Barth54b5a242021-05-21 11:02:52 -050059 {
Matt Spinlerd1f97f42021-10-29 16:19:24 -050060 // Don't call the powerOn or powerOff triggers
61 if (type.find("power") == std::string::npos)
62 {
63 trigger(getName(), _manager, _groups, _actions);
64 }
65 }
66}
67
68void Event::powerOn()
69{
70 for (const auto& [type, trigger] : _triggers)
71 {
72 if (type == "poweron")
73 {
74 trigger(getName(), _manager, _groups, _actions);
75 }
76 }
77}
78
79void Event::powerOff()
80{
81 for (const auto& [type, trigger] : _triggers)
82 {
83 if (type == "poweroff")
84 {
85 trigger(getName(), _manager, _groups, _actions);
86 }
Matthew Barth54b5a242021-05-21 11:02:52 -050087 }
88}
89
Patrick Williams4fa67aa2025-02-03 14:28:47 -050090std::map<configKey, std::unique_ptr<Group>>& Event::getAllGroups(
91 bool loadGroups)
Matthew Barthc8bde4a2021-05-19 15:34:49 -050092{
Matthew Barth3695ac32021-10-06 14:55:30 -050093 if (allGroups.empty() && loadGroups)
94 {
95 allGroups = Manager::getConfig<Group>(true);
96 }
97
98 return allGroups;
Matthew Barthc8bde4a2021-05-19 15:34:49 -050099}
100
Matthew Barth46b34482021-04-06 11:27:23 -0500101void Event::configGroup(Group& group, const json& jsonObj)
102{
103 if (!jsonObj.contains("interface") || !jsonObj.contains("property") ||
104 !jsonObj["property"].contains("name"))
105 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000106 lg2::error("Missing required group attribute", "JSON", jsonObj.dump());
Matthew Barth46b34482021-04-06 11:27:23 -0500107 throw std::runtime_error("Missing required group attribute");
108 }
109
110 // Get the group members' interface
111 auto intf = jsonObj["interface"].get<std::string>();
112 group.setInterface(intf);
113
114 // Get the group members' property name
115 auto prop = jsonObj["property"]["name"].get<std::string>();
116 group.setProperty(prop);
117
118 // Get the group members' data type
119 if (jsonObj["property"].contains("type"))
120 {
121 std::optional<std::string> type =
122 jsonObj["property"]["type"].get<std::string>();
123 group.setType(type);
124 }
125
126 // Get the group members' expected value
127 if (jsonObj["property"].contains("value"))
128 {
129 std::optional<PropertyVariantType> value =
130 getJsonValue(jsonObj["property"]["value"]);
131 group.setValue(value);
132 }
133}
134
Matthew Barthe386fbb2021-06-30 14:33:55 -0500135void Event::setGroups(const json& jsonObj,
136 const std::vector<std::string>& profiles,
137 std::vector<Group>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500138{
Matthew Barthe386fbb2021-06-30 14:33:55 -0500139 if (jsonObj.contains("groups"))
Matthew Barth3174e722020-09-15 15:13:40 -0500140 {
Matthew Barth3695ac32021-10-06 14:55:30 -0500141 auto& availGroups = getAllGroups();
Matthew Barthe386fbb2021-06-30 14:33:55 -0500142 for (const auto& jsonGrp : jsonObj["groups"])
Matthew Barth3174e722020-09-15 15:13:40 -0500143 {
Matthew Barthe386fbb2021-06-30 14:33:55 -0500144 if (!jsonGrp.contains("name"))
145 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000146 lg2::error("Missing required group name attribute", "JSON",
147 jsonGrp.dump());
148 throw std::runtime_error(
149 "Missing required group name attribute");
Matthew Barthe386fbb2021-06-30 14:33:55 -0500150 }
Matthew Barth12bae962021-01-15 16:18:11 -0600151
Matthew Barthe386fbb2021-06-30 14:33:55 -0500152 configKey eventProfile =
153 std::make_pair(jsonGrp["name"].get<std::string>(), profiles);
Patrick Williamsdfddd642024-08-16 15:21:51 -0400154 auto grpEntry = std::find_if(
155 availGroups.begin(), availGroups.end(),
156 [&eventProfile](const auto& grp) {
157 return Manager::inConfig(grp.first, eventProfile);
158 });
Matthew Barthe386fbb2021-06-30 14:33:55 -0500159 if (grpEntry != availGroups.end())
160 {
161 auto group = Group(*grpEntry->second);
162 configGroup(group, jsonGrp);
163 groups.emplace_back(group);
164 }
Matthew Barth12bae962021-01-15 16:18:11 -0600165 }
Matthew Barthe5578602021-03-30 12:53:24 -0500166 }
Matthew Barth3174e722020-09-15 15:13:40 -0500167}
168
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500169void Event::setActions(const json& jsonObj)
Matthew Barth3174e722020-09-15 15:13:40 -0500170{
Matthew Barth46b34482021-04-06 11:27:23 -0500171 for (const auto& jsonAct : jsonObj["actions"])
Matthew Barth3174e722020-09-15 15:13:40 -0500172 {
Matthew Barth46b34482021-04-06 11:27:23 -0500173 if (!jsonAct.contains("name"))
Matthew Barth3174e722020-09-15 15:13:40 -0500174 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000175 lg2::error("Missing required event action name", "JSON",
176 jsonAct.dump());
Matthew Barth3174e722020-09-15 15:13:40 -0500177 throw std::runtime_error("Missing required event action name");
178 }
Matthew Barth46b34482021-04-06 11:27:23 -0500179
Matthew Barth46b34482021-04-06 11:27:23 -0500180 // Determine list of zones action should be run against
181 std::vector<std::reference_wrapper<Zone>> actionZones;
Matthew Bartha8ea0912021-05-03 14:33:52 -0500182 if (!jsonAct.contains("zones"))
Matthew Barth46b34482021-04-06 11:27:23 -0500183 {
184 // No zones configured on the action results in the action running
185 // against all zones matching the event's active profiles
186 for (const auto& zone : _zones)
187 {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400188 configKey eventProfile =
189 std::make_pair(zone.second->getName(), _profiles);
190 auto zoneEntry = std::find_if(
191 _zones.begin(), _zones.end(),
192 [&eventProfile](const auto& z) {
193 return Manager::inConfig(z.first, eventProfile);
194 });
Matthew Barth46b34482021-04-06 11:27:23 -0500195 if (zoneEntry != _zones.end())
196 {
197 actionZones.emplace_back(*zoneEntry->second);
198 }
199 }
200 }
201 else
202 {
203 // Zones configured on the action result in the action only running
204 // against those zones if they match the event's active profiles
Matthew Bartha8ea0912021-05-03 14:33:52 -0500205 for (const auto& jsonZone : jsonAct["zones"])
Matthew Barth46b34482021-04-06 11:27:23 -0500206 {
207 configKey eventProfile =
208 std::make_pair(jsonZone.get<std::string>(), _profiles);
Patrick Williamsdfddd642024-08-16 15:21:51 -0400209 auto zoneEntry = std::find_if(
210 _zones.begin(), _zones.end(),
211 [&eventProfile](const auto& z) {
212 return Manager::inConfig(z.first, eventProfile);
213 });
Matthew Barth46b34482021-04-06 11:27:23 -0500214 if (zoneEntry != _zones.end())
215 {
216 actionZones.emplace_back(*zoneEntry->second);
217 }
218 }
219 }
220 if (actionZones.empty())
221 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000222 lg2::debug(
223 "No zones configured for event {EVENT_NAME}'s action {ACTION} "
224 "based on the active profile(s)",
225 "EVENT_NAME", getName(), "ACTION",
226 jsonAct["name"].get<std::string>());
Matthew Barth46b34482021-04-06 11:27:23 -0500227 }
228
Matthew Barth5fb52682021-09-29 13:57:02 -0500229 // Action specific groups, if any given, will override the use of event
230 // groups in the action(s)
231 std::vector<Group> actionGroups;
232 setGroups(jsonAct, _profiles, actionGroups);
233 if (!actionGroups.empty())
Matthew Barth776ca562021-03-31 09:50:58 -0500234 {
Matthew Barth5fb52682021-09-29 13:57:02 -0500235 // Create the action for the event using the action's groups
236 auto actObj = ActionFactory::getAction(
237 jsonAct["name"].get<std::string>(), jsonAct,
238 std::move(actionGroups), std::move(actionZones));
239 if (actObj)
240 {
Matt Spinlere6fc2102022-04-07 14:31:29 -0500241 actObj->setEventName(_name);
Matthew Barth5fb52682021-09-29 13:57:02 -0500242 _actions.emplace_back(std::move(actObj));
243 }
244 }
245 else
246 {
247 // Create the action for the event using the event's groups
248 auto actObj = ActionFactory::getAction(
249 jsonAct["name"].get<std::string>(), jsonAct, _groups,
250 std::move(actionZones));
251 if (actObj)
252 {
Matt Spinlere6fc2102022-04-07 14:31:29 -0500253 actObj->setEventName(_name);
Matthew Barth5fb52682021-09-29 13:57:02 -0500254 _actions.emplace_back(std::move(actObj));
255 }
256 }
257
258 if (actionGroups.empty() && _groups.empty())
259 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000260 lg2::debug(
261 "No groups configured for event {EVENT_NAME}'s action {ACTION} "
262 "based on the active profile(s)",
263 "EVENT_NAME", getName(), "ACTION",
264 jsonAct["name"].get<std::string>());
Matthew Barth776ca562021-03-31 09:50:58 -0500265 }
Matthew Barth3174e722020-09-15 15:13:40 -0500266 }
267}
268
Matthew Barth9f1632e2021-03-31 15:51:50 -0500269void Event::setTriggers(const json& jsonObj)
270{
271 if (!jsonObj.contains("triggers"))
272 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000273 lg2::error("Missing required event triggers list", "JSON",
274 jsonObj.dump());
Matthew Barth9f1632e2021-03-31 15:51:50 -0500275 throw std::runtime_error("Missing required event triggers list");
276 }
Matthew Barth0620be72021-04-14 13:31:12 -0500277 for (const auto& jsonTrig : jsonObj["triggers"])
278 {
279 if (!jsonTrig.contains("class"))
280 {
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000281 lg2::error("Missing required event trigger class", "JSON",
282 jsonTrig.dump());
Matthew Barth0620be72021-04-14 13:31:12 -0500283 throw std::runtime_error("Missing required event trigger class");
284 }
285 // The class of trigger used to run the event actions
286 auto tClass = jsonTrig["class"].get<std::string>();
287 std::transform(tClass.begin(), tClass.end(), tClass.begin(), tolower);
288 auto trigFunc = trigger::triggers.find(tClass);
289 if (trigFunc != trigger::triggers.end())
290 {
Matthew Barth54b5a242021-05-21 11:02:52 -0500291 _triggers.emplace_back(
Matt Spinlerd1f97f42021-10-29 16:19:24 -0500292 trigFunc->first,
Matthew Barthb6ebac82021-05-21 11:15:33 -0500293 trigFunc->second(jsonTrig, getName(), _actions));
Matthew Barth0620be72021-04-14 13:31:12 -0500294 }
295 else
296 {
297 // Construct list of available triggers
298 auto availTrigs = std::accumulate(
299 std::next(trigger::triggers.begin()), trigger::triggers.end(),
300 trigger::triggers.begin()->first, [](auto list, auto trig) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400301 return std::move(list) + ", " + trig.first;
302 });
Anwaar Hadi64b5ac22025-04-04 23:54:53 +0000303 lg2::error(
304 "Trigger '{TRIGGER}' is not recognized. Available triggers are {AVAILABLE_TRIGGERS}",
305 "TRIGGER", tClass, "AVAILABLE_TRIGGERS", availTrigs);
Matthew Barth0620be72021-04-14 13:31:12 -0500306 throw std::runtime_error("Unsupported trigger class name given");
307 }
308 }
Matthew Barth9f1632e2021-03-31 15:51:50 -0500309}
310
Matt Spinlerc3eb7b32022-04-25 15:44:16 -0500311json Event::dump() const
312{
313 json actionData;
314 std::for_each(_actions.begin(), _actions.end(),
315 [&actionData](const auto& action) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400316 actionData[action->getUniqueName()] = action->dump();
317 });
Matt Spinlerc3eb7b32022-04-25 15:44:16 -0500318
319 std::vector<std::string> groupData;
320 std::for_each(_groups.begin(), _groups.end(),
321 [&groupData](const auto& group) {
Patrick Williamsdfddd642024-08-16 15:21:51 -0400322 groupData.push_back(group.getName());
323 });
Matt Spinlerc3eb7b32022-04-25 15:44:16 -0500324
325 json eventData;
326 eventData["groups"] = groupData;
327 eventData["actions"] = actionData;
328
329 return eventData;
330}
331
Matthew Barth3174e722020-09-15 15:13:40 -0500332} // namespace phosphor::fan::control::json