blob: e50e67e6cc7facec77df94db125d92382969f68b [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 Barth0620be72021-04-14 13:31:12 -050022#include "triggers/trigger.hpp"
Matthew Barthe5578602021-03-30 12:53:24 -050023
24#include <fmt/format.h>
Matthew Barth391ade02021-01-15 14:33:21 -060025
Matthew Barth3174e722020-09-15 15:13:40 -050026#include <nlohmann/json.hpp>
27#include <phosphor-logging/log.hpp>
28#include <sdbusplus/bus.hpp>
29
Matthew Barthe5578602021-03-30 12:53:24 -050030#include <algorithm>
Matthew Barth12bae962021-01-15 16:18:11 -060031#include <optional>
Matthew Barth12bae962021-01-15 16:18:11 -060032
Matthew Barth3174e722020-09-15 15:13:40 -050033namespace phosphor::fan::control::json
34{
35
36using json = nlohmann::json;
37using namespace phosphor::logging;
38
Matthew Barthd4bd0ae2021-04-14 12:42:03 -050039Event::Event(const json& jsonObj, sdbusplus::bus::bus& bus, Manager* mgr,
Matthew Barth9f1632e2021-03-31 15:51:50 -050040 std::map<configKey, std::unique_ptr<Group>>& groups,
41 std::map<configKey, std::unique_ptr<Zone>>& zones) :
Matthew Barth44ab7692021-03-26 11:40:10 -050042 ConfigBase(jsonObj),
Matthew Barthd4bd0ae2021-04-14 12:42:03 -050043 _bus(bus), _manager(mgr), _zones(zones)
Matthew Barth3174e722020-09-15 15:13:40 -050044{
Matthew Barth3174e722020-09-15 15:13:40 -050045 // Event could have a precondition
46 if (!jsonObj.contains("precondition"))
47 {
48 // Event groups are optional
49 if (jsonObj.contains("groups"))
50 {
Matthew Barth44ab7692021-03-26 11:40:10 -050051 setGroups(jsonObj, groups);
Matthew Barth3174e722020-09-15 15:13:40 -050052 }
Matthew Barth3174e722020-09-15 15:13:40 -050053 // Event actions are optional
54 if (jsonObj.contains("actions"))
55 {
Matthew Barth9f1632e2021-03-31 15:51:50 -050056 setActions(jsonObj, groups);
Matthew Barth3174e722020-09-15 15:13:40 -050057 }
Matthew Barth9f1632e2021-03-31 15:51:50 -050058 setTriggers(jsonObj);
Matthew Barth3174e722020-09-15 15:13:40 -050059 }
60 else
61 {
Matthew Barth44ab7692021-03-26 11:40:10 -050062 setPrecond(jsonObj, groups);
Matthew Barth3174e722020-09-15 15:13:40 -050063 }
64}
65
Matthew Barth46b34482021-04-06 11:27:23 -050066void Event::configGroup(Group& group, const json& jsonObj)
67{
68 if (!jsonObj.contains("interface") || !jsonObj.contains("property") ||
69 !jsonObj["property"].contains("name"))
70 {
71 log<level::ERR>("Missing required group attribute",
72 entry("JSON=%s", jsonObj.dump().c_str()));
73 throw std::runtime_error("Missing required group attribute");
74 }
75
76 // Get the group members' interface
77 auto intf = jsonObj["interface"].get<std::string>();
78 group.setInterface(intf);
79
80 // Get the group members' property name
81 auto prop = jsonObj["property"]["name"].get<std::string>();
82 group.setProperty(prop);
83
84 // Get the group members' data type
85 if (jsonObj["property"].contains("type"))
86 {
87 std::optional<std::string> type =
88 jsonObj["property"]["type"].get<std::string>();
89 group.setType(type);
90 }
91
92 // Get the group members' expected value
93 if (jsonObj["property"].contains("value"))
94 {
95 std::optional<PropertyVariantType> value =
96 getJsonValue(jsonObj["property"]["value"]);
97 group.setValue(value);
98 }
99}
100
Matthew Barth44ab7692021-03-26 11:40:10 -0500101void Event::setPrecond(const json& jsonObj,
102 std::map<configKey, std::unique_ptr<Group>>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500103{
104 const auto& precond = jsonObj["precondition"];
105 if (!precond.contains("name") || !precond.contains("groups") ||
106 !precond.contains("triggers") || !precond.contains("events"))
107 {
108 log<level::ERR>("Missing required event precondition attributes",
109 entry("JSON=%s", precond.dump().c_str()));
110 throw std::runtime_error(
111 "Missing required event precondition attributes");
112 }
Matthew Barth44ab7692021-03-26 11:40:10 -0500113 setGroups(precond, groups);
Matthew Barth3174e722020-09-15 15:13:40 -0500114 setTriggers(precond);
115}
116
Matthew Barth44ab7692021-03-26 11:40:10 -0500117void Event::setGroups(const json& jsonObj,
118 std::map<configKey, std::unique_ptr<Group>>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500119{
Matthew Barth46b34482021-04-06 11:27:23 -0500120 for (const auto& jsonGrp : jsonObj["groups"])
Matthew Barth3174e722020-09-15 15:13:40 -0500121 {
Matthew Barth46b34482021-04-06 11:27:23 -0500122 if (!jsonGrp.contains("name"))
Matthew Barth3174e722020-09-15 15:13:40 -0500123 {
Matthew Barth46b34482021-04-06 11:27:23 -0500124 log<level::ERR>("Missing required group name attribute",
125 entry("JSON=%s", jsonGrp.dump().c_str()));
126 throw std::runtime_error("Missing required group name attribute");
Matthew Barth12bae962021-01-15 16:18:11 -0600127 }
128
Matthew Barth0206c722021-03-30 15:20:05 -0500129 configKey eventProfile =
Matthew Barth46b34482021-04-06 11:27:23 -0500130 std::make_pair(jsonGrp["name"].get<std::string>(), _profiles);
Matthew Barth0206c722021-03-30 15:20:05 -0500131 auto grpEntry = std::find_if(
132 groups.begin(), groups.end(), [&eventProfile](const auto& grp) {
133 return Manager::inConfig(grp.first, eventProfile);
Matthew Barthe5578602021-03-30 12:53:24 -0500134 });
Matthew Barth44ab7692021-03-26 11:40:10 -0500135 if (grpEntry != groups.end())
Matthew Barth12bae962021-01-15 16:18:11 -0600136 {
Matthew Barth46b34482021-04-06 11:27:23 -0500137 auto group = Group(*grpEntry->second);
138 configGroup(group, jsonGrp);
139 _groups.emplace_back(group);
Matthew Barth12bae962021-01-15 16:18:11 -0600140 }
Matthew Barthe5578602021-03-30 12:53:24 -0500141 }
Matthew Barth3174e722020-09-15 15:13:40 -0500142}
143
Matthew Barth9f1632e2021-03-31 15:51:50 -0500144void Event::setActions(const json& jsonObj,
145 std::map<configKey, std::unique_ptr<Group>>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500146{
Matthew Barth46b34482021-04-06 11:27:23 -0500147 for (const auto& jsonAct : jsonObj["actions"])
Matthew Barth3174e722020-09-15 15:13:40 -0500148 {
Matthew Barth46b34482021-04-06 11:27:23 -0500149 if (!jsonAct.contains("name"))
Matthew Barth3174e722020-09-15 15:13:40 -0500150 {
151 log<level::ERR>("Missing required event action name",
Matthew Barth46b34482021-04-06 11:27:23 -0500152 entry("JSON=%s", jsonAct.dump().c_str()));
Matthew Barth3174e722020-09-15 15:13:40 -0500153 throw std::runtime_error("Missing required event action name");
154 }
Matthew Barth46b34482021-04-06 11:27:23 -0500155
156 // Append action specific groups to the list of event groups for each
157 // action in the event
158 auto actionGroups = _groups;
159 if (jsonObj.contains("groups"))
160 {
161 for (const auto& jsonGrp : jsonObj["groups"])
162 {
163 if (!jsonGrp.contains("name"))
164 {
165 log<level::ERR>("Missing required group name attribute",
166 entry("JSON=%s", jsonGrp.dump().c_str()));
167 throw std::runtime_error(
168 "Missing required group name attribute");
169 }
170
171 configKey eventProfile = std::make_pair(
172 jsonGrp["name"].get<std::string>(), _profiles);
173 auto grpEntry = std::find_if(groups.begin(), groups.end(),
174 [&eventProfile](const auto& grp) {
175 return Manager::inConfig(
176 grp.first, eventProfile);
177 });
178 if (grpEntry != groups.end())
179 {
180 auto group = Group(*grpEntry->second);
181 configGroup(group, jsonGrp);
182 actionGroups.emplace_back(group);
183 }
184 }
185 }
186 if (actionGroups.empty())
187 {
188 log<level::DEBUG>(
189 fmt::format("No groups configured for event {}'s action {} "
190 "based on the active profile(s)",
191 getName(), jsonAct["name"].get<std::string>())
192 .c_str());
193 }
194
195 // Determine list of zones action should be run against
196 std::vector<std::reference_wrapper<Zone>> actionZones;
197 if (!jsonObj.contains("zones"))
198 {
199 // No zones configured on the action results in the action running
200 // against all zones matching the event's active profiles
201 for (const auto& zone : _zones)
202 {
203 configKey eventProfile =
204 std::make_pair(zone.second->getName(), _profiles);
205 auto zoneEntry = std::find_if(_zones.begin(), _zones.end(),
206 [&eventProfile](const auto& z) {
207 return Manager::inConfig(
208 z.first, eventProfile);
209 });
210 if (zoneEntry != _zones.end())
211 {
212 actionZones.emplace_back(*zoneEntry->second);
213 }
214 }
215 }
216 else
217 {
218 // Zones configured on the action result in the action only running
219 // against those zones if they match the event's active profiles
220 for (const auto& jsonZone : jsonObj["zones"])
221 {
222 configKey eventProfile =
223 std::make_pair(jsonZone.get<std::string>(), _profiles);
224 auto zoneEntry = std::find_if(_zones.begin(), _zones.end(),
225 [&eventProfile](const auto& z) {
226 return Manager::inConfig(
227 z.first, eventProfile);
228 });
229 if (zoneEntry != _zones.end())
230 {
231 actionZones.emplace_back(*zoneEntry->second);
232 }
233 }
234 }
235 if (actionZones.empty())
236 {
237 log<level::DEBUG>(
238 fmt::format("No zones configured for event {}'s action {} "
239 "based on the active profile(s)",
240 getName(), jsonAct["name"].get<std::string>())
241 .c_str());
242 }
243
244 // Create the action for the event
245 auto actObj = ActionFactory::getAction(
246 jsonAct["name"].get<std::string>(), jsonAct,
247 std::move(actionGroups), std::move(actionZones));
Matthew Barth776ca562021-03-31 09:50:58 -0500248 if (actObj)
249 {
250 _actions.emplace_back(std::move(actObj));
251 }
Matthew Barth3174e722020-09-15 15:13:40 -0500252 }
253}
254
Matthew Barth9f1632e2021-03-31 15:51:50 -0500255void Event::setTriggers(const json& jsonObj)
256{
257 if (!jsonObj.contains("triggers"))
258 {
259 log<level::ERR>("Missing required event triggers list",
260 entry("JSON=%s", jsonObj.dump().c_str()));
261 throw std::runtime_error("Missing required event triggers list");
262 }
Matthew Barth0620be72021-04-14 13:31:12 -0500263 for (const auto& jsonTrig : jsonObj["triggers"])
264 {
265 if (!jsonTrig.contains("class"))
266 {
267 log<level::ERR>("Missing required event trigger class",
268 entry("JSON=%s", jsonTrig.dump().c_str()));
269 throw std::runtime_error("Missing required event trigger class");
270 }
271 // The class of trigger used to run the event actions
272 auto tClass = jsonTrig["class"].get<std::string>();
273 std::transform(tClass.begin(), tClass.end(), tClass.begin(), tolower);
274 auto trigFunc = trigger::triggers.find(tClass);
275 if (trigFunc != trigger::triggers.end())
276 {
277 trigFunc->second(jsonTrig, getName(), _manager, _actions);
278 }
279 else
280 {
281 // Construct list of available triggers
282 auto availTrigs = std::accumulate(
283 std::next(trigger::triggers.begin()), trigger::triggers.end(),
284 trigger::triggers.begin()->first, [](auto list, auto trig) {
285 return std::move(list) + ", " + trig.first;
286 });
287 log<level::ERR>(
288 fmt::format("Trigger '{}' is not recognized", tClass).c_str(),
289 entry("AVAILABLE_TRIGGERS=%s", availTrigs.c_str()));
290 throw std::runtime_error("Unsupported trigger class name given");
291 }
292 }
Matthew Barth9f1632e2021-03-31 15:51:50 -0500293}
294
Matthew Barth3174e722020-09-15 15:13:40 -0500295} // namespace phosphor::fan::control::json