blob: 5e2f05003a11b7b7f6d546d561daf4b8efc057aa [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 }
103
104 triggers.push_back({
105 {"Name", name},
106 {"Severity", severity},
107 {"DwellTime", *duration},
108 {"Value", value},
109 });
110 }
111
Szymon Dompke3f215c92022-02-22 13:58:00 +0100112 return {std::move(triggers)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200113}
114
115inline std::optional<nlohmann::json>
116 getNumericThresholds(const TriggerThresholdParamsExt& thresholdParams)
117{
118 const std::vector<NumericThresholdParams>* numericParams =
119 std::get_if<std::vector<NumericThresholdParams>>(&thresholdParams);
120
Ed Tanouse662eae2022-01-25 10:39:19 -0800121 if (numericParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200122 {
123 return std::nullopt;
124 }
125
Szymon Dompke3f215c92022-02-22 13:58:00 +0100126 nlohmann::json::object_t thresholds;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200127 for (const auto& [type, dwellTime, activation, reading] : *numericParams)
128 {
129 std::optional<std::string> duration =
130 time_utils::toDurationStringFromUint(dwellTime);
131
132 if (!duration)
133 {
134 return std::nullopt;
135 }
Ed Tanous14766872022-03-15 10:44:42 -0700136 nlohmann::json& threshold = thresholds[type];
137 threshold["Reading"] = reading;
138 threshold["Activation"] = activation;
139 threshold["DwellTime"] = *duration;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200140 }
141
Szymon Dompke3f215c92022-02-22 13:58:00 +0100142 return {std::move(thresholds)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200143}
144
Szymon Dompke3f215c92022-02-22 13:58:00 +0100145inline std::optional<nlohmann::json> getMetricReportDefinitions(
146 const std::vector<sdbusplus::message::object_path>& reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200147{
148 nlohmann::json reports = nlohmann::json::array();
Szymon Dompke3f215c92022-02-22 13:58:00 +0100149
150 for (const sdbusplus::message::object_path& path : reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200151 {
Szymon Dompke3f215c92022-02-22 13:58:00 +0100152 std::string reportId = path.filename();
153 if (reportId.empty())
154 {
155 {
156 BMCWEB_LOG_ERROR << "Property Reports contains invalid value: "
157 << path.str;
158 return std::nullopt;
159 }
160 }
161
Ed Tanous14766872022-03-15 10:44:42 -0700162 nlohmann::json::object_t report;
163 report["@odata.id"] =
164 crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
Szymon Dompke3f215c92022-02-22 13:58:00 +0100165 "MetricReportDefinitions", reportId);
Ed Tanous14766872022-03-15 10:44:42 -0700166 reports.push_back(std::move(report));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200167 }
168
Szymon Dompke3f215c92022-02-22 13:58:00 +0100169 return {std::move(reports)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200170}
171
172inline std::vector<std::string>
173 getMetricProperties(const TriggerSensorsParams& sensors)
174{
175 std::vector<std::string> metricProperties;
176 metricProperties.reserve(sensors.size());
177 for (const auto& [_, metadata] : sensors)
178 {
179 metricProperties.emplace_back(metadata);
180 }
181
182 return metricProperties;
183}
184
185inline bool fillTrigger(
186 nlohmann::json& json, const std::string& id,
187 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>&
188 properties)
189{
190 const std::string* name = nullptr;
191 const bool* discrete = nullptr;
192 const TriggerSensorsParams* sensors = nullptr;
Szymon Dompke3f215c92022-02-22 13:58:00 +0100193 const std::vector<sdbusplus::message::object_path>* reports = nullptr;
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200194 const std::vector<std::string>* triggerActions = nullptr;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200195 const TriggerThresholdParamsExt* thresholds = nullptr;
196
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200197 const bool success = sdbusplus::unpackPropertiesNoThrow(
198 dbus_utils::UnpackErrorPrinter(), properties, "Name", name, "Discrete",
199 discrete, "Sensors", sensors, "Reports", reports, "TriggerActions",
200 triggerActions, "Thresholds", thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200201
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200202 if (!success)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200203 {
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200204 return false;
205 }
206
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200207 if (triggerActions != nullptr)
Szymon Dompke3f215c92022-02-22 13:58:00 +0100208 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200209 std::optional<std::vector<std::string>> redfishTriggerActions =
210 getTriggerActions(*triggerActions);
211 if (!redfishTriggerActions)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200212 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200213 BMCWEB_LOG_ERROR
214 << "Property TriggerActions is invalid in Trigger: " << id;
215 return false;
216 }
217 json["TriggerActions"] = *triggerActions;
218 }
219
220 if (reports != nullptr)
221 {
222 std::optional<nlohmann::json> linkedReports =
223 getMetricReportDefinitions(*reports);
224 if (!linkedReports)
225 {
226 BMCWEB_LOG_ERROR << "Property Reports is invalid in Trigger: "
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200227 << id;
228 return false;
229 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200230 json["Links"]["MetricReportDefinitions"] = *linkedReports;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200231 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200232
233 if (discrete != nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200234 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200235 if (*discrete)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200236 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200237 std::optional<nlohmann::json::array_t> discreteTriggers =
238 getDiscreteTriggers(*thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200239
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200240 if (!discreteTriggers)
241 {
242 BMCWEB_LOG_ERROR
243 << "Property Thresholds is invalid for discrete "
244 "triggers in Trigger: "
245 << id;
246 return false;
247 }
248
249 json["DiscreteTriggers"] = *discreteTriggers;
250 json["DiscreteTriggerCondition"] =
251 discreteTriggers->empty() ? "Changed" : "Specified";
252 json["MetricType"] = "Discrete";
253 }
254 else
255 {
256 std::optional<nlohmann::json> numericThresholds =
257 getNumericThresholds(*thresholds);
258
259 if (!numericThresholds)
260 {
261 BMCWEB_LOG_ERROR
262 << "Property Thresholds is invalid for numeric "
263 "thresholds in Trigger: "
264 << id;
265 return false;
266 }
267
268 json["NumericThresholds"] = *numericThresholds;
269 json["MetricType"] = "Numeric";
270 }
271 }
272
273 if (name != nullptr)
274 {
275 json["Name"] = *name;
276 }
277
278 if (sensors != nullptr)
279 {
280 json["MetricProperties"] = getMetricProperties(*sensors);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200281 }
282
Szymon Dompke3f215c92022-02-22 13:58:00 +0100283 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
284 json["@odata.id"] = crow::utility::urlFromPieces(
285 "redfish", "v1", "TelemetryService", "Triggers", id);
286 json["Id"] = id;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200287
288 return true;
289}
290
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200291} // namespace telemetry
292
293inline void requestRoutesTriggerCollection(App& app)
294{
295 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
296 .privileges(redfish::privileges::getTriggersCollection)
297 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700298 [&app](const crow::Request& req,
299 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000300 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700301 {
302 return;
303 }
304 asyncResp->res.jsonValue["@odata.type"] =
305 "#TriggersCollection.TriggersCollection";
306 asyncResp->res.jsonValue["@odata.id"] = telemetry::triggerUri;
307 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
308 const std::vector<const char*> interfaces{telemetry::triggerInterface};
309 collection_util::getCollectionMembers(
310 asyncResp, telemetry::triggerUri, interfaces,
311 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
312 });
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200313}
314
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200315inline void requestRoutesTrigger(App& app)
316{
317 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
318 .privileges(redfish::privileges::getTriggers)
319 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700320 [&app](const crow::Request& req,
321 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
322 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000323 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700324 {
325 return;
326 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200327 sdbusplus::asio::getAllProperties(
328 *crow::connections::systemBus, telemetry::service,
329 telemetry::getDbusTriggerPath(id), telemetry::triggerInterface,
Ed Tanous002d39b2022-05-31 08:59:27 -0700330 [asyncResp,
331 id](const boost::system::error_code ec,
332 const std::vector<std::pair<
333 std::string, telemetry::TriggerGetParamsVariant>>& ret) {
334 if (ec.value() == EBADR ||
335 ec == boost::system::errc::host_unreachable)
336 {
337 messages::resourceNotFound(asyncResp->res, "Triggers", id);
338 return;
339 }
340 if (ec)
341 {
342 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
343 messages::internalError(asyncResp->res);
344 return;
345 }
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200346
Ed Tanous002d39b2022-05-31 08:59:27 -0700347 if (!telemetry::fillTrigger(asyncResp->res.jsonValue, id, ret))
348 {
349 messages::internalError(asyncResp->res);
350 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200351 });
Ed Tanous002d39b2022-05-31 08:59:27 -0700352 });
Szymon Dompke163994a2021-08-12 17:30:23 +0200353
354 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
355 .privileges(redfish::privileges::deleteTriggers)
356 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700357 [&app](const crow::Request& req,
358 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
359 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000360 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700361 {
362 return;
363 }
364 const std::string triggerPath = telemetry::getDbusTriggerPath(id);
Szymon Dompke163994a2021-08-12 17:30:23 +0200365
Ed Tanous002d39b2022-05-31 08:59:27 -0700366 crow::connections::systemBus->async_method_call(
367 [asyncResp, id](const boost::system::error_code ec) {
368 if (ec.value() == EBADR)
369 {
370 messages::resourceNotFound(asyncResp->res, "Triggers", id);
371 return;
372 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200373
Ed Tanous002d39b2022-05-31 08:59:27 -0700374 if (ec)
375 {
376 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
377 messages::internalError(asyncResp->res);
378 return;
379 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200380
Ed Tanous002d39b2022-05-31 08:59:27 -0700381 asyncResp->res.result(boost::beast::http::status::no_content);
382 },
383 telemetry::service, triggerPath,
384 "xyz.openbmc_project.Object.Delete", "Delete");
385 });
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200386}
387
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200388} // namespace redfish