blob: cb2707543ae4b20d5c0a7b3fe573dae6bedf396e [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
Krzysztof Grobelny89474492022-09-06 16:30:38 +020011#include <sdbusplus/asio/property.hpp>
12#include <sdbusplus/unpack_properties.hpp>
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020013
George Liu7a1dbc42022-12-07 16:03:22 +080014#include <array>
15#include <string_view>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020016#include <tuple>
17#include <variant>
18#include <vector>
19
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020020namespace redfish
21{
22namespace telemetry
23{
24constexpr const char* triggerInterface =
25 "xyz.openbmc_project.Telemetry.Trigger";
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020026
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020027using NumericThresholdParams =
28 std::tuple<std::string, uint64_t, std::string, double>;
29
30using DiscreteThresholdParams =
31 std::tuple<std::string, std::string, uint64_t, std::string>;
32
33using TriggerThresholdParamsExt =
34 std::variant<std::monostate, std::vector<NumericThresholdParams>,
35 std::vector<DiscreteThresholdParams>>;
36
37using TriggerSensorsParams =
38 std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
39
40using TriggerGetParamsVariant =
41 std::variant<std::monostate, bool, std::string, TriggerThresholdParamsExt,
Szymon Dompke3f215c92022-02-22 13:58:00 +010042 TriggerSensorsParams, std::vector<std::string>,
43 std::vector<sdbusplus::message::object_path>>;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020044
45inline std::optional<std::string>
46 getRedfishFromDbusAction(const std::string& dbusAction)
47{
48 std::optional<std::string> redfishAction = std::nullopt;
49 if (dbusAction == "UpdateReport")
50 {
51 redfishAction = "RedfishMetricReport";
52 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010053 if (dbusAction == "LogToRedfishEventLog")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020054 {
55 redfishAction = "RedfishEvent";
56 }
Szymon Dompke3f215c92022-02-22 13:58:00 +010057 if (dbusAction == "LogToJournal")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020058 {
59 redfishAction = "LogToLogService";
60 }
61 return redfishAction;
62}
63
64inline std::optional<std::vector<std::string>>
65 getTriggerActions(const std::vector<std::string>& dbusActions)
66{
67 std::vector<std::string> triggerActions;
68 for (const std::string& dbusAction : dbusActions)
69 {
70 std::optional<std::string> redfishAction =
71 getRedfishFromDbusAction(dbusAction);
72
73 if (!redfishAction)
74 {
75 return std::nullopt;
76 }
77
78 triggerActions.push_back(*redfishAction);
79 }
80
Szymon Dompke3f215c92022-02-22 13:58:00 +010081 return {std::move(triggerActions)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020082}
83
Szymon Dompke3f215c92022-02-22 13:58:00 +010084inline std::optional<nlohmann::json::array_t>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020085 getDiscreteTriggers(const TriggerThresholdParamsExt& thresholdParams)
86{
87 const std::vector<DiscreteThresholdParams>* discreteParams =
88 std::get_if<std::vector<DiscreteThresholdParams>>(&thresholdParams);
89
Ed Tanouse662eae2022-01-25 10:39:19 -080090 if (discreteParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020091 {
92 return std::nullopt;
93 }
94
Szymon Dompke3f215c92022-02-22 13:58:00 +010095 nlohmann::json::array_t triggers;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020096 for (const auto& [name, severity, dwellTime, value] : *discreteParams)
97 {
98 std::optional<std::string> duration =
99 time_utils::toDurationStringFromUint(dwellTime);
100
101 if (!duration)
102 {
103 return std::nullopt;
104 }
Ed Tanous613dabe2022-07-09 11:17:36 -0700105 nlohmann::json::object_t trigger;
106 trigger["Name"] = name;
107 trigger["Severity"] = severity;
108 trigger["DwellTime"] = *duration;
109 trigger["Value"] = value;
110 triggers.push_back(std::move(trigger));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200111 }
112
Szymon Dompke3f215c92022-02-22 13:58:00 +0100113 return {std::move(triggers)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200114}
115
116inline std::optional<nlohmann::json>
117 getNumericThresholds(const TriggerThresholdParamsExt& thresholdParams)
118{
119 const std::vector<NumericThresholdParams>* numericParams =
120 std::get_if<std::vector<NumericThresholdParams>>(&thresholdParams);
121
Ed Tanouse662eae2022-01-25 10:39:19 -0800122 if (numericParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200123 {
124 return std::nullopt;
125 }
126
Szymon Dompke3f215c92022-02-22 13:58:00 +0100127 nlohmann::json::object_t thresholds;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200128 for (const auto& [type, dwellTime, activation, reading] : *numericParams)
129 {
130 std::optional<std::string> duration =
131 time_utils::toDurationStringFromUint(dwellTime);
132
133 if (!duration)
134 {
135 return std::nullopt;
136 }
Ed Tanous14766872022-03-15 10:44:42 -0700137 nlohmann::json& threshold = thresholds[type];
138 threshold["Reading"] = reading;
139 threshold["Activation"] = activation;
140 threshold["DwellTime"] = *duration;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200141 }
142
Szymon Dompke3f215c92022-02-22 13:58:00 +0100143 return {std::move(thresholds)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200144}
145
Szymon Dompke3f215c92022-02-22 13:58:00 +0100146inline std::optional<nlohmann::json> getMetricReportDefinitions(
147 const std::vector<sdbusplus::message::object_path>& reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200148{
149 nlohmann::json reports = nlohmann::json::array();
Szymon Dompke3f215c92022-02-22 13:58:00 +0100150
151 for (const sdbusplus::message::object_path& path : reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200152 {
Szymon Dompke3f215c92022-02-22 13:58:00 +0100153 std::string reportId = path.filename();
154 if (reportId.empty())
155 {
156 {
157 BMCWEB_LOG_ERROR << "Property Reports contains invalid value: "
158 << path.str;
159 return std::nullopt;
160 }
161 }
162
Ed Tanous14766872022-03-15 10:44:42 -0700163 nlohmann::json::object_t report;
164 report["@odata.id"] =
165 crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
Szymon Dompke3f215c92022-02-22 13:58:00 +0100166 "MetricReportDefinitions", reportId);
Ed Tanous14766872022-03-15 10:44:42 -0700167 reports.push_back(std::move(report));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200168 }
169
Szymon Dompke3f215c92022-02-22 13:58:00 +0100170 return {std::move(reports)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200171}
172
173inline std::vector<std::string>
174 getMetricProperties(const TriggerSensorsParams& sensors)
175{
176 std::vector<std::string> metricProperties;
177 metricProperties.reserve(sensors.size());
178 for (const auto& [_, metadata] : sensors)
179 {
180 metricProperties.emplace_back(metadata);
181 }
182
183 return metricProperties;
184}
185
186inline bool fillTrigger(
187 nlohmann::json& json, const std::string& id,
188 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>&
189 properties)
190{
191 const std::string* name = nullptr;
192 const bool* discrete = nullptr;
193 const TriggerSensorsParams* sensors = nullptr;
Szymon Dompke3f215c92022-02-22 13:58:00 +0100194 const std::vector<sdbusplus::message::object_path>* reports = nullptr;
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200195 const std::vector<std::string>* triggerActions = nullptr;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200196 const TriggerThresholdParamsExt* thresholds = nullptr;
197
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200198 const bool success = sdbusplus::unpackPropertiesNoThrow(
199 dbus_utils::UnpackErrorPrinter(), properties, "Name", name, "Discrete",
200 discrete, "Sensors", sensors, "Reports", reports, "TriggerActions",
201 triggerActions, "Thresholds", thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200202
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200203 if (!success)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200204 {
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200205 return false;
206 }
207
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200208 if (triggerActions != nullptr)
Szymon Dompke3f215c92022-02-22 13:58:00 +0100209 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200210 std::optional<std::vector<std::string>> redfishTriggerActions =
211 getTriggerActions(*triggerActions);
212 if (!redfishTriggerActions)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200213 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200214 BMCWEB_LOG_ERROR
215 << "Property TriggerActions is invalid in Trigger: " << id;
216 return false;
217 }
218 json["TriggerActions"] = *triggerActions;
219 }
220
221 if (reports != nullptr)
222 {
223 std::optional<nlohmann::json> linkedReports =
224 getMetricReportDefinitions(*reports);
225 if (!linkedReports)
226 {
227 BMCWEB_LOG_ERROR << "Property Reports is invalid in Trigger: "
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200228 << id;
229 return false;
230 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200231 json["Links"]["MetricReportDefinitions"] = *linkedReports;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200232 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200233
234 if (discrete != nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200235 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200236 if (*discrete)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200237 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200238 std::optional<nlohmann::json::array_t> discreteTriggers =
239 getDiscreteTriggers(*thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200240
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200241 if (!discreteTriggers)
242 {
243 BMCWEB_LOG_ERROR
244 << "Property Thresholds is invalid for discrete "
245 "triggers in Trigger: "
246 << id;
247 return false;
248 }
249
250 json["DiscreteTriggers"] = *discreteTriggers;
251 json["DiscreteTriggerCondition"] =
252 discreteTriggers->empty() ? "Changed" : "Specified";
253 json["MetricType"] = "Discrete";
254 }
255 else
256 {
257 std::optional<nlohmann::json> numericThresholds =
258 getNumericThresholds(*thresholds);
259
260 if (!numericThresholds)
261 {
262 BMCWEB_LOG_ERROR
263 << "Property Thresholds is invalid for numeric "
264 "thresholds in Trigger: "
265 << id;
266 return false;
267 }
268
269 json["NumericThresholds"] = *numericThresholds;
270 json["MetricType"] = "Numeric";
271 }
272 }
273
274 if (name != nullptr)
275 {
276 json["Name"] = *name;
277 }
278
279 if (sensors != nullptr)
280 {
281 json["MetricProperties"] = getMetricProperties(*sensors);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200282 }
283
Szymon Dompke3f215c92022-02-22 13:58:00 +0100284 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
285 json["@odata.id"] = crow::utility::urlFromPieces(
286 "redfish", "v1", "TelemetryService", "Triggers", id);
287 json["Id"] = id;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200288
289 return true;
290}
291
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200292} // namespace telemetry
293
294inline void requestRoutesTriggerCollection(App& app)
295{
296 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
297 .privileges(redfish::privileges::getTriggersCollection)
298 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700299 [&app](const crow::Request& req,
300 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000301 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700302 {
303 return;
304 }
305 asyncResp->res.jsonValue["@odata.type"] =
306 "#TriggersCollection.TriggersCollection";
Willy Tuae9031f2022-09-27 05:48:07 +0000307 asyncResp->res.jsonValue["@odata.id"] =
308 "/redfish/v1/TelemetryService/Triggers";
Ed Tanous002d39b2022-05-31 08:59:27 -0700309 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
George Liu7a1dbc42022-12-07 16:03:22 +0800310 constexpr std::array<std::string_view, 1> interfaces{
311 telemetry::triggerInterface};
Ed Tanous002d39b2022-05-31 08:59:27 -0700312 collection_util::getCollectionMembers(
Willy Tuae9031f2022-09-27 05:48:07 +0000313 asyncResp,
314 boost::urls::url("/redfish/v1/TelemetryService/Triggers"),
315 interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -0700316 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
317 });
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200318}
319
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200320inline void requestRoutesTrigger(App& app)
321{
322 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
323 .privileges(redfish::privileges::getTriggers)
324 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700325 [&app](const crow::Request& req,
326 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
327 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000328 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700329 {
330 return;
331 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200332 sdbusplus::asio::getAllProperties(
333 *crow::connections::systemBus, telemetry::service,
334 telemetry::getDbusTriggerPath(id), telemetry::triggerInterface,
Ed Tanous002d39b2022-05-31 08:59:27 -0700335 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800336 id](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700337 const std::vector<std::pair<
338 std::string, telemetry::TriggerGetParamsVariant>>& ret) {
339 if (ec.value() == EBADR ||
340 ec == boost::system::errc::host_unreachable)
341 {
342 messages::resourceNotFound(asyncResp->res, "Triggers", id);
343 return;
344 }
345 if (ec)
346 {
347 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
348 messages::internalError(asyncResp->res);
349 return;
350 }
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200351
Ed Tanous002d39b2022-05-31 08:59:27 -0700352 if (!telemetry::fillTrigger(asyncResp->res.jsonValue, id, ret))
353 {
354 messages::internalError(asyncResp->res);
355 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200356 });
Ed Tanous002d39b2022-05-31 08:59:27 -0700357 });
Szymon Dompke163994a2021-08-12 17:30:23 +0200358
359 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
360 .privileges(redfish::privileges::deleteTriggers)
361 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700362 [&app](const crow::Request& req,
363 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
364 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000365 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700366 {
367 return;
368 }
369 const std::string triggerPath = telemetry::getDbusTriggerPath(id);
Szymon Dompke163994a2021-08-12 17:30:23 +0200370
Ed Tanous002d39b2022-05-31 08:59:27 -0700371 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -0800372 [asyncResp, id](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700373 if (ec.value() == EBADR)
374 {
375 messages::resourceNotFound(asyncResp->res, "Triggers", id);
376 return;
377 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200378
Ed Tanous002d39b2022-05-31 08:59:27 -0700379 if (ec)
380 {
381 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
382 messages::internalError(asyncResp->res);
383 return;
384 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200385
Ed Tanous002d39b2022-05-31 08:59:27 -0700386 asyncResp->res.result(boost::beast::http::status::no_content);
387 },
388 telemetry::service, triggerPath,
389 "xyz.openbmc_project.Object.Delete", "Delete");
390 });
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200391}
392
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200393} // namespace redfish