blob: cdd5781b99ce5a3521e98d03684702f66d1fd5bb [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 {
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
Ed Tanouse662eae2022-01-25 10:39:19 -0800207 if (name == nullptr || discrete == nullptr || sensors == nullptr ||
208 reports == nullptr || actions == nullptr || thresholds == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200209 {
210 BMCWEB_LOG_ERROR
211 << "Property type mismatch or property is missing in Trigger: "
212 << id;
213 return false;
214 }
215
216 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
217 json["@odata.id"] = triggerUri + std::string("/") + id;
218 json["Id"] = id;
219 json["Name"] = *name;
220
221 if (*discrete)
222 {
223 std::optional<nlohmann::json> discreteTriggers =
224 getDiscreteTriggers(*thresholds);
225
226 if (!discreteTriggers)
227 {
228 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for discrete "
229 "triggers in Trigger: "
230 << id;
231 return false;
232 }
233
234 json["DiscreteTriggers"] = *discreteTriggers;
235 json["DiscreteTriggerCondition"] =
236 discreteTriggers->empty() ? "Changed" : "Specified";
237 json["MetricType"] = "Discrete";
238 }
239 else
240 {
241 std::optional<nlohmann::json> numericThresholds =
242 getNumericThresholds(*thresholds);
243
244 if (!numericThresholds)
245 {
246 BMCWEB_LOG_ERROR << "Property Thresholds is invalid for numeric "
247 "thresholds in Trigger: "
248 << id;
249 return false;
250 }
251
252 json["NumericThresholds"] = *numericThresholds;
253 json["MetricType"] = "Numeric";
254 }
255
256 std::optional<std::vector<std::string>> triggerActions =
257 getTriggerActions(*actions);
258
259 if (!triggerActions)
260 {
261 BMCWEB_LOG_ERROR << "Property TriggerActions is invalid in Trigger: "
262 << id;
263 return false;
264 }
265
266 json["TriggerActions"] = *triggerActions;
267 json["MetricProperties"] = getMetricProperties(*sensors);
268 json["Links"]["MetricReportDefinitions"] =
269 getMetricReportDefinitions(*reports);
270
271 return true;
272}
273
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200274} // namespace telemetry
275
276inline void requestRoutesTriggerCollection(App& app)
277{
278 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
279 .privileges(redfish::privileges::getTriggersCollection)
280 .methods(boost::beast::http::verb::get)(
281 [](const crow::Request&,
282 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
283 asyncResp->res.jsonValue["@odata.type"] =
284 "#TriggersCollection.TriggersCollection";
285 asyncResp->res.jsonValue["@odata.id"] =
286 "/redfish/v1/TelemetryService/Triggers";
287 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
288 const std::vector<const char*> interfaces{
289 telemetry::triggerInterface};
290 collection_util::getCollectionMembers(
291 asyncResp, telemetry::triggerUri, interfaces,
292 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
293 });
294}
295
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200296inline void requestRoutesTrigger(App& app)
297{
298 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
299 .privileges(redfish::privileges::getTriggers)
300 .methods(boost::beast::http::verb::get)(
301 [](const crow::Request&,
302 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
303 const std::string& id) {
304 crow::connections::systemBus->async_method_call(
305 [asyncResp,
306 id](const boost::system::error_code ec,
307 const std::vector<std::pair<
308 std::string, telemetry::TriggerGetParamsVariant>>&
309 ret) {
310 if (ec.value() == EBADR ||
311 ec == boost::system::errc::host_unreachable)
312 {
313 messages::resourceNotFound(asyncResp->res,
314 "Triggers", id);
315 return;
316 }
317 if (ec)
318 {
319 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
320 messages::internalError(asyncResp->res);
321 return;
322 }
323
324 if (!telemetry::fillTrigger(asyncResp->res.jsonValue,
325 id, ret))
326 {
327 messages::internalError(asyncResp->res);
328 }
329 },
330 telemetry::service, telemetry::getDbusTriggerPath(id),
331 "org.freedesktop.DBus.Properties", "GetAll",
332 telemetry::triggerInterface);
333 });
Szymon Dompke163994a2021-08-12 17:30:23 +0200334
335 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
336 .privileges(redfish::privileges::deleteTriggers)
337 .methods(boost::beast::http::verb::delete_)(
338 [](const crow::Request&,
339 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
340 const std::string& id) {
341 const std::string triggerPath =
342 telemetry::getDbusTriggerPath(id);
343
344 crow::connections::systemBus->async_method_call(
345 [asyncResp, id](const boost::system::error_code ec) {
346 if (ec.value() == EBADR)
347 {
348 messages::resourceNotFound(asyncResp->res,
349 "Triggers", id);
350 return;
351 }
352
353 if (ec)
354 {
355 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
356 messages::internalError(asyncResp->res);
357 return;
358 }
359
360 asyncResp->res.result(
361 boost::beast::http::status::no_content);
362 },
363 telemetry::service, triggerPath,
364 "xyz.openbmc_project.Object.Delete", "Delete");
365 });
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200366}
367
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200368} // namespace redfish