blob: 811d35512433046a87bc8fd48c0050bdc202f046 [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>
Krzysztof Grobelny89474492022-09-06 16:30:38 +02009#include <sdbusplus/asio/property.hpp>
10#include <sdbusplus/unpack_properties.hpp>
11#include <utils/dbus_utils.hpp>
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020012
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020013#include <tuple>
14#include <variant>
15#include <vector>
16
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020017namespace redfish
18{
19namespace telemetry
20{
21constexpr const char* triggerInterface =
22 "xyz.openbmc_project.Telemetry.Trigger";
23constexpr const char* triggerUri = "/redfish/v1/TelemetryService/Triggers";
24
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020025using NumericThresholdParams =
26 std::tuple<std::string, uint64_t, std::string, double>;
27
28using DiscreteThresholdParams =
29 std::tuple<std::string, std::string, uint64_t, std::string>;
30
31using TriggerThresholdParamsExt =
32 std::variant<std::monostate, std::vector<NumericThresholdParams>,
33 std::vector<DiscreteThresholdParams>>;
34
35using TriggerSensorsParams =
36 std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
37
38using TriggerGetParamsVariant =
39 std::variant<std::monostate, bool, std::string, TriggerThresholdParamsExt,
Szymon Dompke3f215c92022-02-22 13:58:00 +010040 TriggerSensorsParams, std::vector<std::string>,
41 std::vector<sdbusplus::message::object_path>>;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020042
43inline std::optional<std::string>
44 getRedfishFromDbusAction(const std::string& dbusAction)
45{
46 std::optional<std::string> redfishAction = std::nullopt;
47 if (dbusAction == "UpdateReport")
48 {
49 redfishAction = "RedfishMetricReport";
50 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010051 if (dbusAction == "LogToRedfishEventLog")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020052 {
53 redfishAction = "RedfishEvent";
54 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010055 if (dbusAction == "LogToJournal")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020056 {
57 redfishAction = "LogToLogService";
58 }
59 return redfishAction;
60}
61
62inline std::optional<std::vector<std::string>>
63 getTriggerActions(const std::vector<std::string>& dbusActions)
64{
65 std::vector<std::string> triggerActions;
66 for (const std::string& dbusAction : dbusActions)
67 {
68 std::optional<std::string> redfishAction =
69 getRedfishFromDbusAction(dbusAction);
70
71 if (!redfishAction)
72 {
73 return std::nullopt;
74 }
75
76 triggerActions.push_back(*redfishAction);
77 }
78
Szymon Dompke3f215c92022-02-22 13:58:00 +010079 return {std::move(triggerActions)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020080}
81
Szymon Dompke3f215c92022-02-22 13:58:00 +010082inline std::optional<nlohmann::json::array_t>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020083 getDiscreteTriggers(const TriggerThresholdParamsExt& thresholdParams)
84{
85 const std::vector<DiscreteThresholdParams>* discreteParams =
86 std::get_if<std::vector<DiscreteThresholdParams>>(&thresholdParams);
87
Ed Tanouse662eae2022-01-25 10:39:19 -080088 if (discreteParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020089 {
90 return std::nullopt;
91 }
92
Szymon Dompke3f215c92022-02-22 13:58:00 +010093 nlohmann::json::array_t triggers;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020094 for (const auto& [name, severity, dwellTime, value] : *discreteParams)
95 {
96 std::optional<std::string> duration =
97 time_utils::toDurationStringFromUint(dwellTime);
98
99 if (!duration)
100 {
101 return std::nullopt;
102 }
Ed Tanous613dabe2022-07-09 11:17:36 -0700103 nlohmann::json::object_t trigger;
104 trigger["Name"] = name;
105 trigger["Severity"] = severity;
106 trigger["DwellTime"] = *duration;
107 trigger["Value"] = value;
108 triggers.push_back(std::move(trigger));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200109 }
110
Szymon Dompke3f215c92022-02-22 13:58:00 +0100111 return {std::move(triggers)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200112}
113
114inline std::optional<nlohmann::json>
115 getNumericThresholds(const TriggerThresholdParamsExt& thresholdParams)
116{
117 const std::vector<NumericThresholdParams>* numericParams =
118 std::get_if<std::vector<NumericThresholdParams>>(&thresholdParams);
119
Ed Tanouse662eae2022-01-25 10:39:19 -0800120 if (numericParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200121 {
122 return std::nullopt;
123 }
124
Szymon Dompke3f215c92022-02-22 13:58:00 +0100125 nlohmann::json::object_t thresholds;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200126 for (const auto& [type, dwellTime, activation, reading] : *numericParams)
127 {
128 std::optional<std::string> duration =
129 time_utils::toDurationStringFromUint(dwellTime);
130
131 if (!duration)
132 {
133 return std::nullopt;
134 }
Ed Tanous14766872022-03-15 10:44:42 -0700135 nlohmann::json& threshold = thresholds[type];
136 threshold["Reading"] = reading;
137 threshold["Activation"] = activation;
138 threshold["DwellTime"] = *duration;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200139 }
140
Szymon Dompke3f215c92022-02-22 13:58:00 +0100141 return {std::move(thresholds)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200142}
143
Szymon Dompke3f215c92022-02-22 13:58:00 +0100144inline std::optional<nlohmann::json> getMetricReportDefinitions(
145 const std::vector<sdbusplus::message::object_path>& reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200146{
147 nlohmann::json reports = nlohmann::json::array();
Szymon Dompke3f215c92022-02-22 13:58:00 +0100148
149 for (const sdbusplus::message::object_path& path : reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200150 {
Szymon Dompke3f215c92022-02-22 13:58:00 +0100151 std::string reportId = path.filename();
152 if (reportId.empty())
153 {
154 {
155 BMCWEB_LOG_ERROR << "Property Reports contains invalid value: "
156 << path.str;
157 return std::nullopt;
158 }
159 }
160
Ed Tanous14766872022-03-15 10:44:42 -0700161 nlohmann::json::object_t report;
162 report["@odata.id"] =
163 crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
Szymon Dompke3f215c92022-02-22 13:58:00 +0100164 "MetricReportDefinitions", reportId);
Ed Tanous14766872022-03-15 10:44:42 -0700165 reports.push_back(std::move(report));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200166 }
167
Szymon Dompke3f215c92022-02-22 13:58:00 +0100168 return {std::move(reports)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200169}
170
171inline std::vector<std::string>
172 getMetricProperties(const TriggerSensorsParams& sensors)
173{
174 std::vector<std::string> metricProperties;
175 metricProperties.reserve(sensors.size());
176 for (const auto& [_, metadata] : sensors)
177 {
178 metricProperties.emplace_back(metadata);
179 }
180
181 return metricProperties;
182}
183
184inline bool fillTrigger(
185 nlohmann::json& json, const std::string& id,
186 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>&
187 properties)
188{
189 const std::string* name = nullptr;
190 const bool* discrete = nullptr;
191 const TriggerSensorsParams* sensors = nullptr;
Szymon Dompke3f215c92022-02-22 13:58:00 +0100192 const std::vector<sdbusplus::message::object_path>* reports = nullptr;
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200193 const std::vector<std::string>* triggerActions = nullptr;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200194 const TriggerThresholdParamsExt* thresholds = nullptr;
195
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200196 const bool success = sdbusplus::unpackPropertiesNoThrow(
197 dbus_utils::UnpackErrorPrinter(), properties, "Name", name, "Discrete",
198 discrete, "Sensors", sensors, "Reports", reports, "TriggerActions",
199 triggerActions, "Thresholds", thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200200
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200201 if (!success)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200202 {
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200203 return false;
204 }
205
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200206 if (triggerActions != nullptr)
Szymon Dompke3f215c92022-02-22 13:58:00 +0100207 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200208 std::optional<std::vector<std::string>> redfishTriggerActions =
209 getTriggerActions(*triggerActions);
210 if (!redfishTriggerActions)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200211 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200212 BMCWEB_LOG_ERROR
213 << "Property TriggerActions is invalid in Trigger: " << id;
214 return false;
215 }
216 json["TriggerActions"] = *triggerActions;
217 }
218
219 if (reports != nullptr)
220 {
221 std::optional<nlohmann::json> linkedReports =
222 getMetricReportDefinitions(*reports);
223 if (!linkedReports)
224 {
225 BMCWEB_LOG_ERROR << "Property Reports is invalid in Trigger: "
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200226 << id;
227 return false;
228 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200229 json["Links"]["MetricReportDefinitions"] = *linkedReports;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200230 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200231
232 if (discrete != nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200233 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200234 if (*discrete)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200235 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200236 std::optional<nlohmann::json::array_t> discreteTriggers =
237 getDiscreteTriggers(*thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200238
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200239 if (!discreteTriggers)
240 {
241 BMCWEB_LOG_ERROR
242 << "Property Thresholds is invalid for discrete "
243 "triggers in Trigger: "
244 << id;
245 return false;
246 }
247
248 json["DiscreteTriggers"] = *discreteTriggers;
249 json["DiscreteTriggerCondition"] =
250 discreteTriggers->empty() ? "Changed" : "Specified";
251 json["MetricType"] = "Discrete";
252 }
253 else
254 {
255 std::optional<nlohmann::json> numericThresholds =
256 getNumericThresholds(*thresholds);
257
258 if (!numericThresholds)
259 {
260 BMCWEB_LOG_ERROR
261 << "Property Thresholds is invalid for numeric "
262 "thresholds in Trigger: "
263 << id;
264 return false;
265 }
266
267 json["NumericThresholds"] = *numericThresholds;
268 json["MetricType"] = "Numeric";
269 }
270 }
271
272 if (name != nullptr)
273 {
274 json["Name"] = *name;
275 }
276
277 if (sensors != nullptr)
278 {
279 json["MetricProperties"] = getMetricProperties(*sensors);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200280 }
281
Szymon Dompke3f215c92022-02-22 13:58:00 +0100282 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
283 json["@odata.id"] = crow::utility::urlFromPieces(
284 "redfish", "v1", "TelemetryService", "Triggers", id);
285 json["Id"] = id;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200286
287 return true;
288}
289
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200290} // namespace telemetry
291
292inline void requestRoutesTriggerCollection(App& app)
293{
294 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
295 .privileges(redfish::privileges::getTriggersCollection)
296 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700297 [&app](const crow::Request& req,
298 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000299 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700300 {
301 return;
302 }
303 asyncResp->res.jsonValue["@odata.type"] =
304 "#TriggersCollection.TriggersCollection";
305 asyncResp->res.jsonValue["@odata.id"] = telemetry::triggerUri;
306 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
307 const std::vector<const char*> interfaces{telemetry::triggerInterface};
308 collection_util::getCollectionMembers(
309 asyncResp, telemetry::triggerUri, interfaces,
310 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
311 });
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200312}
313
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200314inline void requestRoutesTrigger(App& app)
315{
316 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
317 .privileges(redfish::privileges::getTriggers)
318 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700319 [&app](const crow::Request& req,
320 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
321 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000322 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700323 {
324 return;
325 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200326 sdbusplus::asio::getAllProperties(
327 *crow::connections::systemBus, telemetry::service,
328 telemetry::getDbusTriggerPath(id), telemetry::triggerInterface,
Ed Tanous002d39b2022-05-31 08:59:27 -0700329 [asyncResp,
330 id](const boost::system::error_code ec,
331 const std::vector<std::pair<
332 std::string, telemetry::TriggerGetParamsVariant>>& ret) {
333 if (ec.value() == EBADR ||
334 ec == boost::system::errc::host_unreachable)
335 {
336 messages::resourceNotFound(asyncResp->res, "Triggers", id);
337 return;
338 }
339 if (ec)
340 {
341 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
342 messages::internalError(asyncResp->res);
343 return;
344 }
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200345
Ed Tanous002d39b2022-05-31 08:59:27 -0700346 if (!telemetry::fillTrigger(asyncResp->res.jsonValue, id, ret))
347 {
348 messages::internalError(asyncResp->res);
349 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200350 });
Ed Tanous002d39b2022-05-31 08:59:27 -0700351 });
Szymon Dompke163994a2021-08-12 17:30:23 +0200352
353 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
354 .privileges(redfish::privileges::deleteTriggers)
355 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700356 [&app](const crow::Request& req,
357 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
358 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000359 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700360 {
361 return;
362 }
363 const std::string triggerPath = telemetry::getDbusTriggerPath(id);
Szymon Dompke163994a2021-08-12 17:30:23 +0200364
Ed Tanous002d39b2022-05-31 08:59:27 -0700365 crow::connections::systemBus->async_method_call(
366 [asyncResp, id](const boost::system::error_code ec) {
367 if (ec.value() == EBADR)
368 {
369 messages::resourceNotFound(asyncResp->res, "Triggers", id);
370 return;
371 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200372
Ed Tanous002d39b2022-05-31 08:59:27 -0700373 if (ec)
374 {
375 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
376 messages::internalError(asyncResp->res);
377 return;
378 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200379
Ed Tanous002d39b2022-05-31 08:59:27 -0700380 asyncResp->res.result(boost::beast::http::status::no_content);
381 },
382 telemetry::service, triggerPath,
383 "xyz.openbmc_project.Object.Delete", "Delete");
384 });
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200385}
386
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200387} // namespace redfish