blob: 99551052807569b8bbfb48f60eca0bfc22a538b9 [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>
9
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020010#include <tuple>
11#include <variant>
12#include <vector>
13
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020014namespace redfish
15{
16namespace telemetry
17{
18constexpr const char* triggerInterface =
19 "xyz.openbmc_project.Telemetry.Trigger";
20constexpr const char* triggerUri = "/redfish/v1/TelemetryService/Triggers";
21
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020022using NumericThresholdParams =
23 std::tuple<std::string, uint64_t, std::string, double>;
24
25using DiscreteThresholdParams =
26 std::tuple<std::string, std::string, uint64_t, std::string>;
27
28using TriggerThresholdParamsExt =
29 std::variant<std::monostate, std::vector<NumericThresholdParams>,
30 std::vector<DiscreteThresholdParams>>;
31
32using TriggerSensorsParams =
33 std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
34
35using TriggerGetParamsVariant =
36 std::variant<std::monostate, bool, std::string, TriggerThresholdParamsExt,
37 TriggerSensorsParams, std::vector<std::string>>;
38
39inline std::optional<std::string>
40 getRedfishFromDbusAction(const std::string& dbusAction)
41{
42 std::optional<std::string> redfishAction = std::nullopt;
43 if (dbusAction == "UpdateReport")
44 {
45 redfishAction = "RedfishMetricReport";
46 }
47 if (dbusAction == "RedfishEvent")
48 {
49 redfishAction = "RedfishEvent";
50 }
51 if (dbusAction == "LogToLogService")
52 {
53 redfishAction = "LogToLogService";
54 }
55 return redfishAction;
56}
57
58inline std::optional<std::vector<std::string>>
59 getTriggerActions(const std::vector<std::string>& dbusActions)
60{
61 std::vector<std::string> triggerActions;
62 for (const std::string& dbusAction : dbusActions)
63 {
64 std::optional<std::string> redfishAction =
65 getRedfishFromDbusAction(dbusAction);
66
67 if (!redfishAction)
68 {
69 return std::nullopt;
70 }
71
72 triggerActions.push_back(*redfishAction);
73 }
74
75 return std::make_optional(triggerActions);
76}
77
78inline std::optional<nlohmann::json>
79 getDiscreteTriggers(const TriggerThresholdParamsExt& thresholdParams)
80{
81 const std::vector<DiscreteThresholdParams>* discreteParams =
82 std::get_if<std::vector<DiscreteThresholdParams>>(&thresholdParams);
83
Ed Tanouse662eae2022-01-25 10:39:19 -080084 if (discreteParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020085 {
86 return std::nullopt;
87 }
88
89 nlohmann::json triggers = nlohmann::json::array();
90 for (const auto& [name, severity, dwellTime, value] : *discreteParams)
91 {
92 std::optional<std::string> duration =
93 time_utils::toDurationStringFromUint(dwellTime);
94
95 if (!duration)
96 {
97 return std::nullopt;
98 }
99
100 triggers.push_back({
101 {"Name", name},
102 {"Severity", severity},
103 {"DwellTime", *duration},
104 {"Value", value},
105 });
106 }
107
108 return std::make_optional(triggers);
109}
110
111inline std::optional<nlohmann::json>
112 getNumericThresholds(const TriggerThresholdParamsExt& thresholdParams)
113{
114 const std::vector<NumericThresholdParams>* numericParams =
115 std::get_if<std::vector<NumericThresholdParams>>(&thresholdParams);
116
Ed Tanouse662eae2022-01-25 10:39:19 -0800117 if (numericParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200118 {
119 return std::nullopt;
120 }
121
122 nlohmann::json thresholds;
123 for (const auto& [type, dwellTime, activation, reading] : *numericParams)
124 {
125 std::optional<std::string> duration =
126 time_utils::toDurationStringFromUint(dwellTime);
127
128 if (!duration)
129 {
130 return std::nullopt;
131 }
Ed Tanous14766872022-03-15 10:44:42 -0700132 nlohmann::json& threshold = thresholds[type];
133 threshold["Reading"] = reading;
134 threshold["Activation"] = activation;
135 threshold["DwellTime"] = *duration;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200136 }
137
138 return std::make_optional(thresholds);
139}
140
Ed Tanous248d0232022-01-04 20:47:01 +0000141inline nlohmann::json
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200142 getMetricReportDefinitions(const std::vector<std::string>& reportNames)
143{
144 nlohmann::json reports = nlohmann::json::array();
145 for (const std::string& name : reportNames)
146 {
Ed Tanous14766872022-03-15 10:44:42 -0700147 nlohmann::json::object_t report;
148 report["@odata.id"] =
149 crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
150 "MetricReportDefinitions", name)
151 .string();
152 reports.push_back(std::move(report));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200153 }
154
155 return reports;
156}
157
158inline std::vector<std::string>
159 getMetricProperties(const TriggerSensorsParams& sensors)
160{
161 std::vector<std::string> metricProperties;
162 metricProperties.reserve(sensors.size());
163 for (const auto& [_, metadata] : sensors)
164 {
165 metricProperties.emplace_back(metadata);
166 }
167
168 return metricProperties;
169}
170
171inline bool fillTrigger(
172 nlohmann::json& json, const std::string& id,
173 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>&
174 properties)
175{
176 const std::string* name = nullptr;
177 const bool* discrete = nullptr;
178 const TriggerSensorsParams* sensors = nullptr;
179 const std::vector<std::string>* reports = nullptr;
180 const std::vector<std::string>* actions = nullptr;
181 const TriggerThresholdParamsExt* thresholds = nullptr;
182
183 for (const auto& [key, var] : properties)
184 {
185 if (key == "Name")
186 {
187 name = std::get_if<std::string>(&var);
188 }
189 else if (key == "Discrete")
190 {
191 discrete = std::get_if<bool>(&var);
192 }
193 else if (key == "Sensors")
194 {
195 sensors = std::get_if<TriggerSensorsParams>(&var);
196 }
197 else if (key == "ReportNames")
198 {
199 reports = std::get_if<std::vector<std::string>>(&var);
200 }
201 else if (key == "TriggerActions")
202 {
203 actions = std::get_if<std::vector<std::string>>(&var);
204 }
205 else if (key == "Thresholds")
206 {
207 thresholds = std::get_if<TriggerThresholdParamsExt>(&var);
208 }
209 }
210
Ed Tanouse662eae2022-01-25 10:39:19 -0800211 if (name == nullptr || discrete == nullptr || sensors == nullptr ||
212 reports == nullptr || actions == nullptr || thresholds == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200213 {
214 BMCWEB_LOG_ERROR
215 << "Property type mismatch or property is missing in Trigger: "
216 << id;
217 return false;
218 }
219
220 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
Szymon Dompke456cd872022-03-01 16:34:08 +0100221 json["@odata.id"] = crow::utility::urlFromPieces(
222 "redfish", "v1", "TelemetryService", "Triggers", id)
223 .string();
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200224 json["Id"] = id;
225 json["Name"] = *name;
226
227 if (*discrete)
228 {
229 std::optional<nlohmann::json> discreteTriggers =
230 getDiscreteTriggers(*thresholds);
231
232 if (!discreteTriggers)
233 {
234 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for discrete "
235 "triggers in Trigger: "
236 << id;
237 return false;
238 }
239
240 json["DiscreteTriggers"] = *discreteTriggers;
241 json["DiscreteTriggerCondition"] =
242 discreteTriggers->empty() ? "Changed" : "Specified";
243 json["MetricType"] = "Discrete";
244 }
245 else
246 {
247 std::optional<nlohmann::json> numericThresholds =
248 getNumericThresholds(*thresholds);
249
250 if (!numericThresholds)
251 {
252 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for numeric "
253 "thresholds in Trigger: "
254 << id;
255 return false;
256 }
257
258 json["NumericThresholds"] = *numericThresholds;
259 json["MetricType"] = "Numeric";
260 }
261
262 std::optional<std::vector<std::string>> triggerActions =
263 getTriggerActions(*actions);
264
265 if (!triggerActions)
266 {
267 BMCWEB_LOG_ERROR << "Property TriggerActions is invalid in Trigger: "
268 << id;
269 return false;
270 }
271
272 json["TriggerActions"] = *triggerActions;
273 json["MetricProperties"] = getMetricProperties(*sensors);
274 json["Links"]["MetricReportDefinitions"] =
275 getMetricReportDefinitions(*reports);
276
277 return true;
278}
279
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200280} // namespace telemetry
281
282inline void requestRoutesTriggerCollection(App& app)
283{
284 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
285 .privileges(redfish::privileges::getTriggersCollection)
286 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700287 [&app](const crow::Request& req,
288 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000289 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700290 {
291 return;
292 }
293 asyncResp->res.jsonValue["@odata.type"] =
294 "#TriggersCollection.TriggersCollection";
295 asyncResp->res.jsonValue["@odata.id"] = telemetry::triggerUri;
296 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
297 const std::vector<const char*> interfaces{telemetry::triggerInterface};
298 collection_util::getCollectionMembers(
299 asyncResp, telemetry::triggerUri, interfaces,
300 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
301 });
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200302}
303
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200304inline void requestRoutesTrigger(App& app)
305{
306 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
307 .privileges(redfish::privileges::getTriggers)
308 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700309 [&app](const crow::Request& req,
310 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
311 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000312 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700313 {
314 return;
315 }
316 crow::connections::systemBus->async_method_call(
317 [asyncResp,
318 id](const boost::system::error_code ec,
319 const std::vector<std::pair<
320 std::string, telemetry::TriggerGetParamsVariant>>& ret) {
321 if (ec.value() == EBADR ||
322 ec == boost::system::errc::host_unreachable)
323 {
324 messages::resourceNotFound(asyncResp->res, "Triggers", id);
325 return;
326 }
327 if (ec)
328 {
329 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
330 messages::internalError(asyncResp->res);
331 return;
332 }
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200333
Ed Tanous002d39b2022-05-31 08:59:27 -0700334 if (!telemetry::fillTrigger(asyncResp->res.jsonValue, id, ret))
335 {
336 messages::internalError(asyncResp->res);
337 }
338 },
339 telemetry::service, telemetry::getDbusTriggerPath(id),
340 "org.freedesktop.DBus.Properties", "GetAll",
341 telemetry::triggerInterface);
342 });
Szymon Dompke163994a2021-08-12 17:30:23 +0200343
344 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
345 .privileges(redfish::privileges::deleteTriggers)
346 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700347 [&app](const crow::Request& req,
348 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
349 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000350 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700351 {
352 return;
353 }
354 const std::string triggerPath = telemetry::getDbusTriggerPath(id);
Szymon Dompke163994a2021-08-12 17:30:23 +0200355
Ed Tanous002d39b2022-05-31 08:59:27 -0700356 crow::connections::systemBus->async_method_call(
357 [asyncResp, id](const boost::system::error_code ec) {
358 if (ec.value() == EBADR)
359 {
360 messages::resourceNotFound(asyncResp->res, "Triggers", id);
361 return;
362 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200363
Ed Tanous002d39b2022-05-31 08:59:27 -0700364 if (ec)
365 {
366 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
367 messages::internalError(asyncResp->res);
368 return;
369 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200370
Ed Tanous002d39b2022-05-31 08:59:27 -0700371 asyncResp->res.result(boost::beast::http::status::no_content);
372 },
373 telemetry::service, triggerPath,
374 "xyz.openbmc_project.Object.Delete", "Delete");
375 });
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200376}
377
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200378} // namespace redfish