blob: 7bb26ecc8706c2faf5b4a48c2e9a83c697b67db1 [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<Zone>>& zones) :
Matthew Barth44ab7692021-03-26 11:40:10 -050042 ConfigBase(jsonObj),
Matthew Barth9403a212021-05-17 09:31:50 -050043 _bus(util::SDBusPlus::getBus()), _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 Barthc8bde4a2021-05-19 15:34:49 -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 Barthc8bde4a2021-05-19 15:34:49 -050056 setActions(jsonObj);
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 Barthc8bde4a2021-05-19 15:34:49 -050062 setPrecond(jsonObj);
Matthew Barth3174e722020-09-15 15:13:40 -050063 }
64}
65
Matthew Barthc8bde4a2021-05-19 15:34:49 -050066auto& Event::getAvailGroups()
67{
68 static auto groups = Manager::getConfig<Group>(true);
69 return groups;
70}
71
Matthew Barth46b34482021-04-06 11:27:23 -050072void Event::configGroup(Group& group, const json& jsonObj)
73{
74 if (!jsonObj.contains("interface") || !jsonObj.contains("property") ||
75 !jsonObj["property"].contains("name"))
76 {
77 log<level::ERR>("Missing required group attribute",
78 entry("JSON=%s", jsonObj.dump().c_str()));
79 throw std::runtime_error("Missing required group attribute");
80 }
81
82 // Get the group members' interface
83 auto intf = jsonObj["interface"].get<std::string>();
84 group.setInterface(intf);
85
86 // Get the group members' property name
87 auto prop = jsonObj["property"]["name"].get<std::string>();
88 group.setProperty(prop);
89
90 // Get the group members' data type
91 if (jsonObj["property"].contains("type"))
92 {
93 std::optional<std::string> type =
94 jsonObj["property"]["type"].get<std::string>();
95 group.setType(type);
96 }
97
98 // Get the group members' expected value
99 if (jsonObj["property"].contains("value"))
100 {
101 std::optional<PropertyVariantType> value =
102 getJsonValue(jsonObj["property"]["value"]);
103 group.setValue(value);
104 }
105}
106
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500107void Event::setPrecond(const json& jsonObj)
Matthew Barth3174e722020-09-15 15:13:40 -0500108{
109 const auto& precond = jsonObj["precondition"];
110 if (!precond.contains("name") || !precond.contains("groups") ||
111 !precond.contains("triggers") || !precond.contains("events"))
112 {
113 log<level::ERR>("Missing required event precondition attributes",
114 entry("JSON=%s", precond.dump().c_str()));
115 throw std::runtime_error(
116 "Missing required event precondition attributes");
117 }
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500118 setGroups(precond, _groups);
Matthew Barth3174e722020-09-15 15:13:40 -0500119 setTriggers(precond);
120}
121
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500122void Event::setGroups(const json& jsonObj, std::vector<Group>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500123{
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500124 auto& availGroups = getAvailGroups();
Matthew Barth46b34482021-04-06 11:27:23 -0500125 for (const auto& jsonGrp : jsonObj["groups"])
Matthew Barth3174e722020-09-15 15:13:40 -0500126 {
Matthew Barth46b34482021-04-06 11:27:23 -0500127 if (!jsonGrp.contains("name"))
Matthew Barth3174e722020-09-15 15:13:40 -0500128 {
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500129 auto msg = fmt::format(
130 "Missing required group name attribute in event {}", getName());
131 log<level::ERR>(msg.c_str(),
Matthew Barth46b34482021-04-06 11:27:23 -0500132 entry("JSON=%s", jsonGrp.dump().c_str()));
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500133 throw std::runtime_error(msg.c_str());
Matthew Barth12bae962021-01-15 16:18:11 -0600134 }
135
Matthew Barth0206c722021-03-30 15:20:05 -0500136 configKey eventProfile =
Matthew Barth46b34482021-04-06 11:27:23 -0500137 std::make_pair(jsonGrp["name"].get<std::string>(), _profiles);
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500138 auto grpEntry =
139 std::find_if(availGroups.begin(), availGroups.end(),
140 [&eventProfile](const auto& grp) {
141 return Manager::inConfig(grp.first, eventProfile);
142 });
143 if (grpEntry != availGroups.end())
Matthew Barth12bae962021-01-15 16:18:11 -0600144 {
Matthew Barth46b34482021-04-06 11:27:23 -0500145 auto group = Group(*grpEntry->second);
146 configGroup(group, jsonGrp);
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500147 groups.emplace_back(group);
Matthew Barth12bae962021-01-15 16:18:11 -0600148 }
Matthew Barthe5578602021-03-30 12:53:24 -0500149 }
Matthew Barth3174e722020-09-15 15:13:40 -0500150}
151
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500152void Event::setActions(const json& jsonObj)
Matthew Barth3174e722020-09-15 15:13:40 -0500153{
Matthew Barth46b34482021-04-06 11:27:23 -0500154 for (const auto& jsonAct : jsonObj["actions"])
Matthew Barth3174e722020-09-15 15:13:40 -0500155 {
Matthew Barth46b34482021-04-06 11:27:23 -0500156 if (!jsonAct.contains("name"))
Matthew Barth3174e722020-09-15 15:13:40 -0500157 {
158 log<level::ERR>("Missing required event action name",
Matthew Barth46b34482021-04-06 11:27:23 -0500159 entry("JSON=%s", jsonAct.dump().c_str()));
Matthew Barth3174e722020-09-15 15:13:40 -0500160 throw std::runtime_error("Missing required event action name");
161 }
Matthew Barth46b34482021-04-06 11:27:23 -0500162
163 // Append action specific groups to the list of event groups for each
164 // action in the event
165 auto actionGroups = _groups;
Matthew Bartha8ea0912021-05-03 14:33:52 -0500166 if (jsonAct.contains("groups"))
Matthew Barth46b34482021-04-06 11:27:23 -0500167 {
Matthew Barthc8bde4a2021-05-19 15:34:49 -0500168 setGroups(jsonAct, actionGroups);
Matthew Barth46b34482021-04-06 11:27:23 -0500169 }
170 if (actionGroups.empty())
171 {
172 log<level::DEBUG>(
173 fmt::format("No groups configured for event {}'s action {} "
174 "based on the active profile(s)",
175 getName(), jsonAct["name"].get<std::string>())
176 .c_str());
177 }
178
179 // Determine list of zones action should be run against
180 std::vector<std::reference_wrapper<Zone>> actionZones;
Matthew Bartha8ea0912021-05-03 14:33:52 -0500181 if (!jsonAct.contains("zones"))
Matthew Barth46b34482021-04-06 11:27:23 -0500182 {
183 // No zones configured on the action results in the action running
184 // against all zones matching the event's active profiles
185 for (const auto& zone : _zones)
186 {
187 configKey eventProfile =
188 std::make_pair(zone.second->getName(), _profiles);
189 auto zoneEntry = std::find_if(_zones.begin(), _zones.end(),
190 [&eventProfile](const auto& z) {
191 return Manager::inConfig(
192 z.first, eventProfile);
193 });
194 if (zoneEntry != _zones.end())
195 {
196 actionZones.emplace_back(*zoneEntry->second);
197 }
198 }
199 }
200 else
201 {
202 // Zones configured on the action result in the action only running
203 // against those zones if they match the event's active profiles
Matthew Bartha8ea0912021-05-03 14:33:52 -0500204 for (const auto& jsonZone : jsonAct["zones"])
Matthew Barth46b34482021-04-06 11:27:23 -0500205 {
206 configKey eventProfile =
207 std::make_pair(jsonZone.get<std::string>(), _profiles);
208 auto zoneEntry = std::find_if(_zones.begin(), _zones.end(),
209 [&eventProfile](const auto& z) {
210 return Manager::inConfig(
211 z.first, eventProfile);
212 });
213 if (zoneEntry != _zones.end())
214 {
215 actionZones.emplace_back(*zoneEntry->second);
216 }
217 }
218 }
219 if (actionZones.empty())
220 {
221 log<level::DEBUG>(
222 fmt::format("No zones configured for event {}'s action {} "
223 "based on the active profile(s)",
224 getName(), jsonAct["name"].get<std::string>())
225 .c_str());
226 }
227
228 // Create the action for the event
229 auto actObj = ActionFactory::getAction(
230 jsonAct["name"].get<std::string>(), jsonAct,
231 std::move(actionGroups), std::move(actionZones));
Matthew Barth776ca562021-03-31 09:50:58 -0500232 if (actObj)
233 {
234 _actions.emplace_back(std::move(actObj));
235 }
Matthew Barth3174e722020-09-15 15:13:40 -0500236 }
237}
238
Matthew Barth9f1632e2021-03-31 15:51:50 -0500239void Event::setTriggers(const json& jsonObj)
240{
241 if (!jsonObj.contains("triggers"))
242 {
243 log<level::ERR>("Missing required event triggers list",
244 entry("JSON=%s", jsonObj.dump().c_str()));
245 throw std::runtime_error("Missing required event triggers list");
246 }
Matthew Barth0620be72021-04-14 13:31:12 -0500247 for (const auto& jsonTrig : jsonObj["triggers"])
248 {
249 if (!jsonTrig.contains("class"))
250 {
251 log<level::ERR>("Missing required event trigger class",
252 entry("JSON=%s", jsonTrig.dump().c_str()));
253 throw std::runtime_error("Missing required event trigger class");
254 }
255 // The class of trigger used to run the event actions
256 auto tClass = jsonTrig["class"].get<std::string>();
257 std::transform(tClass.begin(), tClass.end(), tClass.begin(), tolower);
258 auto trigFunc = trigger::triggers.find(tClass);
259 if (trigFunc != trigger::triggers.end())
260 {
261 trigFunc->second(jsonTrig, getName(), _manager, _actions);
262 }
263 else
264 {
265 // Construct list of available triggers
266 auto availTrigs = std::accumulate(
267 std::next(trigger::triggers.begin()), trigger::triggers.end(),
268 trigger::triggers.begin()->first, [](auto list, auto trig) {
269 return std::move(list) + ", " + trig.first;
270 });
271 log<level::ERR>(
272 fmt::format("Trigger '{}' is not recognized", tClass).c_str(),
273 entry("AVAILABLE_TRIGGERS=%s", availTrigs.c_str()));
274 throw std::runtime_error("Unsupported trigger class name given");
275 }
276 }
Matthew Barth9f1632e2021-03-31 15:51:50 -0500277}
278
Matthew Barth3174e722020-09-15 15:13:40 -0500279} // namespace phosphor::fan::control::json