xref: /openbmc/bmcweb/redfish-core/lib/metric_report_definition.hpp (revision faf100f963c9cd8c81c277ad6bc188eecd0fc12b)
1 #pragma once
2 
3 #include "app.hpp"
4 #include "dbus_utility.hpp"
5 #include "generated/enums/metric_report_definition.hpp"
6 #include "query.hpp"
7 #include "registries/privilege_registry.hpp"
8 #include "sensors.hpp"
9 #include "utils/collection.hpp"
10 #include "utils/dbus_utils.hpp"
11 #include "utils/telemetry_utils.hpp"
12 #include "utils/time_utils.hpp"
13 
14 #include <boost/container/flat_map.hpp>
15 #include <boost/url/format.hpp>
16 #include <sdbusplus/asio/property.hpp>
17 #include <sdbusplus/unpack_properties.hpp>
18 
19 #include <array>
20 #include <map>
21 #include <optional>
22 #include <span>
23 #include <string>
24 #include <string_view>
25 #include <tuple>
26 #include <utility>
27 #include <variant>
28 #include <vector>
29 
30 namespace redfish
31 {
32 
33 namespace telemetry
34 {
35 
36 using ReadingParameters = std::vector<std::tuple<
37     std::vector<std::tuple<sdbusplus::message::object_path, std::string>>,
38     std::string, std::string, uint64_t>>;
39 
40 inline metric_report_definition::ReportActionsEnum
41     toRedfishReportAction(std::string_view dbusValue)
42 {
43     if (dbusValue ==
44         "xyz.openbmc_project.Telemetry.Report.ReportActions.EmitsReadingsUpdate")
45     {
46         return metric_report_definition::ReportActionsEnum::RedfishEvent;
47     }
48     if (dbusValue ==
49         "xyz.openbmc_project.Telemetry.Report.ReportActions.LogToMetricReportsCollection")
50     {
51         return metric_report_definition::ReportActionsEnum::
52             LogToMetricReportsCollection;
53     }
54     return metric_report_definition::ReportActionsEnum::Invalid;
55 }
56 
57 inline std::string toDbusReportAction(std::string_view redfishValue)
58 {
59     if (redfishValue == "RedfishEvent")
60     {
61         return "xyz.openbmc_project.Telemetry.Report.ReportActions.EmitsReadingsUpdate";
62     }
63     if (redfishValue == "LogToMetricReportsCollection")
64     {
65         return "xyz.openbmc_project.Telemetry.Report.ReportActions.LogToMetricReportsCollection";
66     }
67     return "";
68 }
69 
70 inline metric_report_definition::MetricReportDefinitionType
71     toRedfishReportingType(std::string_view dbusValue)
72 {
73     if (dbusValue ==
74         "xyz.openbmc_project.Telemetry.Report.ReportingType.OnChange")
75     {
76         return metric_report_definition::MetricReportDefinitionType::OnChange;
77     }
78     if (dbusValue ==
79         "xyz.openbmc_project.Telemetry.Report.ReportingType.OnRequest")
80     {
81         return metric_report_definition::MetricReportDefinitionType::OnRequest;
82     }
83     if (dbusValue ==
84         "xyz.openbmc_project.Telemetry.Report.ReportingType.Periodic")
85     {
86         return metric_report_definition::MetricReportDefinitionType::Periodic;
87     }
88     return metric_report_definition::MetricReportDefinitionType::Invalid;
89 }
90 
91 inline std::string toDbusReportingType(std::string_view redfishValue)
92 {
93     if (redfishValue == "OnChange")
94     {
95         return "xyz.openbmc_project.Telemetry.Report.ReportingType.OnChange";
96     }
97     if (redfishValue == "OnRequest")
98     {
99         return "xyz.openbmc_project.Telemetry.Report.ReportingType.OnRequest";
100     }
101     if (redfishValue == "Periodic")
102     {
103         return "xyz.openbmc_project.Telemetry.Report.ReportingType.Periodic";
104     }
105     return "";
106 }
107 
108 inline metric_report_definition::CollectionTimeScope
109     toRedfishCollectionTimeScope(std::string_view dbusValue)
110 {
111     if (dbusValue ==
112         "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Point")
113     {
114         return metric_report_definition::CollectionTimeScope::Point;
115     }
116     if (dbusValue ==
117         "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Interval")
118     {
119         return metric_report_definition::CollectionTimeScope::Interval;
120     }
121     if (dbusValue ==
122         "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.StartupInterval")
123     {
124         return metric_report_definition::CollectionTimeScope::StartupInterval;
125     }
126     return metric_report_definition::CollectionTimeScope::Invalid;
127 }
128 
129 inline std::string toDbusCollectionTimeScope(std::string_view redfishValue)
130 {
131     if (redfishValue == "Point")
132     {
133         return "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Point";
134     }
135     if (redfishValue == "Interval")
136     {
137         return "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Interval";
138     }
139     if (redfishValue == "StartupInterval")
140     {
141         return "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.StartupInterval";
142     }
143     return "";
144 }
145 
146 inline metric_report_definition::ReportUpdatesEnum
147     toRedfishReportUpdates(std::string_view dbusValue)
148 {
149     if (dbusValue ==
150         "xyz.openbmc_project.Telemetry.Report.ReportUpdates.Overwrite")
151     {
152         return metric_report_definition::ReportUpdatesEnum::Overwrite;
153     }
154     if (dbusValue ==
155         "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendWrapsWhenFull")
156     {
157         return metric_report_definition::ReportUpdatesEnum::AppendWrapsWhenFull;
158     }
159     if (dbusValue ==
160         "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendStopsWhenFull")
161     {
162         return metric_report_definition::ReportUpdatesEnum::AppendStopsWhenFull;
163     }
164     return metric_report_definition::ReportUpdatesEnum::Invalid;
165 }
166 
167 inline std::string toDbusReportUpdates(std::string_view redfishValue)
168 {
169     if (redfishValue == "Overwrite")
170     {
171         return "xyz.openbmc_project.Telemetry.Report.ReportUpdates.Overwrite";
172     }
173     if (redfishValue == "AppendWrapsWhenFull")
174     {
175         return "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendWrapsWhenFull";
176     }
177     if (redfishValue == "AppendStopsWhenFull")
178     {
179         return "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendStopsWhenFull";
180     }
181     return "";
182 }
183 
184 inline std::optional<nlohmann::json::array_t> getLinkedTriggers(
185     std::span<const sdbusplus::message::object_path> triggerPaths)
186 {
187     nlohmann::json::array_t triggers;
188 
189     for (const sdbusplus::message::object_path& path : triggerPaths)
190     {
191         if (path.parent_path() !=
192             "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService")
193         {
194             BMCWEB_LOG_ERROR << "Property Triggers contains invalid value: "
195                              << path.str;
196             return std::nullopt;
197         }
198 
199         std::string id = path.filename();
200         if (id.empty())
201         {
202             BMCWEB_LOG_ERROR << "Property Triggers contains invalid value: "
203                              << path.str;
204             return std::nullopt;
205         }
206         nlohmann::json::object_t trigger;
207         trigger["@odata.id"] =
208             boost::urls::format("/redfish/v1/TelemetryService/Triggers/{}", id);
209         triggers.emplace_back(std::move(trigger));
210     }
211 
212     return triggers;
213 }
214 
215 inline void
216     fillReportDefinition(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
217                          const std::string& id,
218                          const dbus::utility::DBusPropertiesMap& properties)
219 {
220     std::vector<std::string> reportActions;
221     ReadingParameters readingParams;
222     std::string reportingType;
223     std::string reportUpdates;
224     std::string name;
225     uint64_t appendLimit = 0;
226     uint64_t interval = 0;
227     bool enabled = false;
228     std::vector<sdbusplus::message::object_path> triggers;
229 
230     const bool success = sdbusplus::unpackPropertiesNoThrow(
231         dbus_utils::UnpackErrorPrinter(), properties, "ReportingType",
232         reportingType, "Interval", interval, "ReportActions", reportActions,
233         "ReportUpdates", reportUpdates, "AppendLimit", appendLimit,
234         "ReadingParameters", readingParams, "Name", name, "Enabled", enabled,
235         "Triggers", triggers);
236 
237     if (!success)
238     {
239         messages::internalError(asyncResp->res);
240         return;
241     }
242 
243     metric_report_definition::MetricReportDefinitionType redfishReportingType =
244         toRedfishReportingType(reportingType);
245     if (redfishReportingType ==
246         metric_report_definition::MetricReportDefinitionType::Invalid)
247     {
248         messages::internalError(asyncResp->res);
249         return;
250     }
251 
252     asyncResp->res.jsonValue["MetricReportDefinitionType"] =
253         redfishReportingType;
254 
255     std::optional<nlohmann::json::array_t> linkedTriggers =
256         getLinkedTriggers(triggers);
257     if (!linkedTriggers)
258     {
259         messages::internalError(asyncResp->res);
260         return;
261     }
262 
263     asyncResp->res.jsonValue["Links"]["Triggers"] = std::move(*linkedTriggers);
264 
265     nlohmann::json::array_t redfishReportActions;
266     for (const std::string& action : reportActions)
267     {
268         metric_report_definition::ReportActionsEnum redfishAction =
269             toRedfishReportAction(action);
270         if (redfishAction ==
271             metric_report_definition::ReportActionsEnum::Invalid)
272         {
273             messages::internalError(asyncResp->res);
274             return;
275         }
276 
277         redfishReportActions.emplace_back(redfishAction);
278     }
279 
280     asyncResp->res.jsonValue["ReportActions"] = std::move(redfishReportActions);
281 
282     nlohmann::json::array_t metrics = nlohmann::json::array();
283     for (const auto& [sensorData, collectionFunction, collectionTimeScope,
284                       collectionDuration] : readingParams)
285     {
286         nlohmann::json::array_t metricProperties;
287 
288         for (const auto& [sensorPath, sensorMetadata] : sensorData)
289         {
290             metricProperties.emplace_back(sensorMetadata);
291         }
292 
293         nlohmann::json::object_t metric;
294 
295         metric_report_definition::CalculationAlgorithmEnum
296             redfishCollectionFunction =
297                 telemetry::toRedfishCollectionFunction(collectionFunction);
298         if (redfishCollectionFunction ==
299             metric_report_definition::CalculationAlgorithmEnum::Invalid)
300         {
301             messages::internalError(asyncResp->res);
302             return;
303         }
304         metric["CollectionFunction"] = redfishCollectionFunction;
305 
306         metric_report_definition::CollectionTimeScope
307             redfishCollectionTimeScope =
308                 toRedfishCollectionTimeScope(collectionTimeScope);
309         if (redfishCollectionTimeScope ==
310             metric_report_definition::CollectionTimeScope::Invalid)
311         {
312             messages::internalError(asyncResp->res);
313             return;
314         }
315         metric["CollectionTimeScope"] = redfishCollectionTimeScope;
316 
317         metric["MetricProperties"] = std::move(metricProperties);
318         metric["CollectionDuration"] = time_utils::toDurationString(
319             std::chrono::milliseconds(collectionDuration));
320         metrics.emplace_back(std::move(metric));
321     }
322     asyncResp->res.jsonValue["Metrics"] = std::move(metrics);
323 
324     if (enabled)
325     {
326         asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
327     }
328     else
329     {
330         asyncResp->res.jsonValue["Status"]["State"] = "Disabled";
331     }
332 
333     metric_report_definition::ReportUpdatesEnum redfishReportUpdates =
334         toRedfishReportUpdates(reportUpdates);
335     if (redfishReportUpdates ==
336         metric_report_definition::ReportUpdatesEnum::Invalid)
337     {
338         messages::internalError(asyncResp->res);
339         return;
340     }
341     asyncResp->res.jsonValue["ReportUpdates"] = redfishReportUpdates;
342 
343     asyncResp->res.jsonValue["MetricReportDefinitionEnabled"] = enabled;
344     asyncResp->res.jsonValue["AppendLimit"] = appendLimit;
345     asyncResp->res.jsonValue["Name"] = name;
346     asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] =
347         time_utils::toDurationString(std::chrono::milliseconds(interval));
348     asyncResp->res.jsonValue["@odata.type"] =
349         "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
350     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
351         "/redfish/v1/TelemetryService/MetricReportDefinitions/{}", id);
352     asyncResp->res.jsonValue["Id"] = id;
353     asyncResp->res.jsonValue["MetricReport"]["@odata.id"] = boost::urls::format(
354         "/redfish/v1/TelemetryService/MetricReports/{}", id);
355 }
356 
357 struct AddReportArgs
358 {
359     struct MetricArgs
360     {
361         std::vector<std::string> uris;
362         std::string collectionFunction;
363         std::string collectionTimeScope;
364         uint64_t collectionDuration = 0;
365     };
366 
367     std::string id;
368     std::string name;
369     std::string reportingType;
370     std::string reportUpdates;
371     uint64_t appendLimit = std::numeric_limits<uint64_t>::max();
372     std::vector<std::string> reportActions;
373     uint64_t interval = std::numeric_limits<uint64_t>::max();
374     std::vector<MetricArgs> metrics;
375     bool metricReportDefinitionEnabled = true;
376 };
377 
378 inline bool toDbusReportActions(crow::Response& res,
379                                 const std::vector<std::string>& actions,
380                                 AddReportArgs& args)
381 {
382     size_t index = 0;
383     for (const std::string& action : actions)
384     {
385         std::string dbusReportAction = toDbusReportAction(action);
386         if (dbusReportAction.empty())
387         {
388             messages::propertyValueNotInList(res, nlohmann::json(action).dump(),
389                                              "ReportActions/" +
390                                                  std::to_string(index));
391             return false;
392         }
393 
394         args.reportActions.emplace_back(std::move(dbusReportAction));
395         index++;
396     }
397     return true;
398 }
399 
400 inline bool getUserMetric(crow::Response& res, nlohmann::json& metric,
401                           AddReportArgs::MetricArgs& metricArgs)
402 {
403     std::optional<std::vector<std::string>> uris;
404     std::optional<std::string> collectionDurationStr;
405     std::optional<std::string> collectionFunction;
406     std::optional<std::string> collectionTimeScopeStr;
407 
408     if (!json_util::readJson(metric, res, "MetricProperties", uris,
409                              "CollectionFunction", collectionFunction,
410                              "CollectionTimeScope", collectionTimeScopeStr,
411                              "CollectionDuration", collectionDurationStr))
412     {
413         return false;
414     }
415 
416     if (uris)
417     {
418         metricArgs.uris = std::move(*uris);
419     }
420 
421     if (collectionFunction)
422     {
423         std::string dbusCollectionFunction =
424             telemetry::toDbusCollectionFunction(*collectionFunction);
425         if (dbusCollectionFunction.empty())
426         {
427             messages::propertyValueIncorrect(res, "CollectionFunction",
428                                              *collectionFunction);
429             return false;
430         }
431         metricArgs.collectionFunction = std::move(dbusCollectionFunction);
432     }
433 
434     if (collectionTimeScopeStr)
435     {
436         std::string dbusCollectionTimeScope =
437             toDbusCollectionTimeScope(*collectionTimeScopeStr);
438         if (dbusCollectionTimeScope.empty())
439         {
440             messages::propertyValueIncorrect(res, "CollectionTimeScope",
441                                              *collectionTimeScopeStr);
442             return false;
443         }
444         metricArgs.collectionTimeScope = std::move(dbusCollectionTimeScope);
445     }
446 
447     if (collectionDurationStr)
448     {
449         std::optional<std::chrono::milliseconds> duration =
450             time_utils::fromDurationString(*collectionDurationStr);
451 
452         if (!duration || duration->count() < 0)
453         {
454             messages::propertyValueIncorrect(res, "CollectionDuration",
455                                              *collectionDurationStr);
456             return false;
457         }
458 
459         metricArgs.collectionDuration =
460             static_cast<uint64_t>(duration->count());
461     }
462 
463     return true;
464 }
465 
466 inline bool getUserMetrics(crow::Response& res,
467                            std::span<nlohmann::json> metrics,
468                            std::vector<AddReportArgs::MetricArgs>& result)
469 {
470     result.reserve(metrics.size());
471 
472     for (nlohmann::json& m : metrics)
473     {
474         AddReportArgs::MetricArgs metricArgs;
475 
476         if (!getUserMetric(res, m, metricArgs))
477         {
478             return false;
479         }
480 
481         result.emplace_back(std::move(metricArgs));
482     }
483 
484     return true;
485 }
486 
487 inline bool getUserParameters(crow::Response& res, const crow::Request& req,
488                               AddReportArgs& args)
489 {
490     std::optional<std::string> id;
491     std::optional<std::string> name;
492     std::optional<std::string> reportingTypeStr;
493     std::optional<std::string> reportUpdatesStr;
494     std::optional<uint64_t> appendLimit;
495     std::optional<bool> metricReportDefinitionEnabled;
496     std::optional<std::vector<nlohmann::json>> metrics;
497     std::optional<std::vector<std::string>> reportActionsStr;
498     std::optional<nlohmann::json> schedule;
499 
500     if (!json_util::readJsonPatch(
501             req, res, "Id", id, "Name", name, "Metrics", metrics,
502             "MetricReportDefinitionType", reportingTypeStr, "ReportUpdates",
503             reportUpdatesStr, "AppendLimit", appendLimit, "ReportActions",
504             reportActionsStr, "Schedule", schedule,
505             "MetricReportDefinitionEnabled", metricReportDefinitionEnabled))
506     {
507         return false;
508     }
509 
510     if (id)
511     {
512         constexpr const char* allowedCharactersInId =
513             "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
514         if (id->empty() ||
515             id->find_first_not_of(allowedCharactersInId) != std::string::npos)
516         {
517             messages::propertyValueIncorrect(res, "Id", *id);
518             return false;
519         }
520         args.id = *id;
521     }
522 
523     if (name)
524     {
525         args.name = *name;
526     }
527 
528     if (reportingTypeStr)
529     {
530         std::string dbusReportingType = toDbusReportingType(*reportingTypeStr);
531         if (dbusReportingType.empty())
532         {
533             messages::propertyValueNotInList(res, *reportingTypeStr,
534                                              "MetricReportDefinitionType");
535             return false;
536         }
537         args.reportingType = dbusReportingType;
538     }
539 
540     if (reportUpdatesStr)
541     {
542         std::string dbusReportUpdates = toDbusReportUpdates(*reportUpdatesStr);
543         if (dbusReportUpdates.empty())
544         {
545             messages::propertyValueNotInList(res, *reportUpdatesStr,
546                                              "ReportUpdates");
547             return false;
548         }
549         args.reportUpdates = dbusReportUpdates;
550     }
551 
552     if (appendLimit)
553     {
554         args.appendLimit = *appendLimit;
555     }
556 
557     if (metricReportDefinitionEnabled)
558     {
559         args.metricReportDefinitionEnabled = *metricReportDefinitionEnabled;
560     }
561 
562     if (reportActionsStr)
563     {
564         if (!toDbusReportActions(res, *reportActionsStr, args))
565         {
566             return false;
567         }
568     }
569 
570     if (reportingTypeStr == "Periodic")
571     {
572         if (!schedule)
573         {
574             messages::createFailedMissingReqProperties(res, "Schedule");
575             return false;
576         }
577 
578         std::string durationStr;
579         if (!json_util::readJson(*schedule, res, "RecurrenceInterval",
580                                  durationStr))
581         {
582             return false;
583         }
584 
585         std::optional<std::chrono::milliseconds> durationNum =
586             time_utils::fromDurationString(durationStr);
587         if (!durationNum || durationNum->count() < 0)
588         {
589             messages::propertyValueIncorrect(res, "RecurrenceInterval",
590                                              durationStr);
591             return false;
592         }
593         args.interval = static_cast<uint64_t>(durationNum->count());
594     }
595 
596     if (metrics)
597     {
598         if (!getUserMetrics(res, *metrics, args.metrics))
599         {
600             return false;
601         }
602     }
603 
604     return true;
605 }
606 
607 inline bool getChassisSensorNodeFromMetrics(
608     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
609     std::span<const AddReportArgs::MetricArgs> metrics,
610     boost::container::flat_set<std::pair<std::string, std::string>>& matched)
611 {
612     for (const auto& metric : metrics)
613     {
614         std::optional<IncorrectMetricUri> error =
615             getChassisSensorNode(metric.uris, matched);
616         if (error)
617         {
618             messages::propertyValueIncorrect(asyncResp->res, error->uri,
619                                              "MetricProperties/" +
620                                                  std::to_string(error->index));
621             return false;
622         }
623     }
624     return true;
625 }
626 
627 class AddReport
628 {
629   public:
630     AddReport(AddReportArgs argsIn,
631               const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
632         asyncResp(asyncRespIn),
633         args{std::move(argsIn)}
634     {}
635 
636     ~AddReport()
637     {
638         boost::asio::post(crow::connections::systemBus->get_io_context(),
639                           std::bind_front(&performAddReport, asyncResp, args,
640                                           std::move(uriToDbus)));
641     }
642 
643     static void performAddReport(
644         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
645         const AddReportArgs& args,
646         const boost::container::flat_map<std::string, std::string>& uriToDbus)
647     {
648         if (asyncResp->res.result() != boost::beast::http::status::ok)
649         {
650             return;
651         }
652 
653         telemetry::ReadingParameters readingParams;
654         readingParams.reserve(args.metrics.size());
655 
656         for (const auto& metric : args.metrics)
657         {
658             std::vector<
659                 std::tuple<sdbusplus::message::object_path, std::string>>
660                 sensorParams;
661             sensorParams.reserve(metric.uris.size());
662 
663             for (size_t i = 0; i < metric.uris.size(); i++)
664             {
665                 const std::string& uri = metric.uris[i];
666                 auto el = uriToDbus.find(uri);
667                 if (el == uriToDbus.end())
668                 {
669                     BMCWEB_LOG_ERROR
670                         << "Failed to find DBus sensor corresponding to URI "
671                         << uri;
672                     messages::propertyValueNotInList(asyncResp->res, uri,
673                                                      "MetricProperties/" +
674                                                          std::to_string(i));
675                     return;
676                 }
677 
678                 const std::string& dbusPath = el->second;
679                 sensorParams.emplace_back(dbusPath, uri);
680             }
681 
682             readingParams.emplace_back(
683                 std::move(sensorParams), metric.collectionFunction,
684                 metric.collectionTimeScope, metric.collectionDuration);
685         }
686 
687         crow::connections::systemBus->async_method_call(
688             [asyncResp, id = args.id, uriToDbus](
689                 const boost::system::error_code& ec, const std::string&) {
690             if (ec == boost::system::errc::file_exists)
691             {
692                 messages::resourceAlreadyExists(
693                     asyncResp->res, "MetricReportDefinition", "Id", id);
694                 return;
695             }
696             if (ec == boost::system::errc::too_many_files_open)
697             {
698                 messages::createLimitReachedForResource(asyncResp->res);
699                 return;
700             }
701             if (ec == boost::system::errc::argument_list_too_long)
702             {
703                 nlohmann::json metricProperties = nlohmann::json::array();
704                 for (const auto& [uri, _] : uriToDbus)
705                 {
706                     metricProperties.emplace_back(uri);
707                 }
708                 messages::propertyValueIncorrect(asyncResp->res,
709                                                  metricProperties.dump(),
710                                                  "MetricProperties");
711                 return;
712             }
713             if (ec)
714             {
715                 messages::internalError(asyncResp->res);
716                 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
717                 return;
718             }
719 
720             messages::created(asyncResp->res);
721             },
722             telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
723             "xyz.openbmc_project.Telemetry.ReportManager", "AddReport",
724             "TelemetryService/" + args.id, args.name, args.reportingType,
725             args.reportUpdates, args.appendLimit, args.reportActions,
726             args.interval, readingParams, args.metricReportDefinitionEnabled);
727     }
728 
729     AddReport(const AddReport&) = delete;
730     AddReport(AddReport&&) = delete;
731     AddReport& operator=(const AddReport&) = delete;
732     AddReport& operator=(AddReport&&) = delete;
733 
734     void insert(const std::map<std::string, std::string>& el)
735     {
736         uriToDbus.insert(el.begin(), el.end());
737     }
738 
739   private:
740     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
741     AddReportArgs args;
742     boost::container::flat_map<std::string, std::string> uriToDbus{};
743 };
744 } // namespace telemetry
745 
746 inline void requestRoutesMetricReportDefinitionCollection(App& app)
747 {
748     BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
749         .privileges(redfish::privileges::getMetricReportDefinitionCollection)
750         .methods(boost::beast::http::verb::get)(
751             [&app](const crow::Request& req,
752                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
753         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
754         {
755             return;
756         }
757 
758         asyncResp->res.jsonValue["@odata.type"] =
759             "#MetricReportDefinitionCollection."
760             "MetricReportDefinitionCollection";
761         asyncResp->res.jsonValue["@odata.id"] =
762             "/redfish/v1/TelemetryService/MetricReportDefinitions";
763         asyncResp->res.jsonValue["Name"] = "Metric Definition Collection";
764         constexpr std::array<std::string_view, 1> interfaces{
765             telemetry::reportInterface};
766         collection_util::getCollectionMembers(
767             asyncResp,
768             boost::urls::url(
769                 "/redfish/v1/TelemetryService/MetricReportDefinitions"),
770             interfaces,
771             "/xyz/openbmc_project/Telemetry/Reports/TelemetryService");
772         });
773 
774     BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
775         .privileges(redfish::privileges::postMetricReportDefinitionCollection)
776         .methods(boost::beast::http::verb::post)(
777             [&app](const crow::Request& req,
778                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
779         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
780         {
781             return;
782         }
783 
784         telemetry::AddReportArgs args;
785         if (!telemetry::getUserParameters(asyncResp->res, req, args))
786         {
787             return;
788         }
789 
790         boost::container::flat_set<std::pair<std::string, std::string>>
791             chassisSensors;
792         if (!telemetry::getChassisSensorNodeFromMetrics(asyncResp, args.metrics,
793                                                         chassisSensors))
794         {
795             return;
796         }
797 
798         auto addReportReq =
799             std::make_shared<telemetry::AddReport>(std::move(args), asyncResp);
800         for (const auto& [chassis, sensorType] : chassisSensors)
801         {
802             retrieveUriToDbusMap(
803                 chassis, sensorType,
804                 [asyncResp, addReportReq](
805                     const boost::beast::http::status status,
806                     const std::map<std::string, std::string>& uriToDbus) {
807                 if (status != boost::beast::http::status::ok)
808                 {
809                     BMCWEB_LOG_ERROR
810                         << "Failed to retrieve URI to dbus sensors map with err "
811                         << static_cast<unsigned>(status);
812                     return;
813                 }
814                 addReportReq->insert(uriToDbus);
815                 });
816         }
817         });
818 }
819 
820 inline void requestRoutesMetricReportDefinition(App& app)
821 {
822     BMCWEB_ROUTE(app,
823                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
824         .privileges(redfish::privileges::getMetricReportDefinition)
825         .methods(boost::beast::http::verb::get)(
826             [&app](const crow::Request& req,
827                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
828                    const std::string& id) {
829         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
830         {
831             return;
832         }
833 
834         sdbusplus::asio::getAllProperties(
835             *crow::connections::systemBus, telemetry::service,
836             telemetry::getDbusReportPath(id), telemetry::reportInterface,
837             [asyncResp,
838              id](const boost::system::error_code& ec,
839                  const dbus::utility::DBusPropertiesMap& properties) {
840             if (ec.value() == EBADR ||
841                 ec == boost::system::errc::host_unreachable)
842             {
843                 messages::resourceNotFound(asyncResp->res,
844                                            "MetricReportDefinition", id);
845                 return;
846             }
847             if (ec)
848             {
849                 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
850                 messages::internalError(asyncResp->res);
851                 return;
852             }
853 
854             telemetry::fillReportDefinition(asyncResp, id, properties);
855             });
856         });
857 
858     BMCWEB_ROUTE(app,
859                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
860         .privileges(redfish::privileges::deleteMetricReportDefinitionCollection)
861         .methods(boost::beast::http::verb::delete_)(
862             [&app](const crow::Request& req,
863                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
864                    const std::string& id)
865 
866             {
867         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
868         {
869             return;
870         }
871 
872         const std::string reportPath = telemetry::getDbusReportPath(id);
873 
874         crow::connections::systemBus->async_method_call(
875             [asyncResp, id](const boost::system::error_code& ec) {
876             /*
877              * boost::system::errc and std::errc are missing value
878              * for EBADR error that is defined in Linux.
879              */
880             if (ec.value() == EBADR)
881             {
882                 messages::resourceNotFound(asyncResp->res,
883                                            "MetricReportDefinition", id);
884                 return;
885             }
886 
887             if (ec)
888             {
889                 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
890                 messages::internalError(asyncResp->res);
891                 return;
892             }
893 
894             asyncResp->res.result(boost::beast::http::status::no_content);
895             },
896             telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete",
897             "Delete");
898         });
899 }
900 } // namespace redfish
901