blob: bb0a0e2ac36ffd3ea6d27a5bc30933775c0068d5 [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
83 if (!discreteParams)
84 {
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
116 if (!numericParams)
117 {
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 {
146 reports.push_back({
147 {"@odata.id", metricReportDefinitionUri + std::string("/") + name},
148 });
149 }
150
151 return reports;
152}
153
154inline std::vector<std::string>
155 getMetricProperties(const TriggerSensorsParams& sensors)
156{
157 std::vector<std::string> metricProperties;
158 metricProperties.reserve(sensors.size());
159 for (const auto& [_, metadata] : sensors)
160 {
161 metricProperties.emplace_back(metadata);
162 }
163
164 return metricProperties;
165}
166
167inline bool fillTrigger(
168 nlohmann::json& json, const std::string& id,
169 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>&
170 properties)
171{
172 const std::string* name = nullptr;
173 const bool* discrete = nullptr;
174 const TriggerSensorsParams* sensors = nullptr;
175 const std::vector<std::string>* reports = nullptr;
176 const std::vector<std::string>* actions = nullptr;
177 const TriggerThresholdParamsExt* thresholds = nullptr;
178
179 for (const auto& [key, var] : properties)
180 {
181 if (key == "Name")
182 {
183 name = std::get_if<std::string>(&var);
184 }
185 else if (key == "Discrete")
186 {
187 discrete = std::get_if<bool>(&var);
188 }
189 else if (key == "Sensors")
190 {
191 sensors = std::get_if<TriggerSensorsParams>(&var);
192 }
193 else if (key == "ReportNames")
194 {
195 reports = std::get_if<std::vector<std::string>>(&var);
196 }
197 else if (key == "TriggerActions")
198 {
199 actions = std::get_if<std::vector<std::string>>(&var);
200 }
201 else if (key == "Thresholds")
202 {
203 thresholds = std::get_if<TriggerThresholdParamsExt>(&var);
204 }
205 }
206
207 if (!name || !discrete || !sensors || !reports || !actions || !thresholds)
208 {
209 BMCWEB_LOG_ERROR
210 << "Property type mismatch or property is missing in Trigger: "
211 << id;
212 return false;
213 }
214
215 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
216 json["@odata.id"] = triggerUri + std::string("/") + id;
217 json["Id"] = id;
218 json["Name"] = *name;
219
220 if (*discrete)
221 {
222 std::optional<nlohmann::json> discreteTriggers =
223 getDiscreteTriggers(*thresholds);
224
225 if (!discreteTriggers)
226 {
227 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for discrete "
228 "triggers in Trigger: "
229 << id;
230 return false;
231 }
232
233 json["DiscreteTriggers"] = *discreteTriggers;
234 json["DiscreteTriggerCondition"] =
235 discreteTriggers->empty() ? "Changed" : "Specified";
236 json["MetricType"] = "Discrete";
237 }
238 else
239 {
240 std::optional<nlohmann::json> numericThresholds =
241 getNumericThresholds(*thresholds);
242
243 if (!numericThresholds)
244 {
245 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for numeric "
246 "thresholds in Trigger: "
247 << id;
248 return false;
249 }
250
251 json["NumericThresholds"] = *numericThresholds;
252 json["MetricType"] = "Numeric";
253 }
254
255 std::optional<std::vector<std::string>> triggerActions =
256 getTriggerActions(*actions);
257
258 if (!triggerActions)
259 {
260 BMCWEB_LOG_ERROR << "Property TriggerActions is invalid in Trigger: "
261 << id;
262 return false;
263 }
264
265 json["TriggerActions"] = *triggerActions;
266 json["MetricProperties"] = getMetricProperties(*sensors);
267 json["Links"]["MetricReportDefinitions"] =
268 getMetricReportDefinitions(*reports);
269
270 return true;
271}
272
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200273} // namespace telemetry
274
275inline void requestRoutesTriggerCollection(App& app)
276{
277 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
278 .privileges(redfish::privileges::getTriggersCollection)
279 .methods(boost::beast::http::verb::get)(
280 [](const crow::Request&,
281 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
282 asyncResp->res.jsonValue["@odata.type"] =
283 "#TriggersCollection.TriggersCollection";
284 asyncResp->res.jsonValue["@odata.id"] =
285 "/redfish/v1/TelemetryService/Triggers";
286 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
287 const std::vector<const char*> interfaces{
288 telemetry::triggerInterface};
289 collection_util::getCollectionMembers(
290 asyncResp, telemetry::triggerUri, interfaces,
291 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
292 });
293}
294
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200295inline void requestRoutesTrigger(App& app)
296{
297 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
298 .privileges(redfish::privileges::getTriggers)
299 .methods(boost::beast::http::verb::get)(
300 [](const crow::Request&,
301 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
302 const std::string& id) {
303 crow::connections::systemBus->async_method_call(
304 [asyncResp,
305 id](const boost::system::error_code ec,
306 const std::vector<std::pair<
307 std::string, telemetry::TriggerGetParamsVariant>>&
308 ret) {
309 if (ec.value() == EBADR ||
310 ec == boost::system::errc::host_unreachable)
311 {
312 messages::resourceNotFound(asyncResp->res,
313 "Triggers", id);
314 return;
315 }
316 if (ec)
317 {
318 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
319 messages::internalError(asyncResp->res);
320 return;
321 }
322
323 if (!telemetry::fillTrigger(asyncResp->res.jsonValue,
324 id, ret))
325 {
326 messages::internalError(asyncResp->res);
327 }
328 },
329 telemetry::service, telemetry::getDbusTriggerPath(id),
330 "org.freedesktop.DBus.Properties", "GetAll",
331 telemetry::triggerInterface);
332 });
Szymon Dompke163994a2021-08-12 17:30:23 +0200333
334 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
335 .privileges(redfish::privileges::deleteTriggers)
336 .methods(boost::beast::http::verb::delete_)(
337 [](const crow::Request&,
338 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
339 const std::string& id) {
340 const std::string triggerPath =
341 telemetry::getDbusTriggerPath(id);
342
343 crow::connections::systemBus->async_method_call(
344 [asyncResp, id](const boost::system::error_code ec) {
345 if (ec.value() == EBADR)
346 {
347 messages::resourceNotFound(asyncResp->res,
348 "Triggers", id);
349 return;
350 }
351
352 if (ec)
353 {
354 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
355 messages::internalError(asyncResp->res);
356 return;
357 }
358
359 asyncResp->res.result(
360 boost::beast::http::status::no_content);
361 },
362 telemetry::service, triggerPath,
363 "xyz.openbmc_project.Object.Delete", "Delete");
364 });
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200365}
366
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200367} // namespace redfish