blob: 3db50aa802b103b1e1cb713e1c804efb3328c209 [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"
22
23#include <fmt/format.h>
Matthew Barth391ade02021-01-15 14:33:21 -060024
Matthew Barth3174e722020-09-15 15:13:40 -050025#include <nlohmann/json.hpp>
26#include <phosphor-logging/log.hpp>
27#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;
36using namespace phosphor::logging;
37
Matthew Barth44ab7692021-03-26 11:40:10 -050038Event::Event(const json& jsonObj, sdbusplus::bus::bus& bus,
Matthew Barth9f1632e2021-03-31 15:51:50 -050039 std::map<configKey, std::unique_ptr<Group>>& groups,
40 std::map<configKey, std::unique_ptr<Zone>>& zones) :
Matthew Barth44ab7692021-03-26 11:40:10 -050041 ConfigBase(jsonObj),
Matthew Barth9f1632e2021-03-31 15:51:50 -050042 _bus(bus), _zones(zones)
Matthew Barth3174e722020-09-15 15:13:40 -050043{
Matthew Barth3174e722020-09-15 15:13:40 -050044 // Event could have a precondition
45 if (!jsonObj.contains("precondition"))
46 {
47 // Event groups are optional
48 if (jsonObj.contains("groups"))
49 {
Matthew Barth44ab7692021-03-26 11:40:10 -050050 setGroups(jsonObj, groups);
Matthew Barth3174e722020-09-15 15:13:40 -050051 }
Matthew Barth3174e722020-09-15 15:13:40 -050052 // Event actions are optional
53 if (jsonObj.contains("actions"))
54 {
Matthew Barth9f1632e2021-03-31 15:51:50 -050055 setActions(jsonObj, groups);
Matthew Barth3174e722020-09-15 15:13:40 -050056 }
Matthew Barth9f1632e2021-03-31 15:51:50 -050057 setTriggers(jsonObj);
Matthew Barth3174e722020-09-15 15:13:40 -050058 }
59 else
60 {
Matthew Barth44ab7692021-03-26 11:40:10 -050061 setPrecond(jsonObj, groups);
Matthew Barth3174e722020-09-15 15:13:40 -050062 }
63}
64
Matthew Barth46b34482021-04-06 11:27:23 -050065void Event::configGroup(Group& group, const json& jsonObj)
66{
67 if (!jsonObj.contains("interface") || !jsonObj.contains("property") ||
68 !jsonObj["property"].contains("name"))
69 {
70 log<level::ERR>("Missing required group attribute",
71 entry("JSON=%s", jsonObj.dump().c_str()));
72 throw std::runtime_error("Missing required group attribute");
73 }
74
75 // Get the group members' interface
76 auto intf = jsonObj["interface"].get<std::string>();
77 group.setInterface(intf);
78
79 // Get the group members' property name
80 auto prop = jsonObj["property"]["name"].get<std::string>();
81 group.setProperty(prop);
82
83 // Get the group members' data type
84 if (jsonObj["property"].contains("type"))
85 {
86 std::optional<std::string> type =
87 jsonObj["property"]["type"].get<std::string>();
88 group.setType(type);
89 }
90
91 // Get the group members' expected value
92 if (jsonObj["property"].contains("value"))
93 {
94 std::optional<PropertyVariantType> value =
95 getJsonValue(jsonObj["property"]["value"]);
96 group.setValue(value);
97 }
98}
99
Matthew Barth44ab7692021-03-26 11:40:10 -0500100void Event::setPrecond(const json& jsonObj,
101 std::map<configKey, std::unique_ptr<Group>>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500102{
103 const auto& precond = jsonObj["precondition"];
104 if (!precond.contains("name") || !precond.contains("groups") ||
105 !precond.contains("triggers") || !precond.contains("events"))
106 {
107 log<level::ERR>("Missing required event precondition attributes",
108 entry("JSON=%s", precond.dump().c_str()));
109 throw std::runtime_error(
110 "Missing required event precondition attributes");
111 }
Matthew Barth44ab7692021-03-26 11:40:10 -0500112 setGroups(precond, groups);
Matthew Barth3174e722020-09-15 15:13:40 -0500113 setTriggers(precond);
114}
115
Matthew Barth44ab7692021-03-26 11:40:10 -0500116void Event::setGroups(const json& jsonObj,
117 std::map<configKey, std::unique_ptr<Group>>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500118{
Matthew Barth46b34482021-04-06 11:27:23 -0500119 for (const auto& jsonGrp : jsonObj["groups"])
Matthew Barth3174e722020-09-15 15:13:40 -0500120 {
Matthew Barth46b34482021-04-06 11:27:23 -0500121 if (!jsonGrp.contains("name"))
Matthew Barth3174e722020-09-15 15:13:40 -0500122 {
Matthew Barth46b34482021-04-06 11:27:23 -0500123 log<level::ERR>("Missing required group name attribute",
124 entry("JSON=%s", jsonGrp.dump().c_str()));
125 throw std::runtime_error("Missing required group name attribute");
Matthew Barth12bae962021-01-15 16:18:11 -0600126 }
127
Matthew Barth0206c722021-03-30 15:20:05 -0500128 configKey eventProfile =
Matthew Barth46b34482021-04-06 11:27:23 -0500129 std::make_pair(jsonGrp["name"].get<std::string>(), _profiles);
Matthew Barth0206c722021-03-30 15:20:05 -0500130 auto grpEntry = std::find_if(
131 groups.begin(), groups.end(), [&eventProfile](const auto& grp) {
132 return Manager::inConfig(grp.first, eventProfile);
Matthew Barthe5578602021-03-30 12:53:24 -0500133 });
Matthew Barth44ab7692021-03-26 11:40:10 -0500134 if (grpEntry != groups.end())
Matthew Barth12bae962021-01-15 16:18:11 -0600135 {
Matthew Barth46b34482021-04-06 11:27:23 -0500136 auto group = Group(*grpEntry->second);
137 configGroup(group, jsonGrp);
138 _groups.emplace_back(group);
Matthew Barth12bae962021-01-15 16:18:11 -0600139 }
Matthew Barthe5578602021-03-30 12:53:24 -0500140 }
Matthew Barth3174e722020-09-15 15:13:40 -0500141}
142
Matthew Barth9f1632e2021-03-31 15:51:50 -0500143void Event::setActions(const json& jsonObj,
144 std::map<configKey, std::unique_ptr<Group>>& groups)
Matthew Barth3174e722020-09-15 15:13:40 -0500145{
Matthew Barth46b34482021-04-06 11:27:23 -0500146 for (const auto& jsonAct : jsonObj["actions"])
Matthew Barth3174e722020-09-15 15:13:40 -0500147 {
Matthew Barth46b34482021-04-06 11:27:23 -0500148 if (!jsonAct.contains("name"))
Matthew Barth3174e722020-09-15 15:13:40 -0500149 {
150 log<level::ERR>("Missing required event action name",
Matthew Barth46b34482021-04-06 11:27:23 -0500151 entry("JSON=%s", jsonAct.dump().c_str()));
Matthew Barth3174e722020-09-15 15:13:40 -0500152 throw std::runtime_error("Missing required event action name");
153 }
Matthew Barth46b34482021-04-06 11:27:23 -0500154
155 // Append action specific groups to the list of event groups for each
156 // action in the event
157 auto actionGroups = _groups;
158 if (jsonObj.contains("groups"))
159 {
160 for (const auto& jsonGrp : jsonObj["groups"])
161 {
162 if (!jsonGrp.contains("name"))
163 {
164 log<level::ERR>("Missing required group name attribute",
165 entry("JSON=%s", jsonGrp.dump().c_str()));
166 throw std::runtime_error(
167 "Missing required group name attribute");
168 }
169
170 configKey eventProfile = std::make_pair(
171 jsonGrp["name"].get<std::string>(), _profiles);
172 auto grpEntry = std::find_if(groups.begin(), groups.end(),
173 [&eventProfile](const auto& grp) {
174 return Manager::inConfig(
175 grp.first, eventProfile);
176 });
177 if (grpEntry != groups.end())
178 {
179 auto group = Group(*grpEntry->second);
180 configGroup(group, jsonGrp);
181 actionGroups.emplace_back(group);
182 }
183 }
184 }
185 if (actionGroups.empty())
186 {
187 log<level::DEBUG>(
188 fmt::format("No groups configured for event {}'s action {} "
189 "based on the active profile(s)",
190 getName(), jsonAct["name"].get<std::string>())
191 .c_str());
192 }
193
194 // Determine list of zones action should be run against
195 std::vector<std::reference_wrapper<Zone>> actionZones;
196 if (!jsonObj.contains("zones"))
197 {
198 // No zones configured on the action results in the action running
199 // against all zones matching the event's active profiles
200 for (const auto& zone : _zones)
201 {
202 configKey eventProfile =
203 std::make_pair(zone.second->getName(), _profiles);
204 auto zoneEntry = std::find_if(_zones.begin(), _zones.end(),
205 [&eventProfile](const auto& z) {
206 return Manager::inConfig(
207 z.first, eventProfile);
208 });
209 if (zoneEntry != _zones.end())
210 {
211 actionZones.emplace_back(*zoneEntry->second);
212 }
213 }
214 }
215 else
216 {
217 // Zones configured on the action result in the action only running
218 // against those zones if they match the event's active profiles
219 for (const auto& jsonZone : jsonObj["zones"])
220 {
221 configKey eventProfile =
222 std::make_pair(jsonZone.get<std::string>(), _profiles);
223 auto zoneEntry = std::find_if(_zones.begin(), _zones.end(),
224 [&eventProfile](const auto& z) {
225 return Manager::inConfig(
226 z.first, eventProfile);
227 });
228 if (zoneEntry != _zones.end())
229 {
230 actionZones.emplace_back(*zoneEntry->second);
231 }
232 }
233 }
234 if (actionZones.empty())
235 {
236 log<level::DEBUG>(
237 fmt::format("No zones configured for event {}'s action {} "
238 "based on the active profile(s)",
239 getName(), jsonAct["name"].get<std::string>())
240 .c_str());
241 }
242
243 // Create the action for the event
244 auto actObj = ActionFactory::getAction(
245 jsonAct["name"].get<std::string>(), jsonAct,
246 std::move(actionGroups), std::move(actionZones));
Matthew Barth776ca562021-03-31 09:50:58 -0500247 if (actObj)
248 {
249 _actions.emplace_back(std::move(actObj));
250 }
Matthew Barth3174e722020-09-15 15:13:40 -0500251 }
252}
253
Matthew Barth9f1632e2021-03-31 15:51:50 -0500254void Event::setTriggers(const json& jsonObj)
255{
256 if (!jsonObj.contains("triggers"))
257 {
258 log<level::ERR>("Missing required event triggers list",
259 entry("JSON=%s", jsonObj.dump().c_str()));
260 throw std::runtime_error("Missing required event triggers list");
261 }
262}
263
Matthew Barth3174e722020-09-15 15:13:40 -0500264} // namespace phosphor::fan::control::json