blob: 68d6dd2fad971f7d04139a5e6a19d8b8f83c3b21 [file] [log] [blame]
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +02001#pragma once
2
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "app.hpp"
4#include "query.hpp"
5#include "registries/privilege_registry.hpp"
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +02006#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08007#include "utils/dbus_utils.hpp"
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +02008#include "utils/telemetry_utils.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08009#include "utils/time_utils.hpp"
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020010
Ed Tanousef4c65b2023-04-24 15:28:50 -070011#include <boost/url/format.hpp>
Krzysztof Grobelny89474492022-09-06 16:30:38 +020012#include <sdbusplus/asio/property.hpp>
13#include <sdbusplus/unpack_properties.hpp>
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020014
George Liu7a1dbc42022-12-07 16:03:22 +080015#include <array>
16#include <string_view>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020017#include <tuple>
18#include <variant>
19#include <vector>
20
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020021namespace redfish
22{
23namespace telemetry
24{
25constexpr const char* triggerInterface =
26 "xyz.openbmc_project.Telemetry.Trigger";
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020027
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020028using NumericThresholdParams =
29 std::tuple<std::string, uint64_t, std::string, double>;
30
31using DiscreteThresholdParams =
32 std::tuple<std::string, std::string, uint64_t, std::string>;
33
34using TriggerThresholdParamsExt =
35 std::variant<std::monostate, std::vector<NumericThresholdParams>,
36 std::vector<DiscreteThresholdParams>>;
37
38using TriggerSensorsParams =
39 std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
40
41using TriggerGetParamsVariant =
42 std::variant<std::monostate, bool, std::string, TriggerThresholdParamsExt,
Szymon Dompke3f215c92022-02-22 13:58:00 +010043 TriggerSensorsParams, std::vector<std::string>,
44 std::vector<sdbusplus::message::object_path>>;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020045
46inline std::optional<std::string>
47 getRedfishFromDbusAction(const std::string& dbusAction)
48{
49 std::optional<std::string> redfishAction = std::nullopt;
50 if (dbusAction == "UpdateReport")
51 {
52 redfishAction = "RedfishMetricReport";
53 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010054 if (dbusAction == "LogToRedfishEventLog")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020055 {
56 redfishAction = "RedfishEvent";
57 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010058 if (dbusAction == "LogToJournal")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020059 {
60 redfishAction = "LogToLogService";
61 }
62 return redfishAction;
63}
64
65inline std::optional<std::vector<std::string>>
66 getTriggerActions(const std::vector<std::string>& dbusActions)
67{
68 std::vector<std::string> triggerActions;
69 for (const std::string& dbusAction : dbusActions)
70 {
71 std::optional<std::string> redfishAction =
72 getRedfishFromDbusAction(dbusAction);
73
74 if (!redfishAction)
75 {
76 return std::nullopt;
77 }
78
79 triggerActions.push_back(*redfishAction);
80 }
81
Szymon Dompke3f215c92022-02-22 13:58:00 +010082 return {std::move(triggerActions)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020083}
84
Szymon Dompke3f215c92022-02-22 13:58:00 +010085inline std::optional<nlohmann::json::array_t>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020086 getDiscreteTriggers(const TriggerThresholdParamsExt& thresholdParams)
87{
88 const std::vector<DiscreteThresholdParams>* discreteParams =
89 std::get_if<std::vector<DiscreteThresholdParams>>(&thresholdParams);
90
Ed Tanouse662eae2022-01-25 10:39:19 -080091 if (discreteParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020092 {
93 return std::nullopt;
94 }
95
Szymon Dompke3f215c92022-02-22 13:58:00 +010096 nlohmann::json::array_t triggers;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020097 for (const auto& [name, severity, dwellTime, value] : *discreteParams)
98 {
99 std::optional<std::string> duration =
100 time_utils::toDurationStringFromUint(dwellTime);
101
102 if (!duration)
103 {
104 return std::nullopt;
105 }
Ed Tanous613dabe2022-07-09 11:17:36 -0700106 nlohmann::json::object_t trigger;
107 trigger["Name"] = name;
108 trigger["Severity"] = severity;
109 trigger["DwellTime"] = *duration;
110 trigger["Value"] = value;
Patrick Williamsad539542023-05-12 10:10:08 -0500111 triggers.emplace_back(std::move(trigger));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200112 }
113
Szymon Dompke3f215c92022-02-22 13:58:00 +0100114 return {std::move(triggers)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200115}
116
117inline std::optional<nlohmann::json>
118 getNumericThresholds(const TriggerThresholdParamsExt& thresholdParams)
119{
120 const std::vector<NumericThresholdParams>* numericParams =
121 std::get_if<std::vector<NumericThresholdParams>>(&thresholdParams);
122
Ed Tanouse662eae2022-01-25 10:39:19 -0800123 if (numericParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200124 {
125 return std::nullopt;
126 }
127
Szymon Dompke3f215c92022-02-22 13:58:00 +0100128 nlohmann::json::object_t thresholds;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200129 for (const auto& [type, dwellTime, activation, reading] : *numericParams)
130 {
131 std::optional<std::string> duration =
132 time_utils::toDurationStringFromUint(dwellTime);
133
134 if (!duration)
135 {
136 return std::nullopt;
137 }
Ed Tanous14766872022-03-15 10:44:42 -0700138 nlohmann::json& threshold = thresholds[type];
139 threshold["Reading"] = reading;
140 threshold["Activation"] = activation;
141 threshold["DwellTime"] = *duration;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200142 }
143
Szymon Dompke3f215c92022-02-22 13:58:00 +0100144 return {std::move(thresholds)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200145}
146
Szymon Dompke3f215c92022-02-22 13:58:00 +0100147inline std::optional<nlohmann::json> getMetricReportDefinitions(
148 const std::vector<sdbusplus::message::object_path>& reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200149{
150 nlohmann::json reports = nlohmann::json::array();
Szymon Dompke3f215c92022-02-22 13:58:00 +0100151
152 for (const sdbusplus::message::object_path& path : reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200153 {
Szymon Dompke3f215c92022-02-22 13:58:00 +0100154 std::string reportId = path.filename();
155 if (reportId.empty())
156 {
157 {
158 BMCWEB_LOG_ERROR << "Property Reports contains invalid value: "
159 << path.str;
160 return std::nullopt;
161 }
162 }
163
Ed Tanous14766872022-03-15 10:44:42 -0700164 nlohmann::json::object_t report;
Ed Tanousef4c65b2023-04-24 15:28:50 -0700165 report["@odata.id"] = boost::urls::format(
166 "/redfish/v1/TelemetryService/MetricReportDefinitions/{}",
167 reportId);
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500168 reports.emplace_back(std::move(report));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200169 }
170
Szymon Dompke3f215c92022-02-22 13:58:00 +0100171 return {std::move(reports)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200172}
173
174inline std::vector<std::string>
175 getMetricProperties(const TriggerSensorsParams& sensors)
176{
177 std::vector<std::string> metricProperties;
178 metricProperties.reserve(sensors.size());
179 for (const auto& [_, metadata] : sensors)
180 {
181 metricProperties.emplace_back(metadata);
182 }
183
184 return metricProperties;
185}
186
187inline bool fillTrigger(
188 nlohmann::json& json, const std::string& id,
189 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>&
190 properties)
191{
192 const std::string* name = nullptr;
193 const bool* discrete = nullptr;
194 const TriggerSensorsParams* sensors = nullptr;
Szymon Dompke3f215c92022-02-22 13:58:00 +0100195 const std::vector<sdbusplus::message::object_path>* reports = nullptr;
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200196 const std::vector<std::string>* triggerActions = nullptr;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200197 const TriggerThresholdParamsExt* thresholds = nullptr;
198
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200199 const bool success = sdbusplus::unpackPropertiesNoThrow(
200 dbus_utils::UnpackErrorPrinter(), properties, "Name", name, "Discrete",
201 discrete, "Sensors", sensors, "Reports", reports, "TriggerActions",
202 triggerActions, "Thresholds", thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200203
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200204 if (!success)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200205 {
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200206 return false;
207 }
208
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200209 if (triggerActions != nullptr)
Szymon Dompke3f215c92022-02-22 13:58:00 +0100210 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200211 std::optional<std::vector<std::string>> redfishTriggerActions =
212 getTriggerActions(*triggerActions);
213 if (!redfishTriggerActions)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200214 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200215 BMCWEB_LOG_ERROR
216 << "Property TriggerActions is invalid in Trigger: " << id;
217 return false;
218 }
219 json["TriggerActions"] = *triggerActions;
220 }
221
222 if (reports != nullptr)
223 {
224 std::optional<nlohmann::json> linkedReports =
225 getMetricReportDefinitions(*reports);
226 if (!linkedReports)
227 {
228 BMCWEB_LOG_ERROR << "Property Reports is invalid in Trigger: "
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200229 << id;
230 return false;
231 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200232 json["Links"]["MetricReportDefinitions"] = *linkedReports;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200233 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200234
235 if (discrete != nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200236 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200237 if (*discrete)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200238 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200239 std::optional<nlohmann::json::array_t> discreteTriggers =
240 getDiscreteTriggers(*thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200241
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200242 if (!discreteTriggers)
243 {
244 BMCWEB_LOG_ERROR
245 << "Property Thresholds is invalid for discrete "
246 "triggers in Trigger: "
247 << id;
248 return false;
249 }
250
251 json["DiscreteTriggers"] = *discreteTriggers;
252 json["DiscreteTriggerCondition"] =
253 discreteTriggers->empty() ? "Changed" : "Specified";
254 json["MetricType"] = "Discrete";
255 }
256 else
257 {
258 std::optional<nlohmann::json> numericThresholds =
259 getNumericThresholds(*thresholds);
260
261 if (!numericThresholds)
262 {
263 BMCWEB_LOG_ERROR
264 << "Property Thresholds is invalid for numeric "
265 "thresholds in Trigger: "
266 << id;
267 return false;
268 }
269
270 json["NumericThresholds"] = *numericThresholds;
271 json["MetricType"] = "Numeric";
272 }
273 }
274
275 if (name != nullptr)
276 {
277 json["Name"] = *name;
278 }
279
280 if (sensors != nullptr)
281 {
282 json["MetricProperties"] = getMetricProperties(*sensors);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200283 }
284
Szymon Dompke3f215c92022-02-22 13:58:00 +0100285 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
Ed Tanousef4c65b2023-04-24 15:28:50 -0700286 json["@odata.id"] =
287 boost::urls::format("/redfish/v1/TelemetryService/Triggers/{}", id);
Szymon Dompke3f215c92022-02-22 13:58:00 +0100288 json["Id"] = id;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200289
290 return true;
291}
292
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200293} // namespace telemetry
294
295inline void requestRoutesTriggerCollection(App& app)
296{
297 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
298 .privileges(redfish::privileges::getTriggersCollection)
299 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700300 [&app](const crow::Request& req,
301 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000302 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700303 {
304 return;
305 }
306 asyncResp->res.jsonValue["@odata.type"] =
307 "#TriggersCollection.TriggersCollection";
Willy Tuae9031f2022-09-27 05:48:07 +0000308 asyncResp->res.jsonValue["@odata.id"] =
309 "/redfish/v1/TelemetryService/Triggers";
Ed Tanous002d39b2022-05-31 08:59:27 -0700310 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
George Liu7a1dbc42022-12-07 16:03:22 +0800311 constexpr std::array<std::string_view, 1> interfaces{
312 telemetry::triggerInterface};
Ed Tanous002d39b2022-05-31 08:59:27 -0700313 collection_util::getCollectionMembers(
Willy Tuae9031f2022-09-27 05:48:07 +0000314 asyncResp,
315 boost::urls::url("/redfish/v1/TelemetryService/Triggers"),
316 interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -0700317 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
318 });
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200319}
320
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200321inline void requestRoutesTrigger(App& app)
322{
323 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
324 .privileges(redfish::privileges::getTriggers)
325 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700326 [&app](const crow::Request& req,
327 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
328 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000329 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700330 {
331 return;
332 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200333 sdbusplus::asio::getAllProperties(
334 *crow::connections::systemBus, telemetry::service,
335 telemetry::getDbusTriggerPath(id), telemetry::triggerInterface,
Ed Tanous002d39b2022-05-31 08:59:27 -0700336 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800337 id](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700338 const std::vector<std::pair<
339 std::string, telemetry::TriggerGetParamsVariant>>& ret) {
340 if (ec.value() == EBADR ||
341 ec == boost::system::errc::host_unreachable)
342 {
343 messages::resourceNotFound(asyncResp->res, "Triggers", id);
344 return;
345 }
346 if (ec)
347 {
348 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
349 messages::internalError(asyncResp->res);
350 return;
351 }
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200352
Ed Tanous002d39b2022-05-31 08:59:27 -0700353 if (!telemetry::fillTrigger(asyncResp->res.jsonValue, id, ret))
354 {
355 messages::internalError(asyncResp->res);
356 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200357 });
Ed Tanous002d39b2022-05-31 08:59:27 -0700358 });
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(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800373 [asyncResp, id](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700374 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