blob: 4adced830468387a0199ff5c38ad6dd467629d3e [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 Barth0620be72021-04-14 13:31:12 -050023#include "triggers/trigger.hpp"
Matthew Barthe5578602021-03-30 12:53:24 -050024
25#include <fmt/format.h>
Matthew Barth391ade02021-01-15 14:33:21 -060026
Matthew Barth3174e722020-09-15 15:13:40 -050027#include <nlohmann/json.hpp>
28#include <phosphor-logging/log.hpp>
29#include <sdbusplus/bus.hpp>
30
Matthew Barthe5578602021-03-30 12:53:24 -050031#include <algorithm>
Matthew Barth12bae962021-01-15 16:18:11 -060032#include <optional>
Matthew Barth12bae962021-01-15 16:18:11 -060033
Matthew Barth3174e722020-09-15 15:13:40 -050034namespace phosphor::fan::control::json
35{
36
37using json = nlohmann::json;
38using namespace phosphor::logging;
39
Matthew Barth9403a212021-05-17 09:31:50 -050040Event::Event(const json& jsonObj, Manager* mgr,
Matthew Barth9f1632e2021-03-31 15:51:50 -050041 std::map<configKey, std::unique_ptr<Group>>& groups,
42 std::map<configKey, std::unique_ptr<Zone>>& zones) :
Matthew Barth44ab7692021-03-26 11:40:10 -050043 ConfigBase(jsonObj),
Matthew Barth9403a212021-05-17 09:31:50 -050044 _bus(util::SDBusPlus::getBus()), _manager(mgr), _zones(zones)
Matthew Barth3174e722020-09-15 15:13:40 -050045{
Matthew Barth3174e722020-09-15 15:13:40 -050046 // Event could have a precondition
47 if (!jsonObj.contains("precondition"))
48 {
49 // Event groups are optional
50 if (jsonObj.contains("groups"))
51 {
Matthew Barth44ab7692021-03-26 11:40:10 -050052 setGroups(jsonObj, groups);
Matthew Barth3174e722020-09-15 15:13:40 -050053 }
Matthew Barth3174e722020-09-15 15:13:40 -050054 // Event actions are optional
55 if (jsonObj.contains("actions"))
56 {
Matthew Barth9f1632e2021-03-31 15:51:50 -050057 setActions(jsonObj, groups);
Matthew Barth3174e722020-09-15 15:13:40 -050058 }
Matthew Barth9f1632e2021-03-31 15:51:50 -050059 setTriggers(jsonObj);
Matthew Barth3174e722020-09-15 15:13:40 -050060 }
61 else
62 {
Matthew Barth44ab7692021-03-26 11:40:10 -050063 setPrecond(jsonObj, groups);
Matthew Barth3174e722020-09-15 15:13:40 -050064 }
65}
66
Matthew Barth46b34482021-04-06 11:27:23 -050067void Event::configGroup(Group& group, const json& jsonObj)
68{
69 if (!jsonObj.contains("interface") || !jsonObj.contains("property") ||
70 !jsonObj["property"].contains("name"))
71 {
72 log<level::ERR>("Missing required group attribute",
73 entry("JSON=%s", jsonObj.dump().c_str()));
74 throw std::runtime_error("Missing required group attribute");
75 }
76
77 // Get the group members' interface
78 auto intf = jsonObj["interface"].get<std::string>();
79 group.setInterface(intf);
80
81 // Get the group members' property name
82 auto prop = jsonObj["property"]["name"].get<std::string>();
83 group.setProperty(prop);
84
85 // Get the group members' data type
86 if (jsonObj["property"].contains("type"))
87 {
88 std::optional<std::string> type =
89 jsonObj["property"]["type"].get<std::string>();
90 group.setType(type);
91 }
92
93 // Get the group members' expected value
94 if (jsonObj["property"].contains("value"))
95 {
96 std::optional<PropertyVariantType> value =
97 getJsonValue(jsonObj["property"]["value"]);
98 group.setValue(value);
99 }
100}
101
Matthew Barth44ab7692021-03-26 11:40:10 -0500102void Event::setPrecond(const json& jsonObj,
103 std::map<configKey, std::unique_ptr<Group>>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500104{
105 const auto& precond = jsonObj["precondition"];
106 if (!precond.contains("name") || !precond.contains("groups") ||
107 !precond.contains("triggers") || !precond.contains("events"))
108 {
109 log<level::ERR>("Missing required event precondition attributes",
110 entry("JSON=%s", precond.dump().c_str()));
111 throw std::runtime_error(
112 "Missing required event precondition attributes");
113 }
Matthew Barth44ab7692021-03-26 11:40:10 -0500114 setGroups(precond, groups);
Matthew Barth3174e722020-09-15 15:13:40 -0500115 setTriggers(precond);
116}
117
Matthew Barth44ab7692021-03-26 11:40:10 -0500118void Event::setGroups(const json& jsonObj,
119 std::map<configKey, std::unique_ptr<Group>>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500120{
Matthew Barth46b34482021-04-06 11:27:23 -0500121 for (const auto& jsonGrp : jsonObj["groups"])
Matthew Barth3174e722020-09-15 15:13:40 -0500122 {
Matthew Barth46b34482021-04-06 11:27:23 -0500123 if (!jsonGrp.contains("name"))
Matthew Barth3174e722020-09-15 15:13:40 -0500124 {
Matthew Barth46b34482021-04-06 11:27:23 -0500125 log<level::ERR>("Missing required group name attribute",
126 entry("JSON=%s", jsonGrp.dump().c_str()));
127 throw std::runtime_error("Missing required group name attribute");
Matthew Barth12bae962021-01-15 16:18:11 -0600128 }
129
Matthew Barth0206c722021-03-30 15:20:05 -0500130 configKey eventProfile =
Matthew Barth46b34482021-04-06 11:27:23 -0500131 std::make_pair(jsonGrp["name"].get<std::string>(), _profiles);
Matthew Barth0206c722021-03-30 15:20:05 -0500132 auto grpEntry = std::find_if(
133 groups.begin(), groups.end(), [&eventProfile](const auto& grp) {
134 return Manager::inConfig(grp.first, eventProfile);
Matthew Barthe5578602021-03-30 12:53:24 -0500135 });
Matthew Barth44ab7692021-03-26 11:40:10 -0500136 if (grpEntry != groups.end())
Matthew Barth12bae962021-01-15 16:18:11 -0600137 {
Matthew Barth46b34482021-04-06 11:27:23 -0500138 auto group = Group(*grpEntry->second);
139 configGroup(group, jsonGrp);
140 _groups.emplace_back(group);
Matthew Barth12bae962021-01-15 16:18:11 -0600141 }
Matthew Barthe5578602021-03-30 12:53:24 -0500142 }
Matthew Barth3174e722020-09-15 15:13:40 -0500143}
144
Matthew Barth9f1632e2021-03-31 15:51:50 -0500145void Event::setActions(const json& jsonObj,
146 std::map<configKey, std::unique_ptr<Group>>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500147{
Matthew Barth46b34482021-04-06 11:27:23 -0500148 for (const auto& jsonAct : jsonObj["actions"])
Matthew Barth3174e722020-09-15 15:13:40 -0500149 {
Matthew Barth46b34482021-04-06 11:27:23 -0500150 if (!jsonAct.contains("name"))
Matthew Barth3174e722020-09-15 15:13:40 -0500151 {
152 log<level::ERR>("Missing required event action name",
Matthew Barth46b34482021-04-06 11:27:23 -0500153 entry("JSON=%s", jsonAct.dump().c_str()));
Matthew Barth3174e722020-09-15 15:13:40 -0500154 throw std::runtime_error("Missing required event action name");
155 }
Matthew Barth46b34482021-04-06 11:27:23 -0500156
157 // Append action specific groups to the list of event groups for each
158 // action in the event
159 auto actionGroups = _groups;
Matthew Bartha8ea0912021-05-03 14:33:52 -0500160 if (jsonAct.contains("groups"))
Matthew Barth46b34482021-04-06 11:27:23 -0500161 {
Matthew Bartha8ea0912021-05-03 14:33:52 -0500162 for (const auto& jsonGrp : jsonAct["groups"])
Matthew Barth46b34482021-04-06 11:27:23 -0500163 {
164 if (!jsonGrp.contains("name"))
165 {
166 log<level::ERR>("Missing required group name attribute",
167 entry("JSON=%s", jsonGrp.dump().c_str()));
168 throw std::runtime_error(
169 "Missing required group name attribute");
170 }
171
172 configKey eventProfile = std::make_pair(
173 jsonGrp["name"].get<std::string>(), _profiles);
174 auto grpEntry = std::find_if(groups.begin(), groups.end(),
175 [&eventProfile](const auto& grp) {
176 return Manager::inConfig(
177 grp.first, eventProfile);
178 });
179 if (grpEntry != groups.end())
180 {
181 auto group = Group(*grpEntry->second);
182 configGroup(group, jsonGrp);
183 actionGroups.emplace_back(group);
184 }
185 }
186 }
187 if (actionGroups.empty())
188 {
189 log<level::DEBUG>(
190 fmt::format("No groups configured for event {}'s action {} "
191 "based on the active profile(s)",
192 getName(), jsonAct["name"].get<std::string>())
193 .c_str());
194 }
195
196 // Determine list of zones action should be run against
197 std::vector<std::reference_wrapper<Zone>> actionZones;
Matthew Bartha8ea0912021-05-03 14:33:52 -0500198 if (!jsonAct.contains("zones"))
Matthew Barth46b34482021-04-06 11:27:23 -0500199 {
200 // No zones configured on the action results in the action running
201 // against all zones matching the event's active profiles
202 for (const auto& zone : _zones)
203 {
204 configKey eventProfile =
205 std::make_pair(zone.second->getName(), _profiles);
206 auto zoneEntry = std::find_if(_zones.begin(), _zones.end(),
207 [&eventProfile](const auto& z) {
208 return Manager::inConfig(
209 z.first, eventProfile);
210 });
211 if (zoneEntry != _zones.end())
212 {
213 actionZones.emplace_back(*zoneEntry->second);
214 }
215 }
216 }
217 else
218 {
219 // Zones configured on the action result in the action only running
220 // against those zones if they match the event's active profiles
Matthew Bartha8ea0912021-05-03 14:33:52 -0500221 for (const auto& jsonZone : jsonAct["zones"])
Matthew Barth46b34482021-04-06 11:27:23 -0500222 {
223 configKey eventProfile =
224 std::make_pair(jsonZone.get<std::string>(), _profiles);
225 auto zoneEntry = std::find_if(_zones.begin(), _zones.end(),
226 [&eventProfile](const auto& z) {
227 return Manager::inConfig(
228 z.first, eventProfile);
229 });
230 if (zoneEntry != _zones.end())
231 {
232 actionZones.emplace_back(*zoneEntry->second);
233 }
234 }
235 }
236 if (actionZones.empty())
237 {
238 log<level::DEBUG>(
239 fmt::format("No zones configured for event {}'s action {} "
240 "based on the active profile(s)",
241 getName(), jsonAct["name"].get<std::string>())
242 .c_str());
243 }
244
245 // Create the action for the event
246 auto actObj = ActionFactory::getAction(
247 jsonAct["name"].get<std::string>(), jsonAct,
248 std::move(actionGroups), std::move(actionZones));
Matthew Barth776ca562021-03-31 09:50:58 -0500249 if (actObj)
250 {
251 _actions.emplace_back(std::move(actObj));
252 }
Matthew Barth3174e722020-09-15 15:13:40 -0500253 }
254}
255
Matthew Barth9f1632e2021-03-31 15:51:50 -0500256void Event::setTriggers(const json& jsonObj)
257{
258 if (!jsonObj.contains("triggers"))
259 {
260 log<level::ERR>("Missing required event triggers list",
261 entry("JSON=%s", jsonObj.dump().c_str()));
262 throw std::runtime_error("Missing required event triggers list");
263 }
Matthew Barth0620be72021-04-14 13:31:12 -0500264 for (const auto& jsonTrig : jsonObj["triggers"])
265 {
266 if (!jsonTrig.contains("class"))
267 {
268 log<level::ERR>("Missing required event trigger class",
269 entry("JSON=%s", jsonTrig.dump().c_str()));
270 throw std::runtime_error("Missing required event trigger class");
271 }
272 // The class of trigger used to run the event actions
273 auto tClass = jsonTrig["class"].get<std::string>();
274 std::transform(tClass.begin(), tClass.end(), tClass.begin(), tolower);
275 auto trigFunc = trigger::triggers.find(tClass);
276 if (trigFunc != trigger::triggers.end())
277 {
278 trigFunc->second(jsonTrig, getName(), _manager, _actions);
279 }
280 else
281 {
282 // Construct list of available triggers
283 auto availTrigs = std::accumulate(
284 std::next(trigger::triggers.begin()), trigger::triggers.end(),
285 trigger::triggers.begin()->first, [](auto list, auto trig) {
286 return std::move(list) + ", " + trig.first;
287 });
288 log<level::ERR>(
289 fmt::format("Trigger '{}' is not recognized", tClass).c_str(),
290 entry("AVAILABLE_TRIGGERS=%s", availTrigs.c_str()));
291 throw std::runtime_error("Unsupported trigger class name given");
292 }
293 }
Matthew Barth9f1632e2021-03-31 15:51:50 -0500294}
295
Matthew Barth3174e722020-09-15 15:13:40 -0500296} // namespace phosphor::fan::control::json