blob: a6d2254310ab19cb5ad3ed6da6d188eb841f522e [file] [log] [blame]
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +02001#pragma once
2
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "app.hpp"
Szymon Dompkedd1c4a92022-03-04 13:11:38 +01004#include "generated/enums/resource.hpp"
5#include "generated/enums/triggers.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08006#include "query.hpp"
7#include "registries/privilege_registry.hpp"
Szymon Dompkedd1c4a92022-03-04 13:11:38 +01008#include "sensors.hpp"
9#include "utility.hpp"
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020010#include "utils/collection.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080011#include "utils/dbus_utils.hpp"
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020012#include "utils/telemetry_utils.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080013#include "utils/time_utils.hpp"
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020014
Ed Tanousef4c65b2023-04-24 15:28:50 -070015#include <boost/url/format.hpp>
Krzysztof Grobelny89474492022-09-06 16:30:38 +020016#include <sdbusplus/asio/property.hpp>
17#include <sdbusplus/unpack_properties.hpp>
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020018
George Liu7a1dbc42022-12-07 16:03:22 +080019#include <array>
20#include <string_view>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020021#include <tuple>
22#include <variant>
23#include <vector>
24
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020025namespace redfish
26{
27namespace telemetry
28{
29constexpr const char* triggerInterface =
30 "xyz.openbmc_project.Telemetry.Trigger";
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +020031
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020032using NumericThresholdParams =
33 std::tuple<std::string, uint64_t, std::string, double>;
34
35using DiscreteThresholdParams =
36 std::tuple<std::string, std::string, uint64_t, std::string>;
37
Szymon Dompkedd1c4a92022-03-04 13:11:38 +010038using TriggerThresholdParams =
39 std::variant<std::vector<NumericThresholdParams>,
40 std::vector<DiscreteThresholdParams>>;
41
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020042using TriggerThresholdParamsExt =
43 std::variant<std::monostate, std::vector<NumericThresholdParams>,
44 std::vector<DiscreteThresholdParams>>;
45
46using TriggerSensorsParams =
47 std::vector<std::pair<sdbusplus::message::object_path, std::string>>;
48
49using TriggerGetParamsVariant =
50 std::variant<std::monostate, bool, std::string, TriggerThresholdParamsExt,
Szymon Dompke3f215c92022-02-22 13:58:00 +010051 TriggerSensorsParams, std::vector<std::string>,
52 std::vector<sdbusplus::message::object_path>>;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020053
Szymon Dompkedd1c4a92022-03-04 13:11:38 +010054inline triggers::TriggerActionEnum
55 toRedfishTriggerAction(std::string_view dbusValue)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020056{
Szymon Dompkedd1c4a92022-03-04 13:11:38 +010057 if (dbusValue ==
58 "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.UpdateReport")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020059 {
Szymon Dompkedd1c4a92022-03-04 13:11:38 +010060 return triggers::TriggerActionEnum::RedfishMetricReport;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020061 }
Szymon Dompkedd1c4a92022-03-04 13:11:38 +010062 if (dbusValue ==
63 "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.LogToRedfishEventLog")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020064 {
Szymon Dompkedd1c4a92022-03-04 13:11:38 +010065 return triggers::TriggerActionEnum::RedfishEvent;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020066 }
Szymon Dompkedd1c4a92022-03-04 13:11:38 +010067 if (dbusValue ==
68 "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.LogToJournal")
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020069 {
Szymon Dompkedd1c4a92022-03-04 13:11:38 +010070 return triggers::TriggerActionEnum::LogToLogService;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020071 }
Szymon Dompkedd1c4a92022-03-04 13:11:38 +010072 return triggers::TriggerActionEnum::Invalid;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +020073}
74
Szymon Dompkedd1c4a92022-03-04 13:11:38 +010075inline std::string toDbusTriggerAction(std::string_view redfishValue)
76{
77 if (redfishValue == "RedfishMetricReport")
78 {
79 return "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.UpdateReport";
80 }
81 if (redfishValue == "RedfishEvent")
82 {
83 return "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.LogToRedfishEventLog";
84 }
85 if (redfishValue == "LogToLogService")
86 {
87 return "xyz.openbmc_project.Telemetry.Trigger.TriggerAction.LogToJournal";
88 }
89 return "";
90}
91
92inline std::string toDbusSeverity(std::string_view redfishValue)
93{
94 if (redfishValue == "OK")
95 {
96 return "xyz.openbmc_project.Telemetry.Trigger.Severity.OK";
97 }
98 if (redfishValue == "Warning")
99 {
100 return "xyz.openbmc_project.Telemetry.Trigger.Severity.Warning";
101 }
102 if (redfishValue == "Critical")
103 {
104 return "xyz.openbmc_project.Telemetry.Trigger.Severity.Critical";
105 }
106 return "";
107}
108
109inline resource::Health toRedfishSeverity(std::string_view dbusValue)
110{
111 if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Severity.OK")
112 {
113 return resource::Health::OK;
114 }
115 if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Severity.Warning")
116 {
117 return resource::Health::Warning;
118 }
119 if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Severity.Critical")
120 {
121 return resource::Health::Critical;
122 }
123 return resource::Health::Invalid;
124}
125
126inline std::string toDbusThresholdName(std::string_view redfishValue)
127{
128 if (redfishValue == "UpperCritical")
129 {
130 return "xyz.openbmc_project.Telemetry.Trigger.Type.UpperCritical";
131 }
132
133 if (redfishValue == "LowerCritical")
134 {
135 return "xyz.openbmc_project.Telemetry.Trigger.Type.LowerCritical";
136 }
137
138 if (redfishValue == "UpperWarning")
139 {
140 return "xyz.openbmc_project.Telemetry.Trigger.Type.UpperWarning";
141 }
142
143 if (redfishValue == "LowerWarning")
144 {
145 return "xyz.openbmc_project.Telemetry.Trigger.Type.LowerWarning";
146 }
147
148 return "";
149}
150
151inline std::string toRedfishThresholdName(std::string_view dbusValue)
152{
153 if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Type.UpperCritical")
154 {
155 return "UpperCritical";
156 }
157
158 if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Type.LowerCritical")
159 {
160 return "LowerCritical";
161 }
162
163 if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Type.UpperWarning")
164 {
165 return "UpperWarning";
166 }
167
168 if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Type.LowerWarning")
169 {
170 return "LowerWarning";
171 }
172
173 return "";
174}
175
176inline std::string toDbusActivation(std::string_view redfishValue)
177{
178 if (redfishValue == "Either")
179 {
180 return "xyz.openbmc_project.Telemetry.Trigger.Direction.Either";
181 }
182
183 if (redfishValue == "Decreasing")
184 {
185 return "xyz.openbmc_project.Telemetry.Trigger.Direction.Decreasing";
186 }
187
188 if (redfishValue == "Increasing")
189 {
190 return "xyz.openbmc_project.Telemetry.Trigger.Direction.Increasing";
191 }
192
193 return "";
194}
195
196inline triggers::ThresholdActivation
197 toRedfishActivation(std::string_view dbusValue)
198{
199 if (dbusValue == "xyz.openbmc_project.Telemetry.Trigger.Direction.Either")
200 {
201 return triggers::ThresholdActivation::Either;
202 }
203
204 if (dbusValue ==
205 "xyz.openbmc_project.Telemetry.Trigger.Direction.Decreasing")
206 {
207 return triggers::ThresholdActivation::Decreasing;
208 }
209
210 if (dbusValue ==
211 "xyz.openbmc_project.Telemetry.Trigger.Direction.Increasing")
212 {
213 return triggers::ThresholdActivation::Increasing;
214 }
215
216 return triggers::ThresholdActivation::Invalid;
217}
218
219enum class MetricType
220{
221 Discrete,
222 Numeric
223};
224
225enum class DiscreteCondition
226{
227 Specified,
228 Changed
229};
230
231struct Context
232{
233 std::string id;
234 std::string name;
235 std::vector<std::string> actions;
236 std::vector<std::pair<sdbusplus::message::object_path, std::string>>
237 sensors;
238 std::vector<sdbusplus::message::object_path> reports;
239 TriggerThresholdParams thresholds;
240
241 std::optional<DiscreteCondition> discreteCondition;
242 std::optional<MetricType> metricType;
243 std::optional<std::vector<std::string>> metricProperties;
244};
245
246inline std::optional<sdbusplus::message::object_path>
247 getReportPathFromReportDefinitionUri(const std::string& uri)
248{
Ed Tanous6fd29552023-10-04 09:40:14 -0700249 boost::system::result<boost::urls::url_view> parsed =
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100250 boost::urls::parse_relative_ref(uri);
251
252 if (!parsed)
253 {
254 return std::nullopt;
255 }
256
257 std::string id;
258 if (!crow::utility::readUrlSegments(
259 *parsed, "redfish", "v1", "TelemetryService",
260 "MetricReportDefinitions", std::ref(id)))
261 {
262 return std::nullopt;
263 }
264
265 return sdbusplus::message::object_path(
266 "/xyz/openbmc_project/Telemetry/Reports") /
267 "TelemetryService" / id;
268}
269
270inline std::optional<MetricType> getMetricType(const std::string& metricType)
271{
272 if (metricType == "Discrete")
273 {
274 return MetricType::Discrete;
275 }
276 if (metricType == "Numeric")
277 {
278 return MetricType::Numeric;
279 }
280 return std::nullopt;
281}
282
283inline std::optional<DiscreteCondition>
284 getDiscreteCondition(const std::string& discreteTriggerCondition)
285{
286 if (discreteTriggerCondition == "Specified")
287 {
288 return DiscreteCondition::Specified;
289 }
290 if (discreteTriggerCondition == "Changed")
291 {
292 return DiscreteCondition::Changed;
293 }
294 return std::nullopt;
295}
296
297inline bool parseNumericThresholds(crow::Response& res,
298 nlohmann::json& numericThresholds,
299 Context& ctx)
300{
301 nlohmann::json::object_t* obj =
302 numericThresholds.get_ptr<nlohmann::json::object_t*>();
303 if (obj == nullptr)
304 {
305 messages::propertyValueTypeError(res, numericThresholds.dump(),
306 "NumericThresholds");
307 return false;
308 }
309
310 std::vector<NumericThresholdParams> parsedParams;
311 parsedParams.reserve(numericThresholds.size());
312
313 for (auto& key : *obj)
314 {
315 std::string dbusThresholdName = toDbusThresholdName(key.first);
316 if (dbusThresholdName.empty())
317 {
318 messages::propertyUnknown(res, key.first);
319 return false;
320 }
321
322 double reading = 0.0;
323 std::string activation;
324 std::string dwellTimeStr;
325
326 if (!json_util::readJson(key.second, res, "Reading", reading,
327 "Activation", activation, "DwellTime",
328 dwellTimeStr))
329 {
330 return false;
331 }
332
333 std::string dbusActivation = toDbusActivation(activation);
334
335 if (dbusActivation.empty())
336 {
337 messages::propertyValueIncorrect(res, "Activation", activation);
338 return false;
339 }
340
341 std::optional<std::chrono::milliseconds> dwellTime =
342 time_utils::fromDurationString(dwellTimeStr);
343 if (!dwellTime)
344 {
345 messages::propertyValueIncorrect(res, "DwellTime", dwellTimeStr);
346 return false;
347 }
348
349 parsedParams.emplace_back(dbusThresholdName,
350 static_cast<uint64_t>(dwellTime->count()),
351 dbusActivation, reading);
352 }
353
354 ctx.thresholds = std::move(parsedParams);
355 return true;
356}
357
358inline bool parseDiscreteTriggers(
359 crow::Response& res,
360 std::optional<std::vector<nlohmann::json>>& discreteTriggers, Context& ctx)
361{
362 std::vector<DiscreteThresholdParams> parsedParams;
363 if (!discreteTriggers)
364 {
365 ctx.thresholds = std::move(parsedParams);
366 return true;
367 }
368
369 parsedParams.reserve(discreteTriggers->size());
370 for (nlohmann::json& thresholdInfo : *discreteTriggers)
371 {
372 std::optional<std::string> name = "";
373 std::string value;
374 std::string dwellTimeStr;
375 std::string severity;
376
377 if (!json_util::readJson(thresholdInfo, res, "Name", name, "Value",
378 value, "DwellTime", dwellTimeStr, "Severity",
379 severity))
380 {
381 return false;
382 }
383
384 std::optional<std::chrono::milliseconds> dwellTime =
385 time_utils::fromDurationString(dwellTimeStr);
386 if (!dwellTime)
387 {
388 messages::propertyValueIncorrect(res, "DwellTime", dwellTimeStr);
389 return false;
390 }
391
392 std::string dbusSeverity = toDbusSeverity(severity);
393 if (dbusSeverity.empty())
394 {
395 messages::propertyValueIncorrect(res, "Severity", severity);
396 return false;
397 }
398
399 parsedParams.emplace_back(*name, dbusSeverity,
400 static_cast<uint64_t>(dwellTime->count()),
401 value);
402 }
403
404 ctx.thresholds = std::move(parsedParams);
405 return true;
406}
407
408inline bool parseTriggerThresholds(
409 crow::Response& res,
410 std::optional<std::vector<nlohmann::json>>& discreteTriggers,
411 std::optional<nlohmann::json>& numericThresholds, Context& ctx)
412{
413 if (discreteTriggers && numericThresholds)
414 {
415 messages::propertyValueConflict(res, "DiscreteTriggers",
416 "NumericThresholds");
417 messages::propertyValueConflict(res, "NumericThresholds",
418 "DiscreteTriggers");
419 return false;
420 }
421
422 if (ctx.discreteCondition)
423 {
424 if (numericThresholds)
425 {
426 messages::propertyValueConflict(res, "DiscreteTriggerCondition",
427 "NumericThresholds");
428 messages::propertyValueConflict(res, "NumericThresholds",
429 "DiscreteTriggerCondition");
430 return false;
431 }
432 }
433
434 if (ctx.metricType)
435 {
436 if (*ctx.metricType == MetricType::Discrete && numericThresholds)
437 {
438 messages::propertyValueConflict(res, "NumericThresholds",
439 "MetricType");
440 return false;
441 }
442 if (*ctx.metricType == MetricType::Numeric && discreteTriggers)
443 {
444 messages::propertyValueConflict(res, "DiscreteTriggers",
445 "MetricType");
446 return false;
447 }
448 if (*ctx.metricType == MetricType::Numeric && ctx.discreteCondition)
449 {
450 messages::propertyValueConflict(res, "DiscreteTriggers",
451 "DiscreteTriggerCondition");
452 return false;
453 }
454 }
455
456 if (discreteTriggers || ctx.discreteCondition ||
457 (ctx.metricType && *ctx.metricType == MetricType::Discrete))
458 {
459 if (ctx.discreteCondition)
460 {
461 if (*ctx.discreteCondition == DiscreteCondition::Specified &&
462 !discreteTriggers)
463 {
464 messages::createFailedMissingReqProperties(res,
465 "DiscreteTriggers");
466 return false;
467 }
468 if (discreteTriggers &&
469 ((*ctx.discreteCondition == DiscreteCondition::Specified &&
470 discreteTriggers->empty()) ||
471 (*ctx.discreteCondition == DiscreteCondition::Changed &&
472 !discreteTriggers->empty())))
473 {
474 messages::propertyValueConflict(res, "DiscreteTriggers",
475 "DiscreteTriggerCondition");
476 return false;
477 }
478 }
479 if (!parseDiscreteTriggers(res, discreteTriggers, ctx))
480 {
481 return false;
482 }
483 }
484 else if (numericThresholds)
485 {
486 if (!parseNumericThresholds(res, *numericThresholds, ctx))
487 {
488 return false;
489 }
490 }
491 else
492 {
493 messages::createFailedMissingReqProperties(
494 res, "'DiscreteTriggers', 'NumericThresholds', "
495 "'DiscreteTriggerCondition' or 'MetricType'");
496 return false;
497 }
498 return true;
499}
500
501inline bool parseLinks(crow::Response& res, nlohmann::json& links, Context& ctx)
502{
503 if (links.empty())
504 {
505 return true;
506 }
507
508 std::optional<std::vector<std::string>> metricReportDefinitions;
509 if (!json_util::readJson(links, res, "MetricReportDefinitions",
510 metricReportDefinitions))
511 {
512 return false;
513 }
514
515 if (metricReportDefinitions)
516 {
517 ctx.reports.reserve(metricReportDefinitions->size());
518 for (std::string& reportDefinionUri : *metricReportDefinitions)
519 {
520 std::optional<sdbusplus::message::object_path> reportPath =
521 getReportPathFromReportDefinitionUri(reportDefinionUri);
522 if (!reportPath)
523 {
524 messages::propertyValueIncorrect(res, "MetricReportDefinitions",
525 reportDefinionUri);
526 return false;
527 }
528 ctx.reports.emplace_back(*reportPath);
529 }
530 }
531 return true;
532}
533
534inline bool parseMetricProperties(crow::Response& res, Context& ctx)
535{
536 if (!ctx.metricProperties)
537 {
538 return true;
539 }
540
541 ctx.sensors.reserve(ctx.metricProperties->size());
542
543 size_t uriIdx = 0;
544 for (const std::string& uriStr : *ctx.metricProperties)
545 {
Ed Tanous6fd29552023-10-04 09:40:14 -0700546 boost::system::result<boost::urls::url_view> uri =
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100547 boost::urls::parse_relative_ref(uriStr);
548 if (!uri)
549 {
550 messages::propertyValueIncorrect(
551 res, "MetricProperties/" + std::to_string(uriIdx), uriStr);
552 return false;
553 }
554 std::string chassisName;
555 std::string sensorName;
556 if (!crow::utility::readUrlSegments(*uri, "redfish", "v1", "Chassis",
557 std::ref(chassisName), "Sensors",
558 std::ref(sensorName)))
559 {
560 messages::propertyValueIncorrect(
561 res, "MetricProperties/" + std::to_string(uriIdx), uriStr);
562 return false;
563 }
564
565 std::pair<std::string, std::string> split =
566 splitSensorNameAndType(sensorName);
567 if (split.first.empty() || split.second.empty())
568 {
569 messages::propertyValueIncorrect(
570 res, "MetricProperties/" + std::to_string(uriIdx), uriStr);
571 return false;
572 }
573
574 std::string sensorPath = "/xyz/openbmc_project/sensors/" + split.first +
575 '/' + split.second;
576
577 ctx.sensors.emplace_back(sensorPath, uriStr);
578 uriIdx++;
579 }
580 return true;
581}
582
583inline bool parsePostTriggerParams(crow::Response& res,
584 const crow::Request& req, Context& ctx)
585{
586 std::optional<std::string> id = "";
587 std::optional<std::string> name = "";
588 std::optional<std::string> metricType;
589 std::optional<std::vector<std::string>> triggerActions;
590 std::optional<std::string> discreteTriggerCondition;
591 std::optional<std::vector<nlohmann::json>> discreteTriggers;
592 std::optional<nlohmann::json> numericThresholds;
593 std::optional<nlohmann::json> links;
594 if (!json_util::readJsonPatch(
595 req, res, "Id", id, "Name", name, "MetricType", metricType,
596 "TriggerActions", triggerActions, "DiscreteTriggerCondition",
597 discreteTriggerCondition, "DiscreteTriggers", discreteTriggers,
598 "NumericThresholds", numericThresholds, "MetricProperties",
599 ctx.metricProperties, "Links", links))
600 {
601 return false;
602 }
603
604 ctx.id = *id;
605 ctx.name = *name;
606
607 if (metricType)
608 {
Ed Tanousd5736ef2023-07-06 10:37:23 -0700609 ctx.metricType = getMetricType(*metricType);
610 if (!ctx.metricType)
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100611 {
612 messages::propertyValueIncorrect(res, "MetricType", *metricType);
613 return false;
614 }
615 }
616
617 if (discreteTriggerCondition)
618 {
Ed Tanousd5736ef2023-07-06 10:37:23 -0700619 ctx.discreteCondition = getDiscreteCondition(*discreteTriggerCondition);
620 if (!ctx.discreteCondition)
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100621 {
622 messages::propertyValueIncorrect(res, "DiscreteTriggerCondition",
623 *discreteTriggerCondition);
624 return false;
625 }
626 }
627
628 if (triggerActions)
629 {
630 ctx.actions.reserve(triggerActions->size());
631 for (const std::string& action : *triggerActions)
632 {
633 std::string dbusAction = toDbusTriggerAction(action);
634
635 if (dbusAction.empty())
636 {
637 messages::propertyValueNotInList(res, action, "TriggerActions");
638 return false;
639 }
640
641 ctx.actions.emplace_back(dbusAction);
642 }
643 }
644 if (!parseMetricProperties(res, ctx))
645 {
646 return false;
647 }
648
649 if (!parseTriggerThresholds(res, discreteTriggers, numericThresholds, ctx))
650 {
651 return false;
652 }
653
654 if (links)
655 {
656 if (!parseLinks(res, *links, ctx))
657 {
658 return false;
659 }
660 }
661 return true;
662}
663
664inline void afterCreateTrigger(
665 const boost::system::error_code& ec, const std::string& dbusPath,
666 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
667{
668 if (ec == boost::system::errc::file_exists)
669 {
670 messages::resourceAlreadyExists(asyncResp->res, "Trigger", "Id", id);
671 return;
672 }
673 if (ec == boost::system::errc::too_many_files_open)
674 {
675 messages::createLimitReachedForResource(asyncResp->res);
676 return;
677 }
678 if (ec)
679 {
680 messages::internalError(asyncResp->res);
Ed Tanous62598e32023-07-17 17:06:25 -0700681 BMCWEB_LOG_ERROR("respHandler DBus error {}", ec);
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100682 return;
683 }
684
685 const std::optional<std::string>& triggerId =
686 getTriggerIdFromDbusPath(dbusPath);
687 if (!triggerId)
688 {
689 messages::internalError(asyncResp->res);
Ed Tanous62598e32023-07-17 17:06:25 -0700690 BMCWEB_LOG_ERROR("Unknown data returned by "
691 "AddTrigger DBus method");
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100692 return;
693 }
694
695 messages::created(asyncResp->res);
696 boost::urls::url locationUrl = boost::urls::format(
697 "/redfish/v1/TelemetryService/Triggers/{}", *triggerId);
698 asyncResp->res.addHeader("Location", locationUrl.buffer());
699}
700
701inline std::optional<nlohmann::json::array_t>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200702 getTriggerActions(const std::vector<std::string>& dbusActions)
703{
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100704 nlohmann::json::array_t triggerActions;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200705 for (const std::string& dbusAction : dbusActions)
706 {
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100707 triggers::TriggerActionEnum redfishAction =
708 toRedfishTriggerAction(dbusAction);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200709
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100710 if (redfishAction == triggers::TriggerActionEnum::Invalid)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200711 {
712 return std::nullopt;
713 }
714
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100715 triggerActions.emplace_back(redfishAction);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200716 }
717
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100718 return triggerActions;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200719}
720
Szymon Dompke3f215c92022-02-22 13:58:00 +0100721inline std::optional<nlohmann::json::array_t>
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200722 getDiscreteTriggers(const TriggerThresholdParamsExt& thresholdParams)
723{
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100724 nlohmann::json::array_t triggers;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200725 const std::vector<DiscreteThresholdParams>* discreteParams =
726 std::get_if<std::vector<DiscreteThresholdParams>>(&thresholdParams);
727
Ed Tanouse662eae2022-01-25 10:39:19 -0800728 if (discreteParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200729 {
730 return std::nullopt;
731 }
732
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200733 for (const auto& [name, severity, dwellTime, value] : *discreteParams)
734 {
735 std::optional<std::string> duration =
736 time_utils::toDurationStringFromUint(dwellTime);
737
738 if (!duration)
739 {
740 return std::nullopt;
741 }
Ed Tanous613dabe2022-07-09 11:17:36 -0700742 nlohmann::json::object_t trigger;
743 trigger["Name"] = name;
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100744 trigger["Severity"] = toRedfishSeverity(severity);
Ed Tanous613dabe2022-07-09 11:17:36 -0700745 trigger["DwellTime"] = *duration;
746 trigger["Value"] = value;
Patrick Williamsad539542023-05-12 10:10:08 -0500747 triggers.emplace_back(std::move(trigger));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200748 }
749
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100750 return triggers;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200751}
752
753inline std::optional<nlohmann::json>
754 getNumericThresholds(const TriggerThresholdParamsExt& thresholdParams)
755{
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100756 nlohmann::json::object_t thresholds;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200757 const std::vector<NumericThresholdParams>* numericParams =
758 std::get_if<std::vector<NumericThresholdParams>>(&thresholdParams);
759
Ed Tanouse662eae2022-01-25 10:39:19 -0800760 if (numericParams == nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200761 {
762 return std::nullopt;
763 }
764
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200765 for (const auto& [type, dwellTime, activation, reading] : *numericParams)
766 {
767 std::optional<std::string> duration =
768 time_utils::toDurationStringFromUint(dwellTime);
769
770 if (!duration)
771 {
772 return std::nullopt;
773 }
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100774 nlohmann::json& threshold = thresholds[toRedfishThresholdName(type)];
Ed Tanous14766872022-03-15 10:44:42 -0700775 threshold["Reading"] = reading;
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100776 threshold["Activation"] = toRedfishActivation(activation);
Ed Tanous14766872022-03-15 10:44:42 -0700777 threshold["DwellTime"] = *duration;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200778 }
779
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100780 return thresholds;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200781}
782
Szymon Dompke3f215c92022-02-22 13:58:00 +0100783inline std::optional<nlohmann::json> getMetricReportDefinitions(
784 const std::vector<sdbusplus::message::object_path>& reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200785{
786 nlohmann::json reports = nlohmann::json::array();
Szymon Dompke3f215c92022-02-22 13:58:00 +0100787
788 for (const sdbusplus::message::object_path& path : reportPaths)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200789 {
Szymon Dompke3f215c92022-02-22 13:58:00 +0100790 std::string reportId = path.filename();
791 if (reportId.empty())
792 {
793 {
Ed Tanous62598e32023-07-17 17:06:25 -0700794 BMCWEB_LOG_ERROR("Property Reports contains invalid value: {}",
795 path.str);
Szymon Dompke3f215c92022-02-22 13:58:00 +0100796 return std::nullopt;
797 }
798 }
799
Ed Tanous14766872022-03-15 10:44:42 -0700800 nlohmann::json::object_t report;
Ed Tanousef4c65b2023-04-24 15:28:50 -0700801 report["@odata.id"] = boost::urls::format(
802 "/redfish/v1/TelemetryService/MetricReportDefinitions/{}",
803 reportId);
Patrick Williamsb2ba3072023-05-12 10:27:39 -0500804 reports.emplace_back(std::move(report));
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200805 }
806
Szymon Dompke3f215c92022-02-22 13:58:00 +0100807 return {std::move(reports)};
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200808}
809
810inline std::vector<std::string>
811 getMetricProperties(const TriggerSensorsParams& sensors)
812{
813 std::vector<std::string> metricProperties;
814 metricProperties.reserve(sensors.size());
815 for (const auto& [_, metadata] : sensors)
816 {
817 metricProperties.emplace_back(metadata);
818 }
819
820 return metricProperties;
821}
822
823inline bool fillTrigger(
824 nlohmann::json& json, const std::string& id,
825 const std::vector<std::pair<std::string, TriggerGetParamsVariant>>&
826 properties)
827{
828 const std::string* name = nullptr;
829 const bool* discrete = nullptr;
830 const TriggerSensorsParams* sensors = nullptr;
Szymon Dompke3f215c92022-02-22 13:58:00 +0100831 const std::vector<sdbusplus::message::object_path>* reports = nullptr;
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200832 const std::vector<std::string>* triggerActions = nullptr;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200833 const TriggerThresholdParamsExt* thresholds = nullptr;
834
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200835 const bool success = sdbusplus::unpackPropertiesNoThrow(
836 dbus_utils::UnpackErrorPrinter(), properties, "Name", name, "Discrete",
837 discrete, "Sensors", sensors, "Reports", reports, "TriggerActions",
838 triggerActions, "Thresholds", thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200839
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200840 if (!success)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200841 {
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200842 return false;
843 }
844
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200845 if (triggerActions != nullptr)
Szymon Dompke3f215c92022-02-22 13:58:00 +0100846 {
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100847 std::optional<nlohmann::json::array_t> redfishTriggerActions =
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200848 getTriggerActions(*triggerActions);
849 if (!redfishTriggerActions)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200850 {
Ed Tanous62598e32023-07-17 17:06:25 -0700851 BMCWEB_LOG_ERROR(
852 "Property TriggerActions is invalid in Trigger: {}", id);
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200853 return false;
854 }
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100855 json["TriggerActions"] = *redfishTriggerActions;
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200856 }
857
858 if (reports != nullptr)
859 {
860 std::optional<nlohmann::json> linkedReports =
861 getMetricReportDefinitions(*reports);
862 if (!linkedReports)
863 {
Ed Tanous62598e32023-07-17 17:06:25 -0700864 BMCWEB_LOG_ERROR("Property Reports is invalid in Trigger: {}", id);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200865 return false;
866 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200867 json["Links"]["MetricReportDefinitions"] = *linkedReports;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200868 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200869
870 if (discrete != nullptr)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200871 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200872 if (*discrete)
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200873 {
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200874 std::optional<nlohmann::json::array_t> discreteTriggers =
875 getDiscreteTriggers(*thresholds);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200876
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200877 if (!discreteTriggers)
878 {
Ed Tanous62598e32023-07-17 17:06:25 -0700879 BMCWEB_LOG_ERROR("Property Thresholds is invalid for discrete "
880 "triggers in Trigger: {}",
881 id);
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200882 return false;
883 }
884
885 json["DiscreteTriggers"] = *discreteTriggers;
886 json["DiscreteTriggerCondition"] =
887 discreteTriggers->empty() ? "Changed" : "Specified";
888 json["MetricType"] = "Discrete";
889 }
890 else
891 {
892 std::optional<nlohmann::json> numericThresholds =
893 getNumericThresholds(*thresholds);
894
895 if (!numericThresholds)
896 {
Ed Tanous62598e32023-07-17 17:06:25 -0700897 BMCWEB_LOG_ERROR("Property Thresholds is invalid for numeric "
898 "thresholds in Trigger: {}",
899 id);
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200900 return false;
901 }
902
903 json["NumericThresholds"] = *numericThresholds;
904 json["MetricType"] = "Numeric";
905 }
906 }
907
908 if (name != nullptr)
909 {
910 json["Name"] = *name;
911 }
912
913 if (sensors != nullptr)
914 {
915 json["MetricProperties"] = getMetricProperties(*sensors);
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200916 }
917
Szymon Dompke3f215c92022-02-22 13:58:00 +0100918 json["@odata.type"] = "#Triggers.v1_2_0.Triggers";
Ed Tanousef4c65b2023-04-24 15:28:50 -0700919 json["@odata.id"] =
920 boost::urls::format("/redfish/v1/TelemetryService/Triggers/{}", id);
Szymon Dompke3f215c92022-02-22 13:58:00 +0100921 json["Id"] = id;
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200922
923 return true;
924}
925
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100926inline void handleTriggerCollectionPost(
927 App& app, const crow::Request& req,
928 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
929{
930 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
931 {
932 return;
933 }
934
935 telemetry::Context ctx;
936 if (!telemetry::parsePostTriggerParams(asyncResp->res, req, ctx))
937 {
938 return;
939 }
940
941 crow::connections::systemBus->async_method_call(
942 [asyncResp, id = ctx.id](const boost::system::error_code& ec,
943 const std::string& dbusPath) {
944 afterCreateTrigger(ec, dbusPath, asyncResp, id);
Patrick Williams5a39f772023-10-20 11:20:21 -0500945 },
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100946 service, "/xyz/openbmc_project/Telemetry/Triggers",
947 "xyz.openbmc_project.Telemetry.TriggerManager", "AddTrigger",
948 "TelemetryService/" + ctx.id, ctx.name, ctx.actions, ctx.sensors,
949 ctx.reports, ctx.thresholds);
950}
951
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200952} // namespace telemetry
953
954inline void requestRoutesTriggerCollection(App& app)
955{
956 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
957 .privileges(redfish::privileges::getTriggersCollection)
958 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700959 [&app](const crow::Request& req,
960 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000961 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700962 {
963 return;
964 }
965 asyncResp->res.jsonValue["@odata.type"] =
966 "#TriggersCollection.TriggersCollection";
Willy Tuae9031f2022-09-27 05:48:07 +0000967 asyncResp->res.jsonValue["@odata.id"] =
968 "/redfish/v1/TelemetryService/Triggers";
Ed Tanous002d39b2022-05-31 08:59:27 -0700969 asyncResp->res.jsonValue["Name"] = "Triggers Collection";
George Liu7a1dbc42022-12-07 16:03:22 +0800970 constexpr std::array<std::string_view, 1> interfaces{
971 telemetry::triggerInterface};
Ed Tanous002d39b2022-05-31 08:59:27 -0700972 collection_util::getCollectionMembers(
Willy Tuae9031f2022-09-27 05:48:07 +0000973 asyncResp,
974 boost::urls::url("/redfish/v1/TelemetryService/Triggers"),
975 interfaces,
Ed Tanous002d39b2022-05-31 08:59:27 -0700976 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService");
Patrick Williams5a39f772023-10-20 11:20:21 -0500977 });
Szymon Dompkedd1c4a92022-03-04 13:11:38 +0100978
979 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/")
980 .privileges(redfish::privileges::postTriggersCollection)
981 .methods(boost::beast::http::verb::post)(std::bind_front(
982 telemetry::handleTriggerCollectionPost, std::ref(app)));
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +0200983}
984
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +0200985inline void requestRoutesTrigger(App& app)
986{
987 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
988 .privileges(redfish::privileges::getTriggers)
989 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700990 [&app](const crow::Request& req,
991 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
992 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000993 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700994 {
995 return;
996 }
Krzysztof Grobelny89474492022-09-06 16:30:38 +0200997 sdbusplus::asio::getAllProperties(
998 *crow::connections::systemBus, telemetry::service,
999 telemetry::getDbusTriggerPath(id), telemetry::triggerInterface,
Ed Tanous002d39b2022-05-31 08:59:27 -07001000 [asyncResp,
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001001 id](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -07001002 const std::vector<std::pair<
1003 std::string, telemetry::TriggerGetParamsVariant>>& ret) {
1004 if (ec.value() == EBADR ||
1005 ec == boost::system::errc::host_unreachable)
1006 {
1007 messages::resourceNotFound(asyncResp->res, "Triggers", id);
1008 return;
1009 }
1010 if (ec)
1011 {
Ed Tanous62598e32023-07-17 17:06:25 -07001012 BMCWEB_LOG_ERROR("respHandler DBus error {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -07001013 messages::internalError(asyncResp->res);
1014 return;
1015 }
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +02001016
Ed Tanous002d39b2022-05-31 08:59:27 -07001017 if (!telemetry::fillTrigger(asyncResp->res.jsonValue, id, ret))
1018 {
1019 messages::internalError(asyncResp->res);
1020 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001021 });
Patrick Williams5a39f772023-10-20 11:20:21 -05001022 });
Szymon Dompke163994a2021-08-12 17:30:23 +02001023
1024 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/Triggers/<str>/")
1025 .privileges(redfish::privileges::deleteTriggers)
1026 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001027 [&app](const crow::Request& req,
1028 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1029 const std::string& id) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001030 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001031 {
1032 return;
1033 }
1034 const std::string triggerPath = telemetry::getDbusTriggerPath(id);
Szymon Dompke163994a2021-08-12 17:30:23 +02001035
Ed Tanous002d39b2022-05-31 08:59:27 -07001036 crow::connections::systemBus->async_method_call(
Ed Tanous5e7e2dc2023-02-16 10:37:01 -08001037 [asyncResp, id](const boost::system::error_code& ec) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001038 if (ec.value() == EBADR)
1039 {
1040 messages::resourceNotFound(asyncResp->res, "Triggers", id);
1041 return;
1042 }
Szymon Dompke163994a2021-08-12 17:30:23 +02001043
Ed Tanous002d39b2022-05-31 08:59:27 -07001044 if (ec)
1045 {
Ed Tanous62598e32023-07-17 17:06:25 -07001046 BMCWEB_LOG_ERROR("respHandler DBus error {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -07001047 messages::internalError(asyncResp->res);
1048 return;
1049 }
Szymon Dompke163994a2021-08-12 17:30:23 +02001050
Ed Tanous002d39b2022-05-31 08:59:27 -07001051 asyncResp->res.result(boost::beast::http::status::no_content);
Patrick Williams5a39f772023-10-20 11:20:21 -05001052 },
Ed Tanous002d39b2022-05-31 08:59:27 -07001053 telemetry::service, triggerPath,
1054 "xyz.openbmc_project.Object.Delete", "Delete");
Patrick Williams5a39f772023-10-20 11:20:21 -05001055 });
Lukasz Kazmierczak1b7e6962021-08-02 13:40:27 +02001056}
1057
Lukasz Kazmierczak07148cf2021-08-02 11:08:53 +02001058} // namespace redfish