blob: ad3855621b3b8081789ced72a6287ebf54151837 [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";
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020023
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020024using NumericThresholdParams =
25 std::tuple<std::string, uint64_t, std::string, double>;
26
27using DiscreteThresholdParams =
28 std::tuple<std::string, std::string, uint64_t, std::string>;
29
30using TriggerThresholdParamsExt =
31 std::variant<std::monostate, std::vector<NumericThresholdParams>,
32 std::vector<DiscreteThresholdParams>>;
33
34using TriggerSensorsParams =
35 std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
36
37using TriggerGetParamsVariant =
38 std::variant<std::monostate, bool, std::string, TriggerThresholdParamsExt,
Szymon Dompke3f215c92022-02-22 13:58:00 +010039 TriggerSensorsParams, std::vector<std::string>,
40 std::vector<sdbusplus::message::object_path>>;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020041
42inline std::optional<std::string>
43 getRedfishFromDbusAction(const std::string& dbusAction)
44{
45 std::optional<std::string> redfishAction = std::nullopt;
46 if (dbusAction == "UpdateReport")
47 {
48 redfishAction = "RedfishMetricReport";
49 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010050 if (dbusAction == "LogToRedfishEventLog")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020051 {
52 redfishAction = "RedfishEvent";
53 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010054 if (dbusAction == "LogToJournal")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020055 {
56 redfishAction = "LogToLogService";
57 }
58 return redfishAction;
59}
60
61inline std::optional<std::vector<std::string>>
62 getTriggerActions(const std::vector<std::string>& dbusActions)
63{
64 std::vector<std::string> triggerActions;
65 for (const std::string& dbusAction : dbusActions)
66 {
67 std::optional<std::string> redfishAction =
68 getRedfishFromDbusAction(dbusAction);
69
70 if (!redfishAction)
71 {
72 return std::nullopt;
73 }
74
75 triggerActions.push_back(*redfishAction);
76 }
77
Szymon Dompke3f215c92022-02-22 13:58:00 +010078 return {std::move(triggerActions)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020079}
80
Szymon Dompke3f215c92022-02-22 13:58:00 +010081inline std::optional<nlohmann::json::array_t>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020082 getDiscreteTriggers(const TriggerThresholdParamsExt& thresholdParams)
83{
84 const std::vector<DiscreteThresholdParams>* discreteParams =
85 std::get_if<std::vector<DiscreteThresholdParams>>(&thresholdParams);
86
Ed Tanouse662eae2022-01-25 10:39:19 -080087 if (discreteParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020088 {
89 return std::nullopt;
90 }
91
Szymon Dompke3f215c92022-02-22 13:58:00 +010092 nlohmann::json::array_t triggers;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020093 for (const auto& [name, severity, dwellTime, value] : *discreteParams)
94 {
95 std::optional<std::string> duration =
96 time_utils::toDurationStringFromUint(dwellTime);
97
98 if (!duration)
99 {
100 return std::nullopt;
101 }
Ed Tanous613dabe2022-07-09 11:17:36 -0700102 nlohmann::json::object_t trigger;
103 trigger["Name"] = name;
104 trigger["Severity"] = severity;
105 trigger["DwellTime"] = *duration;
106 trigger["Value"] = value;
107 triggers.push_back(std::move(trigger));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200108 }
109
Szymon Dompke3f215c92022-02-22 13:58:00 +0100110 return {std::move(triggers)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200111}
112
113inline std::optional<nlohmann::json>
114 getNumericThresholds(const TriggerThresholdParamsExt& thresholdParams)
115{
116 const std::vector<NumericThresholdParams>* numericParams =
117 std::get_if<std::vector<NumericThresholdParams>>(&thresholdParams);
118
Ed Tanouse662eae2022-01-25 10:39:19 -0800119 if (numericParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200120 {
121 return std::nullopt;
122 }
123
Szymon Dompke3f215c92022-02-22 13:58:00 +0100124 nlohmann::json::object_t thresholds;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200125 for (const auto& [type, dwellTime, activation, reading] : *numericParams)
126 {
127 std::optional<std::string> duration =
128 time_utils::toDurationStringFromUint(dwellTime);
129
130 if (!duration)
131 {
132 return std::nullopt;
133 }
Ed Tanous14766872022-03-15 10:44:42 -0700134 nlohmann::json& threshold = thresholds[type];
135 threshold["Reading"] = reading;
136 threshold["Activation"] = activation;
137 threshold["DwellTime"] = *duration;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200138 }
139
Szymon Dompke3f215c92022-02-22 13:58:00 +0100140 return {std::move(thresholds)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200141}
142
Szymon Dompke3f215c92022-02-22 13:58:00 +0100143inline std::optional<nlohmann::json> getMetricReportDefinitions(
144 const std::vector<sdbusplus::message::object_path>& reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200145{
146 nlohmann::json reports = nlohmann::json::array();
Szymon Dompke3f215c92022-02-22 13:58:00 +0100147
148 for (const sdbusplus::message::object_path& path : reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200149 {
Szymon Dompke3f215c92022-02-22 13:58:00 +0100150 std::string reportId = path.filename();
151 if (reportId.empty())
152 {
153 {
154 BMCWEB_LOG_ERROR << "Property Reports contains invalid value: "
155 << path.str;
156 return std::nullopt;
157 }
158 }
159
Ed Tanous14766872022-03-15 10:44:42 -0700160 nlohmann::json::object_t report;
161 report["@odata.id"] =
162 crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
Szymon Dompke3f215c92022-02-22 13:58:00 +0100163 "MetricReportDefinitions", reportId);
Ed Tanous14766872022-03-15 10:44:42 -0700164 reports.push_back(std::move(report));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200165 }
166
Szymon Dompke3f215c92022-02-22 13:58:00 +0100167 return {std::move(reports)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200168}
169
170inline std::vector<std::string>
171 getMetricProperties(const TriggerSensorsParams& sensors)
172{
173 std::vector<std::string> metricProperties;
174 metricProperties.reserve(sensors.size());
175 for (const auto& [_, metadata] : sensors)
176 {
177 metricProperties.emplace_back(metadata);
178 }
179
180 return metricProperties;
181}
182
183inline bool fillTrigger(
184 nlohmann::json& json, const std::string& id,
185 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>&
186 properties)
187{
188 const std::string* name = nullptr;
189 const bool* discrete = nullptr;
190 const TriggerSensorsParams* sensors = nullptr;
Szymon Dompke3f215c92022-02-22 13:58:00 +0100191 const std::vector<sdbusplus::message::object_path>* reports = nullptr;
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200192 const std::vector<std::string>* triggerActions = nullptr;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200193 const TriggerThresholdParamsExt* thresholds = nullptr;
194
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200195 const bool success = sdbusplus::unpackPropertiesNoThrow(
196 dbus_utils::UnpackErrorPrinter(), properties, "Name", name, "Discrete",
197 discrete, "Sensors", sensors, "Reports", reports, "TriggerActions",
198 triggerActions, "Thresholds", thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200199
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200200 if (!success)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200201 {
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200202 return false;
203 }
204
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200205 if (triggerActions != nullptr)
Szymon Dompke3f215c92022-02-22 13:58:00 +0100206 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200207 std::optional<std::vector<std::string>> redfishTriggerActions =
208 getTriggerActions(*triggerActions);
209 if (!redfishTriggerActions)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200210 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200211 BMCWEB_LOG_ERROR
212 << "Property TriggerActions is invalid in Trigger: " << id;
213 return false;
214 }
215 json["TriggerActions"] = *triggerActions;
216 }
217
218 if (reports != nullptr)
219 {
220 std::optional<nlohmann::json> linkedReports =
221 getMetricReportDefinitions(*reports);
222 if (!linkedReports)
223 {
224 BMCWEB_LOG_ERROR << "Property Reports is invalid in Trigger: "
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200225 << id;
226 return false;
227 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200228 json["Links"]["MetricReportDefinitions"] = *linkedReports;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200229 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200230
231 if (discrete != nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200232 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200233 if (*discrete)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200234 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200235 std::optional<nlohmann::json::array_t> discreteTriggers =
236 getDiscreteTriggers(*thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200237
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200238 if (!discreteTriggers)
239 {
240 BMCWEB_LOG_ERROR
241 << "Property Thresholds is invalid for discrete "
242 "triggers in Trigger: "
243 << id;
244 return false;
245 }
246
247 json["DiscreteTriggers"] = *discreteTriggers;
248 json["DiscreteTriggerCondition"] =
249 discreteTriggers->empty() ? "Changed" : "Specified";
250 json["MetricType"] = "Discrete";
251 }
252 else
253 {
254 std::optional<nlohmann::json> numericThresholds =
255 getNumericThresholds(*thresholds);
256
257 if (!numericThresholds)
258 {
259 BMCWEB_LOG_ERROR
260 << "Property Thresholds is invalid for numeric "
261 "thresholds in Trigger: "
262 << id;
263 return false;
264 }
265
266 json["NumericThresholds"] = *numericThresholds;
267 json["MetricType"] = "Numeric";
268 }
269 }
270
271 if (name != nullptr)
272 {
273 json["Name"] = *name;
274 }
275
276 if (sensors != nullptr)
277 {
278 json["MetricProperties"] = getMetricProperties(*sensors);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200279 }
280
Szymon Dompke3f215c92022-02-22 13:58:00 +0100281 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
282 json["@odata.id"] = crow::utility::urlFromPieces(
283 "redfish", "v1", "TelemetryService", "Triggers", id);
284 json["Id"] = id;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200285
286 return true;
287}
288
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200289} // namespace telemetry
290
291inline void requestRoutesTriggerCollection(App& app)
292{
293 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
294 .privileges(redfish::privileges::getTriggersCollection)
295 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700296 [&app](const crow::Request& req,
297 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000298 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700299 {
300 return;
301 }
302 asyncResp->res.jsonValue["@odata.type"] =
303 "#TriggersCollection.TriggersCollection";
Willy Tuae9031f2022-09-27 05:48:07 +0000304 asyncResp->res.jsonValue["@odata.id"] =
305 "/redfish/v1/TelemetryService/Triggers";
Ed Tanous002d39b2022-05-31 08:59:27 -0700306 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
307 const std::vector<const char*> interfaces{telemetry::triggerInterface};
308 collection_util::getCollectionMembers(
Willy Tuae9031f2022-09-27 05:48:07 +0000309 asyncResp,
310 boost::urls::url("/redfish/v1/TelemetryService/Triggers"),
311 interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -0700312 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
313 });
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200314}
315
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200316inline void requestRoutesTrigger(App& app)
317{
318 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
319 .privileges(redfish::privileges::getTriggers)
320 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700321 [&app](const crow::Request& req,
322 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
323 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000324 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700325 {
326 return;
327 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200328 sdbusplus::asio::getAllProperties(
329 *crow::connections::systemBus, telemetry::service,
330 telemetry::getDbusTriggerPath(id), telemetry::triggerInterface,
Ed Tanous002d39b2022-05-31 08:59:27 -0700331 [asyncResp,
332 id](const boost::system::error_code ec,
333 const std::vector<std::pair<
334 std::string, telemetry::TriggerGetParamsVariant>>& ret) {
335 if (ec.value() == EBADR ||
336 ec == boost::system::errc::host_unreachable)
337 {
338 messages::resourceNotFound(asyncResp->res, "Triggers", id);
339 return;
340 }
341 if (ec)
342 {
343 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
344 messages::internalError(asyncResp->res);
345 return;
346 }
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200347
Ed Tanous002d39b2022-05-31 08:59:27 -0700348 if (!telemetry::fillTrigger(asyncResp->res.jsonValue, id, ret))
349 {
350 messages::internalError(asyncResp->res);
351 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200352 });
Ed Tanous002d39b2022-05-31 08:59:27 -0700353 });
Szymon Dompke163994a2021-08-12 17:30:23 +0200354
355 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
356 .privileges(redfish::privileges::deleteTriggers)
357 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700358 [&app](const crow::Request& req,
359 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
360 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000361 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700362 {
363 return;
364 }
365 const std::string triggerPath = telemetry::getDbusTriggerPath(id);
Szymon Dompke163994a2021-08-12 17:30:23 +0200366
Ed Tanous002d39b2022-05-31 08:59:27 -0700367 crow::connections::systemBus->async_method_call(
368 [asyncResp, id](const boost::system::error_code ec) {
369 if (ec.value() == EBADR)
370 {
371 messages::resourceNotFound(asyncResp->res, "Triggers", id);
372 return;
373 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200374
Ed Tanous002d39b2022-05-31 08:59:27 -0700375 if (ec)
376 {
377 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
378 messages::internalError(asyncResp->res);
379 return;
380 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200381
Ed Tanous002d39b2022-05-31 08:59:27 -0700382 asyncResp->res.result(boost::beast::http::status::no_content);
383 },
384 telemetry::service, triggerPath,
385 "xyz.openbmc_project.Object.Delete", "Delete");
386 });
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200387}
388
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200389} // namespace redfish