blob: c3d144899e0625395f389d3fb1051bccc2424a60 [file] [log] [blame]
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +02001#pragma once
2
3#include "utils/collection.hpp"
4#include "utils/telemetry_utils.hpp"
5
6#include <app.hpp>
7#include <registries/privilege_registry.hpp>
8
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +02009#include <tuple>
10#include <variant>
11#include <vector>
12
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020013namespace redfish
14{
15namespace telemetry
16{
17constexpr const char* triggerInterface =
18 "xyz.openbmc_project.Telemetry.Trigger";
19constexpr const char* triggerUri = "/redfish/v1/TelemetryService/Triggers";
20
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020021using NumericThresholdParams =
22 std::tuple<std::string, uint64_t, std::string, double>;
23
24using DiscreteThresholdParams =
25 std::tuple<std::string, std::string, uint64_t, std::string>;
26
27using TriggerThresholdParamsExt =
28 std::variant<std::monostate, std::vector<NumericThresholdParams>,
29 std::vector<DiscreteThresholdParams>>;
30
31using TriggerSensorsParams =
32 std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
33
34using TriggerGetParamsVariant =
35 std::variant<std::monostate, bool, std::string, TriggerThresholdParamsExt,
36 TriggerSensorsParams, std::vector<std::string>>;
37
38inline std::optional<std::string>
39 getRedfishFromDbusAction(const std::string& dbusAction)
40{
41 std::optional<std::string> redfishAction = std::nullopt;
42 if (dbusAction == "UpdateReport")
43 {
44 redfishAction = "RedfishMetricReport";
45 }
46 if (dbusAction == "RedfishEvent")
47 {
48 redfishAction = "RedfishEvent";
49 }
50 if (dbusAction == "LogToLogService")
51 {
52 redfishAction = "LogToLogService";
53 }
54 return redfishAction;
55}
56
57inline std::optional<std::vector<std::string>>
58 getTriggerActions(const std::vector<std::string>& dbusActions)
59{
60 std::vector<std::string> triggerActions;
61 for (const std::string& dbusAction : dbusActions)
62 {
63 std::optional<std::string> redfishAction =
64 getRedfishFromDbusAction(dbusAction);
65
66 if (!redfishAction)
67 {
68 return std::nullopt;
69 }
70
71 triggerActions.push_back(*redfishAction);
72 }
73
74 return std::make_optional(triggerActions);
75}
76
77inline std::optional<nlohmann::json>
78 getDiscreteTriggers(const TriggerThresholdParamsExt& thresholdParams)
79{
80 const std::vector<DiscreteThresholdParams>* discreteParams =
81 std::get_if<std::vector<DiscreteThresholdParams>>(&thresholdParams);
82
83 if (!discreteParams)
84 {
85 return std::nullopt;
86 }
87
88 nlohmann::json triggers = nlohmann::json::array();
89 for (const auto& [name, severity, dwellTime, value] : *discreteParams)
90 {
91 std::optional<std::string> duration =
92 time_utils::toDurationStringFromUint(dwellTime);
93
94 if (!duration)
95 {
96 return std::nullopt;
97 }
98
99 triggers.push_back({
100 {"Name", name},
101 {"Severity", severity},
102 {"DwellTime", *duration},
103 {"Value", value},
104 });
105 }
106
107 return std::make_optional(triggers);
108}
109
110inline std::optional<nlohmann::json>
111 getNumericThresholds(const TriggerThresholdParamsExt& thresholdParams)
112{
113 const std::vector<NumericThresholdParams>* numericParams =
114 std::get_if<std::vector<NumericThresholdParams>>(&thresholdParams);
115
116 if (!numericParams)
117 {
118 return std::nullopt;
119 }
120
121 nlohmann::json thresholds;
122 for (const auto& [type, dwellTime, activation, reading] : *numericParams)
123 {
124 std::optional<std::string> duration =
125 time_utils::toDurationStringFromUint(dwellTime);
126
127 if (!duration)
128 {
129 return std::nullopt;
130 }
131
132 thresholds[type] = {{"Reading", reading},
133 {"Activation", activation},
134 {"DwellTime", *duration}};
135 }
136
137 return std::make_optional(thresholds);
138}
139
140nlohmann::json
141 getMetricReportDefinitions(const std::vector<std::string>& reportNames)
142{
143 nlohmann::json reports = nlohmann::json::array();
144 for (const std::string& name : reportNames)
145 {
146 reports.push_back({
147 {"@odata.id", metricReportDefinitionUri + std::string("/") + name},
148 });
149 }
150
151 return reports;
152}
153
154inline std::vector<std::string>
155 getMetricProperties(const TriggerSensorsParams& sensors)
156{
157 std::vector<std::string> metricProperties;
158 metricProperties.reserve(sensors.size());
159 for (const auto& [_, metadata] : sensors)
160 {
161 metricProperties.emplace_back(metadata);
162 }
163
164 return metricProperties;
165}
166
167inline bool fillTrigger(
168 nlohmann::json& json, const std::string& id,
169 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>&
170 properties)
171{
172 const std::string* name = nullptr;
173 const bool* discrete = nullptr;
174 const TriggerSensorsParams* sensors = nullptr;
175 const std::vector<std::string>* reports = nullptr;
176 const std::vector<std::string>* actions = nullptr;
177 const TriggerThresholdParamsExt* thresholds = nullptr;
178
179 for (const auto& [key, var] : properties)
180 {
181 if (key == "Name")
182 {
183 name = std::get_if<std::string>(&var);
184 }
185 else if (key == "Discrete")
186 {
187 discrete = std::get_if<bool>(&var);
188 }
189 else if (key == "Sensors")
190 {
191 sensors = std::get_if<TriggerSensorsParams>(&var);
192 }
193 else if (key == "ReportNames")
194 {
195 reports = std::get_if<std::vector<std::string>>(&var);
196 }
197 else if (key == "TriggerActions")
198 {
199 actions = std::get_if<std::vector<std::string>>(&var);
200 }
201 else if (key == "Thresholds")
202 {
203 thresholds = std::get_if<TriggerThresholdParamsExt>(&var);
204 }
205 }
206
207 if (!name || !discrete || !sensors || !reports || !actions || !thresholds)
208 {
209 BMCWEB_LOG_ERROR
210 << "Property type mismatch or property is missing in Trigger: "
211 << id;
212 return false;
213 }
214
215 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
216 json["@odata.id"] = triggerUri + std::string("/") + id;
217 json["Id"] = id;
218 json["Name"] = *name;
219
220 if (*discrete)
221 {
222 std::optional<nlohmann::json> discreteTriggers =
223 getDiscreteTriggers(*thresholds);
224
225 if (!discreteTriggers)
226 {
227 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for discrete "
228 "triggers in Trigger: "
229 << id;
230 return false;
231 }
232
233 json["DiscreteTriggers"] = *discreteTriggers;
234 json["DiscreteTriggerCondition"] =
235 discreteTriggers->empty() ? "Changed" : "Specified";
236 json["MetricType"] = "Discrete";
237 }
238 else
239 {
240 std::optional<nlohmann::json> numericThresholds =
241 getNumericThresholds(*thresholds);
242
243 if (!numericThresholds)
244 {
245 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for numeric "
246 "thresholds in Trigger: "
247 << id;
248 return false;
249 }
250
251 json["NumericThresholds"] = *numericThresholds;
252 json["MetricType"] = "Numeric";
253 }
254
255 std::optional<std::vector<std::string>> triggerActions =
256 getTriggerActions(*actions);
257
258 if (!triggerActions)
259 {
260 BMCWEB_LOG_ERROR << "Property TriggerActions is invalid in Trigger: "
261 << id;
262 return false;
263 }
264
265 json["TriggerActions"] = *triggerActions;
266 json["MetricProperties"] = getMetricProperties(*sensors);
267 json["Links"]["MetricReportDefinitions"] =
268 getMetricReportDefinitions(*reports);
269
270 return true;
271}
272
273inline std::string getDbusTriggerPath(const std::string& id)
274{
275 sdbusplus::message::object_path path(
276 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
277 return {path / id};
278}
279
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200280} // namespace telemetry
281
282inline void requestRoutesTriggerCollection(App& app)
283{
284 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
285 .privileges(redfish::privileges::getTriggersCollection)
286 .methods(boost::beast::http::verb::get)(
287 [](const crow::Request&,
288 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
289 asyncResp->res.jsonValue["@odata.type"] =
290 "#TriggersCollection.TriggersCollection";
291 asyncResp->res.jsonValue["@odata.id"] =
292 "/redfish/v1/TelemetryService/Triggers";
293 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
294 const std::vector<const char*> interfaces{
295 telemetry::triggerInterface};
296 collection_util::getCollectionMembers(
297 asyncResp, telemetry::triggerUri, interfaces,
298 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
299 });
300}
301
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200302inline void requestRoutesTrigger(App& app)
303{
304 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
305 .privileges(redfish::privileges::getTriggers)
306 .methods(boost::beast::http::verb::get)(
307 [](const crow::Request&,
308 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
309 const std::string& id) {
310 crow::connections::systemBus->async_method_call(
311 [asyncResp,
312 id](const boost::system::error_code ec,
313 const std::vector<std::pair<
314 std::string, telemetry::TriggerGetParamsVariant>>&
315 ret) {
316 if (ec.value() == EBADR ||
317 ec == boost::system::errc::host_unreachable)
318 {
319 messages::resourceNotFound(asyncResp->res,
320 "Triggers", id);
321 return;
322 }
323 if (ec)
324 {
325 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
326 messages::internalError(asyncResp->res);
327 return;
328 }
329
330 if (!telemetry::fillTrigger(asyncResp->res.jsonValue,
331 id, ret))
332 {
333 messages::internalError(asyncResp->res);
334 }
335 },
336 telemetry::service, telemetry::getDbusTriggerPath(id),
337 "org.freedesktop.DBus.Properties", "GetAll",
338 telemetry::triggerInterface);
339 });
340}
341
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200342} // namespace redfish