blob: e2f46ecef2e6dd4c104a333ad4396543a4030a96 [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 }
132
133 thresholds[type] = {{"Reading", reading},
134 {"Activation", activation},
135 {"DwellTime", *duration}};
136 }
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 {
Szymon Dompke456cd872022-03-01 16:34:08 +0100147 reports.push_back(
148 {{"@odata.id",
149 crow::utility::urlFromPieces("redfish", "v1", "TelemetryService",
150 "MetricReportDefinitions", name)
151 .string()}});
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200152 }
153
154 return reports;
155}
156
157inline std::vector<std::string>
158 getMetricProperties(const TriggerSensorsParams& sensors)
159{
160 std::vector<std::string> metricProperties;
161 metricProperties.reserve(sensors.size());
162 for (const auto& [_, metadata] : sensors)
163 {
164 metricProperties.emplace_back(metadata);
165 }
166
167 return metricProperties;
168}
169
170inline bool fillTrigger(
171 nlohmann::json& json, const std::string& id,
172 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>&
173 properties)
174{
175 const std::string* name = nullptr;
176 const bool* discrete = nullptr;
177 const TriggerSensorsParams* sensors = nullptr;
178 const std::vector<std::string>* reports = nullptr;
179 const std::vector<std::string>* actions = nullptr;
180 const TriggerThresholdParamsExt* thresholds = nullptr;
181
182 for (const auto& [key, var] : properties)
183 {
184 if (key == "Name")
185 {
186 name = std::get_if<std::string>(&var);
187 }
188 else if (key == "Discrete")
189 {
190 discrete = std::get_if<bool>(&var);
191 }
192 else if (key == "Sensors")
193 {
194 sensors = std::get_if<TriggerSensorsParams>(&var);
195 }
196 else if (key == "ReportNames")
197 {
198 reports = std::get_if<std::vector<std::string>>(&var);
199 }
200 else if (key == "TriggerActions")
201 {
202 actions = std::get_if<std::vector<std::string>>(&var);
203 }
204 else if (key == "Thresholds")
205 {
206 thresholds = std::get_if<TriggerThresholdParamsExt>(&var);
207 }
208 }
209
Ed Tanouse662eae2022-01-25 10:39:19 -0800210 if (name == nullptr || discrete == nullptr || sensors == nullptr ||
211 reports == nullptr || actions == nullptr || thresholds == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200212 {
213 BMCWEB_LOG_ERROR
214 << "Property type mismatch or property is missing in Trigger: "
215 << id;
216 return false;
217 }
218
219 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
Szymon Dompke456cd872022-03-01 16:34:08 +0100220 json["@odata.id"] = crow::utility::urlFromPieces(
221 "redfish", "v1", "TelemetryService", "Triggers", id)
222 .string();
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200223 json["Id"] = id;
224 json["Name"] = *name;
225
226 if (*discrete)
227 {
228 std::optional<nlohmann::json> discreteTriggers =
229 getDiscreteTriggers(*thresholds);
230
231 if (!discreteTriggers)
232 {
233 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for discrete "
234 "triggers in Trigger: "
235 << id;
236 return false;
237 }
238
239 json["DiscreteTriggers"] = *discreteTriggers;
240 json["DiscreteTriggerCondition"] =
241 discreteTriggers->empty() ? "Changed" : "Specified";
242 json["MetricType"] = "Discrete";
243 }
244 else
245 {
246 std::optional<nlohmann::json> numericThresholds =
247 getNumericThresholds(*thresholds);
248
249 if (!numericThresholds)
250 {
251 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for numeric "
252 "thresholds in Trigger: "
253 << id;
254 return false;
255 }
256
257 json["NumericThresholds"] = *numericThresholds;
258 json["MetricType"] = "Numeric";
259 }
260
261 std::optional<std::vector<std::string>> triggerActions =
262 getTriggerActions(*actions);
263
264 if (!triggerActions)
265 {
266 BMCWEB_LOG_ERROR << "Property TriggerActions is invalid in Trigger: "
267 << id;
268 return false;
269 }
270
271 json["TriggerActions"] = *triggerActions;
272 json["MetricProperties"] = getMetricProperties(*sensors);
273 json["Links"]["MetricReportDefinitions"] =
274 getMetricReportDefinitions(*reports);
275
276 return true;
277}
278
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200279} // namespace telemetry
280
281inline void requestRoutesTriggerCollection(App& app)
282{
283 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
284 .privileges(redfish::privileges::getTriggersCollection)
285 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700286 [&app](const crow::Request& req,
287 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
288 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
289 {
290 return;
291 }
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200292 asyncResp->res.jsonValue["@odata.type"] =
293 "#TriggersCollection.TriggersCollection";
Szymon Dompke456cd872022-03-01 16:34:08 +0100294 asyncResp->res.jsonValue["@odata.id"] = telemetry::triggerUri;
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200295 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
296 const std::vector<const char*> interfaces{
297 telemetry::triggerInterface};
298 collection_util::getCollectionMembers(
299 asyncResp, telemetry::triggerUri, interfaces,
300 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
301 });
302}
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) {
312 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
313 {
314 return;
315 }
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200316 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>>&
321 ret) {
322 if (ec.value() == EBADR ||
323 ec == boost::system::errc::host_unreachable)
324 {
325 messages::resourceNotFound(asyncResp->res,
326 "Triggers", id);
327 return;
328 }
329 if (ec)
330 {
331 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
332 messages::internalError(asyncResp->res);
333 return;
334 }
335
336 if (!telemetry::fillTrigger(asyncResp->res.jsonValue,
337 id, ret))
338 {
339 messages::internalError(asyncResp->res);
340 }
341 },
342 telemetry::service, telemetry::getDbusTriggerPath(id),
343 "org.freedesktop.DBus.Properties", "GetAll",
344 telemetry::triggerInterface);
345 });
Szymon Dompke163994a2021-08-12 17:30:23 +0200346
347 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
348 .privileges(redfish::privileges::deleteTriggers)
349 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700350 [&app](const crow::Request& req,
351 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
352 const std::string& id) {
353 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
354 {
355 return;
356 }
Szymon Dompke163994a2021-08-12 17:30:23 +0200357 const std::string triggerPath =
358 telemetry::getDbusTriggerPath(id);
359
360 crow::connections::systemBus->async_method_call(
361 [asyncResp, id](const boost::system::error_code ec) {
362 if (ec.value() == EBADR)
363 {
364 messages::resourceNotFound(asyncResp->res,
365 "Triggers", id);
366 return;
367 }
368
369 if (ec)
370 {
371 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
372 messages::internalError(asyncResp->res);
373 return;
374 }
375
376 asyncResp->res.result(
377 boost::beast::http::status::no_content);
378 },
379 telemetry::service, triggerPath,
380 "xyz.openbmc_project.Object.Delete", "Delete");
381 });
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200382}
383
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200384} // namespace redfish