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(
709                     asyncResp->res, metricProperties, "MetricProperties");
710                 return;
711             }
712             if (ec)
713             {
714                 messages::internalError(asyncResp->res);
715                 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
716                 return;
717             }
718 
719             messages::created(asyncResp->res);
720             },
721             telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
722             "xyz.openbmc_project.Telemetry.ReportManager", "AddReport",
723             "TelemetryService/" + args.id, args.name, args.reportingType,
724             args.reportUpdates, args.appendLimit, args.reportActions,
725             args.interval, readingParams, args.metricReportDefinitionEnabled);
726     }
727 
728     AddReport(const AddReport&) = delete;
729     AddReport(AddReport&&) = delete;
730     AddReport& operator=(const AddReport&) = delete;
731     AddReport& operator=(AddReport&&) = delete;
732 
733     void insert(const std::map<std::string, std::string>& el)
734     {
735         uriToDbus.insert(el.begin(), el.end());
736     }
737 
738   private:
739     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
740     AddReportArgs args;
741     boost::container::flat_map<std::string, std::string> uriToDbus{};
742 };
743 } // namespace telemetry
744 
745 inline void handleMetricReportDefinitionCollectionHead(
746     App& app, const crow::Request& req,
747     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
748 {
749     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
750     {
751         return;
752     }
753     asyncResp->res.addHeader(
754         boost::beast::http::field::link,
755         "</redfish/v1/JsonSchemas/MetricReportDefinitionCollection/MetricReportDefinitionCollection.json>; rel=describedby");
756 }
757 
758 inline void handleMetricReportDefinitionCollectionGet(
759     App& app, const crow::Request& req,
760     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
761 {
762     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
763     {
764         return;
765     }
766 
767     asyncResp->res.jsonValue["@odata.type"] =
768         "#MetricReportDefinitionCollection."
769         "MetricReportDefinitionCollection";
770     asyncResp->res.jsonValue["@odata.id"] =
771         "/redfish/v1/TelemetryService/MetricReportDefinitions";
772     asyncResp->res.jsonValue["Name"] = "Metric Definition Collection";
773     constexpr std::array<std::string_view, 1> interfaces{
774         telemetry::reportInterface};
775     collection_util::getCollectionMembers(
776         asyncResp,
777         boost::urls::url(
778             "/redfish/v1/TelemetryService/MetricReportDefinitions"),
779         interfaces, "/xyz/openbmc_project/Telemetry/Reports/TelemetryService");
780 }
781 
782 inline void
783     handleMetricReportHead(App& app, const crow::Request& req,
784                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
785                            const std::string& /*id*/)
786 {
787     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
788     {
789         return;
790     }
791     asyncResp->res.addHeader(
792         boost::beast::http::field::link,
793         "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby");
794 }
795 
796 inline void
797     handleMetricReportGet(App& app, const crow::Request& req,
798                           const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
799                           const std::string& id)
800 {
801     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
802     {
803         return;
804     }
805     asyncResp->res.addHeader(
806         boost::beast::http::field::link,
807         "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby");
808 
809     sdbusplus::asio::getAllProperties(
810         *crow::connections::systemBus, telemetry::service,
811         telemetry::getDbusReportPath(id), telemetry::reportInterface,
812         [asyncResp, id](const boost::system::error_code& ec,
813                         const dbus::utility::DBusPropertiesMap& properties) {
814         if (ec.value() == EBADR || ec == boost::system::errc::host_unreachable)
815         {
816             messages::resourceNotFound(asyncResp->res, "MetricReportDefinition",
817                                        id);
818             return;
819         }
820         if (ec)
821         {
822             BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
823             messages::internalError(asyncResp->res);
824             return;
825         }
826 
827         telemetry::fillReportDefinition(asyncResp, id, properties);
828         });
829 }
830 
831 inline void handleMetricReportDelete(
832     App& app, const crow::Request& req,
833     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
834 
835 {
836     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
837     {
838         return;
839     }
840 
841     const std::string reportPath = telemetry::getDbusReportPath(id);
842 
843     crow::connections::systemBus->async_method_call(
844         [asyncResp, id](const boost::system::error_code& ec) {
845         /*
846          * boost::system::errc and std::errc are missing value
847          * for EBADR error that is defined in Linux.
848          */
849         if (ec.value() == EBADR)
850         {
851             messages::resourceNotFound(asyncResp->res, "MetricReportDefinition",
852                                        id);
853             return;
854         }
855 
856         if (ec)
857         {
858             BMCWEB_LOG_ERROR << "respHandler DBus error " << ec;
859             messages::internalError(asyncResp->res);
860             return;
861         }
862 
863         asyncResp->res.result(boost::beast::http::status::no_content);
864         },
865         telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete",
866         "Delete");
867 }
868 
869 inline void requestRoutesMetricReportDefinitionCollection(App& app)
870 {
871     BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
872         .privileges(redfish::privileges::headMetricReportDefinitionCollection)
873         .methods(boost::beast::http::verb::head)(std::bind_front(
874             handleMetricReportDefinitionCollectionHead, std::ref(app)));
875 
876     BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
877         .privileges(redfish::privileges::getMetricReportDefinitionCollection)
878         .methods(boost::beast::http::verb::get)(std::bind_front(
879             handleMetricReportDefinitionCollectionGet, std::ref(app)));
880 
881     BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
882         .privileges(redfish::privileges::postMetricReportDefinitionCollection)
883         .methods(boost::beast::http::verb::post)(
884             [&app](const crow::Request& req,
885                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
886         if (!redfish::setUpRedfishRoute(app, req, asyncResp))
887         {
888             return;
889         }
890 
891         telemetry::AddReportArgs args;
892         if (!telemetry::getUserParameters(asyncResp->res, req, args))
893         {
894             return;
895         }
896 
897         boost::container::flat_set<std::pair<std::string, std::string>>
898             chassisSensors;
899         if (!telemetry::getChassisSensorNodeFromMetrics(asyncResp, args.metrics,
900                                                         chassisSensors))
901         {
902             return;
903         }
904 
905         auto addReportReq =
906             std::make_shared<telemetry::AddReport>(std::move(args), asyncResp);
907         for (const auto& [chassis, sensorType] : chassisSensors)
908         {
909             retrieveUriToDbusMap(
910                 chassis, sensorType,
911                 [asyncResp, addReportReq](
912                     const boost::beast::http::status status,
913                     const std::map<std::string, std::string>& uriToDbus) {
914                 if (status != boost::beast::http::status::ok)
915                 {
916                     BMCWEB_LOG_ERROR
917                         << "Failed to retrieve URI to dbus sensors map with err "
918                         << static_cast<unsigned>(status);
919                     return;
920                 }
921                 addReportReq->insert(uriToDbus);
922                 });
923         }
924         });
925 }
926 
927 inline void requestRoutesMetricReportDefinition(App& app)
928 {
929     BMCWEB_ROUTE(app,
930                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
931         .privileges(redfish::privileges::getMetricReportDefinition)
932         .methods(boost::beast::http::verb::head)(
933             std::bind_front(handleMetricReportHead, std::ref(app)));
934 
935     BMCWEB_ROUTE(app,
936                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
937         .privileges(redfish::privileges::getMetricReportDefinition)
938         .methods(boost::beast::http::verb::get)(
939             std::bind_front(handleMetricReportGet, std::ref(app)));
940 
941     BMCWEB_ROUTE(app,
942                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
943         .privileges(redfish::privileges::deleteMetricReportDefinitionCollection)
944         .methods(boost::beast::http::verb::delete_)(
945             std::bind_front(handleMetricReportDelete, std::ref(app)));
946 }
947 } // namespace redfish
948