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