blob: 920a8c30c64be3209105624254c0554697b5c3bf [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>
Ed Tanous45ca1b82022-03-25 13:07:27 -07007#include <query.hpp>
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +02008#include <registries/privilege_registry.hpp>
9
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020010#include <tuple>
11#include <variant>
12#include <vector>
13
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020014namespace redfish
15{
16namespace telemetry
17{
18constexpr const char* triggerInterface =
19 "xyz.openbmc_project.Telemetry.Trigger";
20constexpr const char* triggerUri = "/redfish/v1/TelemetryService/Triggers";
21
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020022using NumericThresholdParams =
23 std::tuple<std::string, uint64_t, std::string, double>;
24
25using DiscreteThresholdParams =
26 std::tuple<std::string, std::string, uint64_t, std::string>;
27
28using TriggerThresholdParamsExt =
29 std::variant<std::monostate, std::vector<NumericThresholdParams>,
30 std::vector<DiscreteThresholdParams>>;
31
32using TriggerSensorsParams =
33 std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
34
35using TriggerGetParamsVariant =
36 std::variant<std::monostate, bool, std::string, TriggerThresholdParamsExt,
Szymon Dompke3f215c92022-02-22 13:58:00 +010037 TriggerSensorsParams, std::vector<std::string>,
38 std::vector<sdbusplus::message::object_path>>;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020039
40inline std::optional<std::string>
41 getRedfishFromDbusAction(const std::string& dbusAction)
42{
43 std::optional<std::string> redfishAction = std::nullopt;
44 if (dbusAction == "UpdateReport")
45 {
46 redfishAction = "RedfishMetricReport";
47 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010048 if (dbusAction == "LogToRedfishEventLog")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020049 {
50 redfishAction = "RedfishEvent";
51 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010052 if (dbusAction == "LogToJournal")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020053 {
54 redfishAction = "LogToLogService";
55 }
56 return redfishAction;
57}
58
59inline std::optional<std::vector<std::string>>
60 getTriggerActions(const std::vector<std::string>& dbusActions)
61{
62 std::vector<std::string> triggerActions;
63 for (const std::string& dbusAction : dbusActions)
64 {
65 std::optional<std::string> redfishAction =
66 getRedfishFromDbusAction(dbusAction);
67
68 if (!redfishAction)
69 {
70 return std::nullopt;
71 }
72
73 triggerActions.push_back(*redfishAction);
74 }
75
Szymon Dompke3f215c92022-02-22 13:58:00 +010076 return {std::move(triggerActions)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020077}
78
Szymon Dompke3f215c92022-02-22 13:58:00 +010079inline std::optional<nlohmann::json::array_t>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020080 getDiscreteTriggers(const TriggerThresholdParamsExt& thresholdParams)
81{
82 const std::vector<DiscreteThresholdParams>* discreteParams =
83 std::get_if<std::vector<DiscreteThresholdParams>>(&thresholdParams);
84
Ed Tanouse662eae2022-01-25 10:39:19 -080085 if (discreteParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020086 {
87 return std::nullopt;
88 }
89
Szymon Dompke3f215c92022-02-22 13:58:00 +010090 nlohmann::json::array_t triggers;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020091 for (const auto& [name, severity, dwellTime, value] : *discreteParams)
92 {
93 std::optional<std::string> duration =
94 time_utils::toDurationStringFromUint(dwellTime);
95
96 if (!duration)
97 {
98 return std::nullopt;
99 }
100
101 triggers.push_back({
102 {"Name", name},
103 {"Severity", severity},
104 {"DwellTime", *duration},
105 {"Value", value},
106 });
107 }
108
Szymon Dompke3f215c92022-02-22 13:58:00 +0100109 return {std::move(triggers)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200110}
111
112inline std::optional<nlohmann::json>
113 getNumericThresholds(const TriggerThresholdParamsExt& thresholdParams)
114{
115 const std::vector<NumericThresholdParams>* numericParams =
116 std::get_if<std::vector<NumericThresholdParams>>(&thresholdParams);
117
Ed Tanouse662eae2022-01-25 10:39:19 -0800118 if (numericParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200119 {
120 return std::nullopt;
121 }
122
Szymon Dompke3f215c92022-02-22 13:58:00 +0100123 nlohmann::json::object_t thresholds;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200124 for (const auto& [type, dwellTime, activation, reading] : *numericParams)
125 {
126 std::optional<std::string> duration =
127 time_utils::toDurationStringFromUint(dwellTime);
128
129 if (!duration)
130 {
131 return std::nullopt;
132 }
Ed Tanous14766872022-03-15 10:44:42 -0700133 nlohmann::json& threshold = thresholds[type];
134 threshold["Reading"] = reading;
135 threshold["Activation"] = activation;
136 threshold["DwellTime"] = *duration;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200137 }
138
Szymon Dompke3f215c92022-02-22 13:58:00 +0100139 return {std::move(thresholds)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200140}
141
Szymon Dompke3f215c92022-02-22 13:58:00 +0100142inline std::optional<nlohmann::json> getMetricReportDefinitions(
143 const std::vector<sdbusplus::message::object_path>& reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200144{
145 nlohmann::json reports = nlohmann::json::array();
Szymon Dompke3f215c92022-02-22 13:58:00 +0100146
147 for (const sdbusplus::message::object_path& path : reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200148 {
Szymon Dompke3f215c92022-02-22 13:58:00 +0100149 std::string reportId = path.filename();
150 if (reportId.empty())
151 {
152 {
153 BMCWEB_LOG_ERROR << "Property Reports contains invalid value: "
154 << path.str;
155 return std::nullopt;
156 }
157 }
158
Ed Tanous14766872022-03-15 10:44:42 -0700159 nlohmann::json::object_t report;
160 report["@odata.id"] =
161 crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
Szymon Dompke3f215c92022-02-22 13:58:00 +0100162 "MetricReportDefinitions", reportId);
Ed Tanous14766872022-03-15 10:44:42 -0700163 reports.push_back(std::move(report));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200164 }
165
Szymon Dompke3f215c92022-02-22 13:58:00 +0100166 return {std::move(reports)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200167}
168
169inline std::vector<std::string>
170 getMetricProperties(const TriggerSensorsParams& sensors)
171{
172 std::vector<std::string> metricProperties;
173 metricProperties.reserve(sensors.size());
174 for (const auto& [_, metadata] : sensors)
175 {
176 metricProperties.emplace_back(metadata);
177 }
178
179 return metricProperties;
180}
181
182inline bool fillTrigger(
183 nlohmann::json& json, const std::string& id,
184 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>&
185 properties)
186{
187 const std::string* name = nullptr;
188 const bool* discrete = nullptr;
189 const TriggerSensorsParams* sensors = nullptr;
Szymon Dompke3f215c92022-02-22 13:58:00 +0100190 const std::vector<sdbusplus::message::object_path>* reports = nullptr;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200191 const std::vector<std::string>* actions = nullptr;
192 const TriggerThresholdParamsExt* thresholds = nullptr;
193
194 for (const auto& [key, var] : properties)
195 {
196 if (key == "Name")
197 {
198 name = std::get_if<std::string>(&var);
199 }
200 else if (key == "Discrete")
201 {
202 discrete = std::get_if<bool>(&var);
203 }
204 else if (key == "Sensors")
205 {
206 sensors = std::get_if<TriggerSensorsParams>(&var);
207 }
Szymon Dompke3f215c92022-02-22 13:58:00 +0100208 else if (key == "Reports")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200209 {
Szymon Dompke3f215c92022-02-22 13:58:00 +0100210 reports =
211 std::get_if<std::vector<sdbusplus::message::object_path>>(&var);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200212 }
213 else if (key == "TriggerActions")
214 {
215 actions = std::get_if<std::vector<std::string>>(&var);
216 }
217 else if (key == "Thresholds")
218 {
219 thresholds = std::get_if<TriggerThresholdParamsExt>(&var);
220 }
221 }
222
Ed Tanouse662eae2022-01-25 10:39:19 -0800223 if (name == nullptr || discrete == nullptr || sensors == nullptr ||
224 reports == nullptr || actions == nullptr || thresholds == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200225 {
226 BMCWEB_LOG_ERROR
227 << "Property type mismatch or property is missing in Trigger: "
228 << id;
229 return false;
230 }
231
Szymon Dompke3f215c92022-02-22 13:58:00 +0100232 std::optional<std::vector<std::string>> triggerActions =
233 getTriggerActions(*actions);
234 if (!triggerActions)
235 {
236 BMCWEB_LOG_ERROR << "Property TriggerActions is invalid in Trigger: "
237 << id;
238 return false;
239 }
240
241 std::optional<nlohmann::json> linkedReports =
242 getMetricReportDefinitions(*reports);
243 if (!linkedReports)
244 {
245 BMCWEB_LOG_ERROR << "Property Reports is invalid in Trigger: " << id;
246 return false;
247 }
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200248
249 if (*discrete)
250 {
Szymon Dompke3f215c92022-02-22 13:58:00 +0100251 std::optional<nlohmann::json::array_t> discreteTriggers =
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200252 getDiscreteTriggers(*thresholds);
253
254 if (!discreteTriggers)
255 {
256 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for discrete "
257 "triggers in Trigger: "
258 << id;
259 return false;
260 }
261
262 json["DiscreteTriggers"] = *discreteTriggers;
263 json["DiscreteTriggerCondition"] =
264 discreteTriggers->empty() ? "Changed" : "Specified";
265 json["MetricType"] = "Discrete";
266 }
267 else
268 {
269 std::optional<nlohmann::json> numericThresholds =
270 getNumericThresholds(*thresholds);
271
272 if (!numericThresholds)
273 {
274 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for numeric "
275 "thresholds in Trigger: "
276 << id;
277 return false;
278 }
279
280 json["NumericThresholds"] = *numericThresholds;
281 json["MetricType"] = "Numeric";
282 }
283
Szymon Dompke3f215c92022-02-22 13:58:00 +0100284 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
285 json["@odata.id"] = crow::utility::urlFromPieces(
286 "redfish", "v1", "TelemetryService", "Triggers", id);
287 json["Id"] = id;
288 json["Name"] = *name;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200289 json["TriggerActions"] = *triggerActions;
290 json["MetricProperties"] = getMetricProperties(*sensors);
Szymon Dompke3f215c92022-02-22 13:58:00 +0100291 json["Links"]["MetricReportDefinitions"] = *linkedReports;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200292
293 return true;
294}
295
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200296} // namespace telemetry
297
298inline void requestRoutesTriggerCollection(App& app)
299{
300 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
301 .privileges(redfish::privileges::getTriggersCollection)
302 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700303 [&app](const crow::Request& req,
304 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000305 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700306 {
307 return;
308 }
309 asyncResp->res.jsonValue["@odata.type"] =
310 "#TriggersCollection.TriggersCollection";
311 asyncResp->res.jsonValue["@odata.id"] = telemetry::triggerUri;
312 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
313 const std::vector<const char*> interfaces{telemetry::triggerInterface};
314 collection_util::getCollectionMembers(
315 asyncResp, telemetry::triggerUri, interfaces,
316 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
317 });
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200318}
319
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200320inline void requestRoutesTrigger(App& app)
321{
322 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
323 .privileges(redfish::privileges::getTriggers)
324 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700325 [&app](const crow::Request& req,
326 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
327 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000328 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700329 {
330 return;
331 }
332 crow::connections::systemBus->async_method_call(
333 [asyncResp,
334 id](const boost::system::error_code ec,
335 const std::vector<std::pair<
336 std::string, telemetry::TriggerGetParamsVariant>>& ret) {
337 if (ec.value() == EBADR ||
338 ec == boost::system::errc::host_unreachable)
339 {
340 messages::resourceNotFound(asyncResp->res, "Triggers", id);
341 return;
342 }
343 if (ec)
344 {
345 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
346 messages::internalError(asyncResp->res);
347 return;
348 }
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200349
Ed Tanous002d39b2022-05-31 08:59:27 -0700350 if (!telemetry::fillTrigger(asyncResp->res.jsonValue, id, ret))
351 {
352 messages::internalError(asyncResp->res);
353 }
354 },
355 telemetry::service, telemetry::getDbusTriggerPath(id),
356 "org.freedesktop.DBus.Properties", "GetAll",
357 telemetry::triggerInterface);
358 });
Szymon Dompke163994a2021-08-12 17:30:23 +0200359
360 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
361 .privileges(redfish::privileges::deleteTriggers)
362 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700363 [&app](const crow::Request& req,
364 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
365 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000366 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700367 {
368 return;
369 }
370 const std::string triggerPath = telemetry::getDbusTriggerPath(id);
Szymon Dompke163994a2021-08-12 17:30:23 +0200371
Ed Tanous002d39b2022-05-31 08:59:27 -0700372 crow::connections::systemBus->async_method_call(
373 [asyncResp, id](const boost::system::error_code ec) {
374 if (ec.value() == EBADR)
375 {
376 messages::resourceNotFound(asyncResp->res, "Triggers", id);
377 return;
378 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200379
Ed Tanous002d39b2022-05-31 08:59:27 -0700380 if (ec)
381 {
382 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
383 messages::internalError(asyncResp->res);
384 return;
385 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200386
Ed Tanous002d39b2022-05-31 08:59:27 -0700387 asyncResp->res.result(boost::beast::http::status::no_content);
388 },
389 telemetry::service, triggerPath,
390 "xyz.openbmc_project.Object.Delete", "Delete");
391 });
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200392}
393
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200394} // namespace redfish