blob: 4da1ed5fdbfeafa759deb67ac3cf833b361efc61 [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
George Liu7a1dbc42022-12-07 16:03:22 +080013#include <array>
14#include <string_view>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020015#include <tuple>
16#include <variant>
17#include <vector>
18
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020019namespace redfish
20{
21namespace telemetry
22{
23constexpr const char* triggerInterface =
24 "xyz.openbmc_project.Telemetry.Trigger";
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020025
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020026using NumericThresholdParams =
27 std::tuple<std::string, uint64_t, std::string, double>;
28
29using DiscreteThresholdParams =
30 std::tuple<std::string, std::string, uint64_t, std::string>;
31
32using TriggerThresholdParamsExt =
33 std::variant<std::monostate, std::vector<NumericThresholdParams>,
34 std::vector<DiscreteThresholdParams>>;
35
36using TriggerSensorsParams =
37 std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
38
39using TriggerGetParamsVariant =
40 std::variant<std::monostate, bool, std::string, TriggerThresholdParamsExt,
Szymon Dompke3f215c92022-02-22 13:58:00 +010041 TriggerSensorsParams, std::vector<std::string>,
42 std::vector<sdbusplus::message::object_path>>;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020043
44inline std::optional<std::string>
45 getRedfishFromDbusAction(const std::string& dbusAction)
46{
47 std::optional<std::string> redfishAction = std::nullopt;
48 if (dbusAction == "UpdateReport")
49 {
50 redfishAction = "RedfishMetricReport";
51 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010052 if (dbusAction == "LogToRedfishEventLog")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020053 {
54 redfishAction = "RedfishEvent";
55 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010056 if (dbusAction == "LogToJournal")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020057 {
58 redfishAction = "LogToLogService";
59 }
60 return redfishAction;
61}
62
63inline std::optional<std::vector<std::string>>
64 getTriggerActions(const std::vector<std::string>& dbusActions)
65{
66 std::vector<std::string> triggerActions;
67 for (const std::string& dbusAction : dbusActions)
68 {
69 std::optional<std::string> redfishAction =
70 getRedfishFromDbusAction(dbusAction);
71
72 if (!redfishAction)
73 {
74 return std::nullopt;
75 }
76
77 triggerActions.push_back(*redfishAction);
78 }
79
Szymon Dompke3f215c92022-02-22 13:58:00 +010080 return {std::move(triggerActions)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020081}
82
Szymon Dompke3f215c92022-02-22 13:58:00 +010083inline std::optional<nlohmann::json::array_t>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020084 getDiscreteTriggers(const TriggerThresholdParamsExt& thresholdParams)
85{
86 const std::vector<DiscreteThresholdParams>* discreteParams =
87 std::get_if<std::vector<DiscreteThresholdParams>>(&thresholdParams);
88
Ed Tanouse662eae2022-01-25 10:39:19 -080089 if (discreteParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020090 {
91 return std::nullopt;
92 }
93
Szymon Dompke3f215c92022-02-22 13:58:00 +010094 nlohmann::json::array_t triggers;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020095 for (const auto& [name, severity, dwellTime, value] : *discreteParams)
96 {
97 std::optional<std::string> duration =
98 time_utils::toDurationStringFromUint(dwellTime);
99
100 if (!duration)
101 {
102 return std::nullopt;
103 }
Ed Tanous613dabe2022-07-09 11:17:36 -0700104 nlohmann::json::object_t trigger;
105 trigger["Name"] = name;
106 trigger["Severity"] = severity;
107 trigger["DwellTime"] = *duration;
108 trigger["Value"] = value;
109 triggers.push_back(std::move(trigger));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200110 }
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";
Willy Tuae9031f2022-09-27 05:48:07 +0000306 asyncResp->res.jsonValue["@odata.id"] =
307 "/redfish/v1/TelemetryService/Triggers";
Ed Tanous002d39b2022-05-31 08:59:27 -0700308 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
George Liu7a1dbc42022-12-07 16:03:22 +0800309 constexpr std::array<std::string_view, 1> interfaces{
310 telemetry::triggerInterface};
Ed Tanous002d39b2022-05-31 08:59:27 -0700311 collection_util::getCollectionMembers(
Willy Tuae9031f2022-09-27 05:48:07 +0000312 asyncResp,
313 boost::urls::url("/redfish/v1/TelemetryService/Triggers"),
314 interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -0700315 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
316 });
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200317}
318
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200319inline void requestRoutesTrigger(App& app)
320{
321 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
322 .privileges(redfish::privileges::getTriggers)
323 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700324 [&app](const crow::Request& req,
325 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
326 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000327 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700328 {
329 return;
330 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200331 sdbusplus::asio::getAllProperties(
332 *crow::connections::systemBus, telemetry::service,
333 telemetry::getDbusTriggerPath(id), telemetry::triggerInterface,
Ed Tanous002d39b2022-05-31 08:59:27 -0700334 [asyncResp,
335 id](const boost::system::error_code ec,
336 const std::vector<std::pair<
337 std::string, telemetry::TriggerGetParamsVariant>>& ret) {
338 if (ec.value() == EBADR ||
339 ec == boost::system::errc::host_unreachable)
340 {
341 messages::resourceNotFound(asyncResp->res, "Triggers", id);
342 return;
343 }
344 if (ec)
345 {
346 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
347 messages::internalError(asyncResp->res);
348 return;
349 }
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200350
Ed Tanous002d39b2022-05-31 08:59:27 -0700351 if (!telemetry::fillTrigger(asyncResp->res.jsonValue, id, ret))
352 {
353 messages::internalError(asyncResp->res);
354 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200355 });
Ed Tanous002d39b2022-05-31 08:59:27 -0700356 });
Szymon Dompke163994a2021-08-12 17:30:23 +0200357
358 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
359 .privileges(redfish::privileges::deleteTriggers)
360 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700361 [&app](const crow::Request& req,
362 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
363 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000364 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700365 {
366 return;
367 }
368 const std::string triggerPath = telemetry::getDbusTriggerPath(id);
Szymon Dompke163994a2021-08-12 17:30:23 +0200369
Ed Tanous002d39b2022-05-31 08:59:27 -0700370 crow::connections::systemBus->async_method_call(
371 [asyncResp, id](const boost::system::error_code ec) {
372 if (ec.value() == EBADR)
373 {
374 messages::resourceNotFound(asyncResp->res, "Triggers", id);
375 return;
376 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200377
Ed Tanous002d39b2022-05-31 08:59:27 -0700378 if (ec)
379 {
380 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
381 messages::internalError(asyncResp->res);
382 return;
383 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200384
Ed Tanous002d39b2022-05-31 08:59:27 -0700385 asyncResp->res.result(boost::beast::http::status::no_content);
386 },
387 telemetry::service, triggerPath,
388 "xyz.openbmc_project.Object.Delete", "Delete");
389 });
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200390}
391
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200392} // namespace redfish