xref: /openbmc/bmcweb/redfish-core/lib/metric_report_definition.hpp (revision 1516c21b27faf8dcf7c41e9b7253da97025a5f28)
1 #pragma once
2 
3 #include "app.hpp"
4 #include "dbus_utility.hpp"
5 #include "generated/enums/metric_report_definition.hpp"
6 #include "generated/enums/resource.hpp"
7 #include "query.hpp"
8 #include "registries/privilege_registry.hpp"
9 #include "sensors.hpp"
10 #include "utils/collection.hpp"
11 #include "utils/dbus_utils.hpp"
12 #include "utils/json_utils.hpp"
13 #include "utils/telemetry_utils.hpp"
14 #include "utils/time_utils.hpp"
15 
16 #include <boost/container/flat_map.hpp>
17 #include <boost/url/format.hpp>
18 #include <sdbusplus/asio/property.hpp>
19 #include <sdbusplus/unpack_properties.hpp>
20 
21 #include <array>
22 #include <map>
23 #include <optional>
24 #include <span>
25 #include <string>
26 #include <string_view>
27 #include <tuple>
28 #include <utility>
29 #include <variant>
30 #include <vector>
31 
32 namespace redfish
33 {
34 
35 namespace telemetry
36 {
37 
38 using ReadingParameters = std::vector<std::tuple<
39     std::vector<std::tuple<sdbusplus::message::object_path, std::string>>,
40     std::string, std::string, uint64_t>>;
41 
42 inline bool verifyCommonErrors(crow::Response& res, const std::string& id,
43                                const boost::system::error_code& ec)
44 {
45     if (ec.value() == EBADR || ec == boost::system::errc::host_unreachable)
46     {
47         messages::resourceNotFound(res, "MetricReportDefinition", id);
48         return false;
49     }
50 
51     if (ec == boost::system::errc::file_exists)
52     {
53         messages::resourceAlreadyExists(res, "MetricReportDefinition", "Id",
54                                         id);
55         return false;
56     }
57 
58     if (ec == boost::system::errc::too_many_files_open)
59     {
60         messages::createLimitReachedForResource(res);
61         return false;
62     }
63 
64     if (ec)
65     {
66         BMCWEB_LOG_ERROR("DBUS response error {}", ec);
67         messages::internalError(res);
68         return false;
69     }
70 
71     return true;
72 }
73 
74 inline metric_report_definition::ReportActionsEnum
75     toRedfishReportAction(std::string_view dbusValue)
76 {
77     if (dbusValue ==
78         "xyz.openbmc_project.Telemetry.Report.ReportActions.EmitsReadingsUpdate")
79     {
80         return metric_report_definition::ReportActionsEnum::RedfishEvent;
81     }
82     if (dbusValue ==
83         "xyz.openbmc_project.Telemetry.Report.ReportActions.LogToMetricReportsCollection")
84     {
85         return metric_report_definition::ReportActionsEnum::
86             LogToMetricReportsCollection;
87     }
88     return metric_report_definition::ReportActionsEnum::Invalid;
89 }
90 
91 inline std::string toDbusReportAction(std::string_view redfishValue)
92 {
93     if (redfishValue == "RedfishEvent")
94     {
95         return "xyz.openbmc_project.Telemetry.Report.ReportActions.EmitsReadingsUpdate";
96     }
97     if (redfishValue == "LogToMetricReportsCollection")
98     {
99         return "xyz.openbmc_project.Telemetry.Report.ReportActions.LogToMetricReportsCollection";
100     }
101     return "";
102 }
103 
104 inline metric_report_definition::MetricReportDefinitionType
105     toRedfishReportingType(std::string_view dbusValue)
106 {
107     if (dbusValue ==
108         "xyz.openbmc_project.Telemetry.Report.ReportingType.OnChange")
109     {
110         return metric_report_definition::MetricReportDefinitionType::OnChange;
111     }
112     if (dbusValue ==
113         "xyz.openbmc_project.Telemetry.Report.ReportingType.OnRequest")
114     {
115         return metric_report_definition::MetricReportDefinitionType::OnRequest;
116     }
117     if (dbusValue ==
118         "xyz.openbmc_project.Telemetry.Report.ReportingType.Periodic")
119     {
120         return metric_report_definition::MetricReportDefinitionType::Periodic;
121     }
122     return metric_report_definition::MetricReportDefinitionType::Invalid;
123 }
124 
125 inline std::string toDbusReportingType(std::string_view redfishValue)
126 {
127     if (redfishValue == "OnChange")
128     {
129         return "xyz.openbmc_project.Telemetry.Report.ReportingType.OnChange";
130     }
131     if (redfishValue == "OnRequest")
132     {
133         return "xyz.openbmc_project.Telemetry.Report.ReportingType.OnRequest";
134     }
135     if (redfishValue == "Periodic")
136     {
137         return "xyz.openbmc_project.Telemetry.Report.ReportingType.Periodic";
138     }
139     return "";
140 }
141 
142 inline metric_report_definition::CollectionTimeScope
143     toRedfishCollectionTimeScope(std::string_view dbusValue)
144 {
145     if (dbusValue ==
146         "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Point")
147     {
148         return metric_report_definition::CollectionTimeScope::Point;
149     }
150     if (dbusValue ==
151         "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Interval")
152     {
153         return metric_report_definition::CollectionTimeScope::Interval;
154     }
155     if (dbusValue ==
156         "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.StartupInterval")
157     {
158         return metric_report_definition::CollectionTimeScope::StartupInterval;
159     }
160     return metric_report_definition::CollectionTimeScope::Invalid;
161 }
162 
163 inline std::string toDbusCollectionTimeScope(std::string_view redfishValue)
164 {
165     if (redfishValue == "Point")
166     {
167         return "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Point";
168     }
169     if (redfishValue == "Interval")
170     {
171         return "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Interval";
172     }
173     if (redfishValue == "StartupInterval")
174     {
175         return "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.StartupInterval";
176     }
177     return "";
178 }
179 
180 inline metric_report_definition::ReportUpdatesEnum
181     toRedfishReportUpdates(std::string_view dbusValue)
182 {
183     if (dbusValue ==
184         "xyz.openbmc_project.Telemetry.Report.ReportUpdates.Overwrite")
185     {
186         return metric_report_definition::ReportUpdatesEnum::Overwrite;
187     }
188     if (dbusValue ==
189         "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendWrapsWhenFull")
190     {
191         return metric_report_definition::ReportUpdatesEnum::AppendWrapsWhenFull;
192     }
193     if (dbusValue ==
194         "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendStopsWhenFull")
195     {
196         return metric_report_definition::ReportUpdatesEnum::AppendStopsWhenFull;
197     }
198     return metric_report_definition::ReportUpdatesEnum::Invalid;
199 }
200 
201 inline std::string toDbusReportUpdates(std::string_view redfishValue)
202 {
203     if (redfishValue == "Overwrite")
204     {
205         return "xyz.openbmc_project.Telemetry.Report.ReportUpdates.Overwrite";
206     }
207     if (redfishValue == "AppendWrapsWhenFull")
208     {
209         return "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendWrapsWhenFull";
210     }
211     if (redfishValue == "AppendStopsWhenFull")
212     {
213         return "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendStopsWhenFull";
214     }
215     return "";
216 }
217 
218 inline std::optional<nlohmann::json::array_t> getLinkedTriggers(
219     std::span<const sdbusplus::message::object_path> triggerPaths)
220 {
221     nlohmann::json::array_t triggers;
222 
223     for (const sdbusplus::message::object_path& path : triggerPaths)
224     {
225         if (path.parent_path() !=
226             "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService")
227         {
228             BMCWEB_LOG_ERROR("Property Triggers contains invalid value: {}",
229                              path.str);
230             return std::nullopt;
231         }
232 
233         std::string id = path.filename();
234         if (id.empty())
235         {
236             BMCWEB_LOG_ERROR("Property Triggers contains invalid value: {}",
237                              path.str);
238             return std::nullopt;
239         }
240         nlohmann::json::object_t trigger;
241         trigger["@odata.id"] =
242             boost::urls::format("/redfish/v1/TelemetryService/Triggers/{}", id);
243         triggers.emplace_back(std::move(trigger));
244     }
245 
246     return triggers;
247 }
248 
249 inline void fillReportDefinition(
250     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
251     const dbus::utility::DBusPropertiesMap& properties)
252 {
253     std::vector<std::string> reportActions;
254     ReadingParameters readingParams;
255     std::string reportingType;
256     std::string reportUpdates;
257     std::string name;
258     uint64_t appendLimit = 0;
259     uint64_t interval = 0;
260     bool enabled = false;
261     std::vector<sdbusplus::message::object_path> triggers;
262 
263     const bool success = sdbusplus::unpackPropertiesNoThrow(
264         dbus_utils::UnpackErrorPrinter(), properties, "ReportingType",
265         reportingType, "Interval", interval, "ReportActions", reportActions,
266         "ReportUpdates", reportUpdates, "AppendLimit", appendLimit,
267         "ReadingParameters", readingParams, "Name", name, "Enabled", enabled,
268         "Triggers", triggers);
269 
270     if (!success)
271     {
272         messages::internalError(asyncResp->res);
273         return;
274     }
275 
276     metric_report_definition::MetricReportDefinitionType redfishReportingType =
277         toRedfishReportingType(reportingType);
278     if (redfishReportingType ==
279         metric_report_definition::MetricReportDefinitionType::Invalid)
280     {
281         messages::internalError(asyncResp->res);
282         return;
283     }
284 
285     asyncResp->res.jsonValue["MetricReportDefinitionType"] =
286         redfishReportingType;
287 
288     std::optional<nlohmann::json::array_t> linkedTriggers =
289         getLinkedTriggers(triggers);
290     if (!linkedTriggers)
291     {
292         messages::internalError(asyncResp->res);
293         return;
294     }
295 
296     asyncResp->res.jsonValue["Links"]["Triggers"] = std::move(*linkedTriggers);
297 
298     nlohmann::json::array_t redfishReportActions;
299     for (const std::string& action : reportActions)
300     {
301         metric_report_definition::ReportActionsEnum redfishAction =
302             toRedfishReportAction(action);
303         if (redfishAction ==
304             metric_report_definition::ReportActionsEnum::Invalid)
305         {
306             messages::internalError(asyncResp->res);
307             return;
308         }
309 
310         redfishReportActions.emplace_back(redfishAction);
311     }
312 
313     asyncResp->res.jsonValue["ReportActions"] = std::move(redfishReportActions);
314 
315     nlohmann::json::array_t metrics = nlohmann::json::array();
316     for (const auto& [sensorData, collectionFunction, collectionTimeScope,
317                       collectionDuration] : readingParams)
318     {
319         nlohmann::json::array_t metricProperties;
320 
321         for (const auto& [sensorPath, sensorMetadata] : sensorData)
322         {
323             metricProperties.emplace_back(sensorMetadata);
324         }
325 
326         nlohmann::json::object_t metric;
327 
328         metric_report_definition::CalculationAlgorithmEnum
329             redfishCollectionFunction =
330                 telemetry::toRedfishCollectionFunction(collectionFunction);
331         if (redfishCollectionFunction ==
332             metric_report_definition::CalculationAlgorithmEnum::Invalid)
333         {
334             messages::internalError(asyncResp->res);
335             return;
336         }
337         metric["CollectionFunction"] = redfishCollectionFunction;
338 
339         metric_report_definition::CollectionTimeScope
340             redfishCollectionTimeScope =
341                 toRedfishCollectionTimeScope(collectionTimeScope);
342         if (redfishCollectionTimeScope ==
343             metric_report_definition::CollectionTimeScope::Invalid)
344         {
345             messages::internalError(asyncResp->res);
346             return;
347         }
348         metric["CollectionTimeScope"] = redfishCollectionTimeScope;
349 
350         metric["MetricProperties"] = std::move(metricProperties);
351         metric["CollectionDuration"] = time_utils::toDurationString(
352             std::chrono::milliseconds(collectionDuration));
353         metrics.emplace_back(std::move(metric));
354     }
355     asyncResp->res.jsonValue["Metrics"] = std::move(metrics);
356 
357     if (enabled)
358     {
359         asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled;
360     }
361     else
362     {
363         asyncResp->res.jsonValue["Status"]["State"] = resource::State::Disabled;
364     }
365 
366     metric_report_definition::ReportUpdatesEnum redfishReportUpdates =
367         toRedfishReportUpdates(reportUpdates);
368     if (redfishReportUpdates ==
369         metric_report_definition::ReportUpdatesEnum::Invalid)
370     {
371         messages::internalError(asyncResp->res);
372         return;
373     }
374     asyncResp->res.jsonValue["ReportUpdates"] = redfishReportUpdates;
375 
376     asyncResp->res.jsonValue["MetricReportDefinitionEnabled"] = enabled;
377     asyncResp->res.jsonValue["AppendLimit"] = appendLimit;
378     asyncResp->res.jsonValue["Name"] = name;
379     asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] =
380         time_utils::toDurationString(std::chrono::milliseconds(interval));
381     asyncResp->res.jsonValue["@odata.type"] =
382         "#MetricReportDefinition.v1_3_0.MetricReportDefinition";
383     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
384         "/redfish/v1/TelemetryService/MetricReportDefinitions/{}", id);
385     asyncResp->res.jsonValue["Id"] = id;
386     asyncResp->res.jsonValue["MetricReport"]["@odata.id"] = boost::urls::format(
387         "/redfish/v1/TelemetryService/MetricReports/{}", id);
388 }
389 
390 struct AddReportArgs
391 {
392     struct MetricArgs
393     {
394         std::vector<std::string> uris;
395         std::string collectionFunction;
396         std::string collectionTimeScope;
397         uint64_t collectionDuration = 0;
398     };
399 
400     std::string id;
401     std::string name;
402     std::string reportingType;
403     std::string reportUpdates;
404     uint64_t appendLimit = std::numeric_limits<uint64_t>::max();
405     std::vector<std::string> reportActions;
406     uint64_t interval = std::numeric_limits<uint64_t>::max();
407     std::vector<MetricArgs> metrics;
408     bool metricReportDefinitionEnabled = true;
409 };
410 
411 inline bool toDbusReportActions(crow::Response& res,
412                                 const std::vector<std::string>& actions,
413                                 std::vector<std::string>& outReportActions)
414 {
415     size_t index = 0;
416     for (const std::string& action : actions)
417     {
418         std::string dbusReportAction = toDbusReportAction(action);
419         if (dbusReportAction.empty())
420         {
421             messages::propertyValueNotInList(
422                 res, action, "ReportActions/" + std::to_string(index));
423             return false;
424         }
425 
426         outReportActions.emplace_back(std::move(dbusReportAction));
427         index++;
428     }
429     return true;
430 }
431 
432 inline bool getUserMetric(crow::Response& res, nlohmann::json::object_t& metric,
433                           AddReportArgs::MetricArgs& metricArgs)
434 {
435     std::optional<std::vector<std::string>> uris;
436     std::optional<std::string> collectionDurationStr;
437     std::optional<std::string> collectionFunction;
438     std::optional<std::string> collectionTimeScopeStr;
439 
440     if (!json_util::readJsonObject(
441             metric, res, "MetricProperties", uris, "CollectionFunction",
442             collectionFunction, "CollectionTimeScope", collectionTimeScopeStr,
443             "CollectionDuration", collectionDurationStr))
444     {
445         return false;
446     }
447 
448     if (uris)
449     {
450         metricArgs.uris = std::move(*uris);
451     }
452 
453     if (collectionFunction)
454     {
455         std::string dbusCollectionFunction =
456             telemetry::toDbusCollectionFunction(*collectionFunction);
457         if (dbusCollectionFunction.empty())
458         {
459             messages::propertyValueIncorrect(res, "CollectionFunction",
460                                              *collectionFunction);
461             return false;
462         }
463         metricArgs.collectionFunction = std::move(dbusCollectionFunction);
464     }
465 
466     if (collectionTimeScopeStr)
467     {
468         std::string dbusCollectionTimeScope =
469             toDbusCollectionTimeScope(*collectionTimeScopeStr);
470         if (dbusCollectionTimeScope.empty())
471         {
472             messages::propertyValueIncorrect(res, "CollectionTimeScope",
473                                              *collectionTimeScopeStr);
474             return false;
475         }
476         metricArgs.collectionTimeScope = std::move(dbusCollectionTimeScope);
477     }
478 
479     if (collectionDurationStr)
480     {
481         std::optional<std::chrono::milliseconds> duration =
482             time_utils::fromDurationString(*collectionDurationStr);
483 
484         if (!duration || duration->count() < 0)
485         {
486             messages::propertyValueIncorrect(res, "CollectionDuration",
487                                              *collectionDurationStr);
488             return false;
489         }
490 
491         metricArgs.collectionDuration =
492             static_cast<uint64_t>(duration->count());
493     }
494 
495     return true;
496 }
497 
498 inline bool getUserMetrics(crow::Response& res,
499                            std::span<nlohmann::json::object_t> metrics,
500                            std::vector<AddReportArgs::MetricArgs>& result)
501 {
502     result.reserve(metrics.size());
503 
504     for (nlohmann::json::object_t& m : metrics)
505     {
506         AddReportArgs::MetricArgs metricArgs;
507 
508         if (!getUserMetric(res, m, metricArgs))
509         {
510             return false;
511         }
512 
513         result.emplace_back(std::move(metricArgs));
514     }
515 
516     return true;
517 }
518 
519 inline bool getUserParameters(crow::Response& res, const crow::Request& req,
520                               AddReportArgs& args)
521 {
522     std::optional<std::string> id;
523     std::optional<std::string> name;
524     std::optional<std::string> reportingTypeStr;
525     std::optional<std::string> reportUpdatesStr;
526     std::optional<uint64_t> appendLimit;
527     std::optional<bool> metricReportDefinitionEnabled;
528     std::optional<std::vector<nlohmann::json::object_t>> metrics;
529     std::optional<std::vector<std::string>> reportActionsStr;
530     std::optional<std::string> scheduleDurationStr;
531 
532     if (!json_util::readJsonPatch(
533             req, res, "Id", id, "Name", name, "Metrics", metrics,
534             "MetricReportDefinitionType", reportingTypeStr, "ReportUpdates",
535             reportUpdatesStr, "AppendLimit", appendLimit, "ReportActions",
536             reportActionsStr, "Schedule/RecurrenceInterval",
537             scheduleDurationStr, "MetricReportDefinitionEnabled",
538             metricReportDefinitionEnabled))
539     {
540         return false;
541     }
542 
543     if (id)
544     {
545         constexpr const char* allowedCharactersInId =
546             "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
547         if (id->empty() ||
548             id->find_first_not_of(allowedCharactersInId) != std::string::npos)
549         {
550             messages::propertyValueIncorrect(res, "Id", *id);
551             return false;
552         }
553         args.id = *id;
554     }
555 
556     if (name)
557     {
558         args.name = *name;
559     }
560 
561     if (reportingTypeStr)
562     {
563         std::string dbusReportingType = toDbusReportingType(*reportingTypeStr);
564         if (dbusReportingType.empty())
565         {
566             messages::propertyValueNotInList(res, *reportingTypeStr,
567                                              "MetricReportDefinitionType");
568             return false;
569         }
570         args.reportingType = dbusReportingType;
571     }
572 
573     if (reportUpdatesStr)
574     {
575         std::string dbusReportUpdates = toDbusReportUpdates(*reportUpdatesStr);
576         if (dbusReportUpdates.empty())
577         {
578             messages::propertyValueNotInList(res, *reportUpdatesStr,
579                                              "ReportUpdates");
580             return false;
581         }
582         args.reportUpdates = dbusReportUpdates;
583     }
584 
585     if (appendLimit)
586     {
587         args.appendLimit = *appendLimit;
588     }
589 
590     if (metricReportDefinitionEnabled)
591     {
592         args.metricReportDefinitionEnabled = *metricReportDefinitionEnabled;
593     }
594 
595     if (reportActionsStr)
596     {
597         if (!toDbusReportActions(res, *reportActionsStr, args.reportActions))
598         {
599             return false;
600         }
601     }
602 
603     if (reportingTypeStr == "Periodic")
604     {
605         if (!scheduleDurationStr)
606         {
607             messages::createFailedMissingReqProperties(res, "Schedule");
608             return false;
609         }
610 
611         std::optional<std::chrono::milliseconds> durationNum =
612             time_utils::fromDurationString(*scheduleDurationStr);
613         if (!durationNum || durationNum->count() < 0)
614         {
615             messages::propertyValueIncorrect(res, "RecurrenceInterval",
616                                              *scheduleDurationStr);
617             return false;
618         }
619         args.interval = static_cast<uint64_t>(durationNum->count());
620     }
621 
622     if (metrics)
623     {
624         if (!getUserMetrics(res, *metrics, args.metrics))
625         {
626             return false;
627         }
628     }
629 
630     return true;
631 }
632 
633 inline bool getChassisSensorNodeFromMetrics(
634     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
635     std::span<const AddReportArgs::MetricArgs> metrics,
636     boost::container::flat_set<std::pair<std::string, std::string>>& matched)
637 {
638     for (const auto& metric : metrics)
639     {
640         std::optional<IncorrectMetricUri> error =
641             getChassisSensorNode(metric.uris, matched);
642         if (error)
643         {
644             messages::propertyValueIncorrect(
645                 asyncResp->res, error->uri,
646                 "MetricProperties/" + std::to_string(error->index));
647             return false;
648         }
649     }
650     return true;
651 }
652 
653 class AddReport
654 {
655   public:
656     AddReport(AddReportArgs&& argsIn,
657               const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
658         asyncResp(asyncRespIn), args(std::move(argsIn))
659     {}
660 
661     ~AddReport()
662     {
663         boost::asio::post(crow::connections::systemBus->get_io_context(),
664                           std::bind_front(&performAddReport, asyncResp, args,
665                                           std::move(uriToDbus)));
666     }
667 
668     static void performAddReport(
669         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
670         const AddReportArgs& args,
671         const boost::container::flat_map<std::string, std::string>& uriToDbus)
672     {
673         if (asyncResp->res.result() != boost::beast::http::status::ok)
674         {
675             return;
676         }
677 
678         telemetry::ReadingParameters readingParams;
679         readingParams.reserve(args.metrics.size());
680 
681         for (const auto& metric : args.metrics)
682         {
683             std::vector<
684                 std::tuple<sdbusplus::message::object_path, std::string>>
685                 sensorParams;
686             sensorParams.reserve(metric.uris.size());
687 
688             for (size_t i = 0; i < metric.uris.size(); i++)
689             {
690                 const std::string& uri = metric.uris[i];
691                 auto el = uriToDbus.find(uri);
692                 if (el == uriToDbus.end())
693                 {
694                     BMCWEB_LOG_ERROR(
695                         "Failed to find DBus sensor corresponding to URI {}",
696                         uri);
697                     messages::propertyValueNotInList(
698                         asyncResp->res, uri,
699                         "MetricProperties/" + std::to_string(i));
700                     return;
701                 }
702 
703                 const std::string& dbusPath = el->second;
704                 sensorParams.emplace_back(dbusPath, uri);
705             }
706 
707             readingParams.emplace_back(
708                 std::move(sensorParams), metric.collectionFunction,
709                 metric.collectionTimeScope, metric.collectionDuration);
710         }
711 
712         crow::connections::systemBus->async_method_call(
713             [asyncResp, id = args.id, uriToDbus](
714                 const boost::system::error_code& ec, const std::string&) {
715                 if (ec == boost::system::errc::file_exists)
716                 {
717                     messages::resourceAlreadyExists(
718                         asyncResp->res, "MetricReportDefinition", "Id", id);
719                     return;
720                 }
721                 if (ec == boost::system::errc::too_many_files_open)
722                 {
723                     messages::createLimitReachedForResource(asyncResp->res);
724                     return;
725                 }
726                 if (ec == boost::system::errc::argument_list_too_long)
727                 {
728                     nlohmann::json metricProperties = nlohmann::json::array();
729                     for (const auto& [uri, _] : uriToDbus)
730                     {
731                         metricProperties.emplace_back(uri);
732                     }
733                     messages::propertyValueIncorrect(
734                         asyncResp->res, "MetricProperties", metricProperties);
735                     return;
736                 }
737                 if (ec)
738                 {
739                     messages::internalError(asyncResp->res);
740                     BMCWEB_LOG_ERROR("respHandler DBus error {}", ec);
741                     return;
742                 }
743 
744                 messages::created(asyncResp->res);
745             },
746             telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
747             "xyz.openbmc_project.Telemetry.ReportManager", "AddReport",
748             "TelemetryService/" + args.id, args.name, args.reportingType,
749             args.reportUpdates, args.appendLimit, args.reportActions,
750             args.interval, readingParams, args.metricReportDefinitionEnabled);
751     }
752 
753     AddReport(const AddReport&) = delete;
754     AddReport(AddReport&&) = delete;
755     AddReport& operator=(const AddReport&) = delete;
756     AddReport& operator=(AddReport&&) = delete;
757 
758     void insert(const std::map<std::string, std::string>& el)
759     {
760         uriToDbus.insert(el.begin(), el.end());
761     }
762 
763   private:
764     std::shared_ptr<bmcweb::AsyncResp> asyncResp;
765     AddReportArgs args;
766     boost::container::flat_map<std::string, std::string> uriToDbus;
767 };
768 
769 class UpdateMetrics
770 {
771   public:
772     UpdateMetrics(std::string_view idIn,
773                   const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
774         id(idIn), asyncResp(asyncRespIn)
775     {}
776 
777     ~UpdateMetrics()
778     {
779         try
780         {
781             setReadingParams();
782         }
783         catch (const std::exception& e)
784         {
785             BMCWEB_LOG_ERROR("{}", e.what());
786         }
787         catch (...)
788         {
789             BMCWEB_LOG_ERROR("Unknown error");
790         }
791     }
792 
793     UpdateMetrics(const UpdateMetrics&) = delete;
794     UpdateMetrics(UpdateMetrics&&) = delete;
795     UpdateMetrics& operator=(const UpdateMetrics&) = delete;
796     UpdateMetrics& operator=(UpdateMetrics&&) = delete;
797 
798     std::string id;
799     std::map<std::string, std::string> metricPropertyToDbusPaths;
800 
801     void insert(const std::map<std::string, std::string>&
802                     additionalMetricPropertyToDbusPaths)
803     {
804         metricPropertyToDbusPaths.insert(
805             additionalMetricPropertyToDbusPaths.begin(),
806             additionalMetricPropertyToDbusPaths.end());
807     }
808 
809     void emplace(
810         std::span<
811             const std::tuple<sdbusplus::message::object_path, std::string>>
812             pathAndUri,
813         const AddReportArgs::MetricArgs& metricArgs)
814     {
815         readingParamsUris.emplace_back(metricArgs.uris);
816         readingParams.emplace_back(
817             std::vector(pathAndUri.begin(), pathAndUri.end()),
818             metricArgs.collectionFunction, metricArgs.collectionTimeScope,
819             metricArgs.collectionDuration);
820     }
821 
822     void setReadingParams()
823     {
824         if (asyncResp->res.result() != boost::beast::http::status::ok)
825         {
826             return;
827         }
828 
829         for (size_t index = 0; index < readingParamsUris.size(); ++index)
830         {
831             std::span<const std::string> newUris = readingParamsUris[index];
832 
833             const std::optional<std::vector<
834                 std::tuple<sdbusplus::message::object_path, std::string>>>
835                 readingParam = sensorPathToUri(newUris);
836 
837             if (!readingParam)
838             {
839                 return;
840             }
841 
842             std::get<0>(readingParams[index]) = *readingParam;
843         }
844 
845         crow::connections::systemBus->async_method_call(
846             [asyncResp(this->asyncResp),
847              reportId = id](const boost::system::error_code& ec) {
848                 if (!verifyCommonErrors(asyncResp->res, reportId, ec))
849                 {
850                     return;
851                 }
852             },
853             "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
854             "org.freedesktop.DBus.Properties", "Set",
855             "xyz.openbmc_project.Telemetry.Report", "ReadingParameters",
856             dbus::utility::DbusVariantType{readingParams});
857     }
858 
859   private:
860     std::optional<
861         std::vector<std::tuple<sdbusplus::message::object_path, std::string>>>
862         sensorPathToUri(std::span<const std::string> uris) const
863     {
864         std::vector<std::tuple<sdbusplus::message::object_path, std::string>>
865             result;
866 
867         for (const std::string& uri : uris)
868         {
869             auto it = metricPropertyToDbusPaths.find(uri);
870             if (it == metricPropertyToDbusPaths.end())
871             {
872                 messages::propertyValueNotInList(asyncResp->res, uri,
873                                                  "MetricProperties");
874                 return {};
875             }
876             result.emplace_back(it->second, uri);
877         }
878 
879         return result;
880     }
881 
882     const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
883     std::vector<std::vector<std::string>> readingParamsUris;
884     ReadingParameters readingParams;
885 };
886 
887 inline void
888     setReportEnabled(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
889                      std::string_view id, bool enabled)
890 {
891     crow::connections::systemBus->async_method_call(
892         [asyncResp, id = std::string(id)](const boost::system::error_code& ec) {
893             if (!verifyCommonErrors(asyncResp->res, id, ec))
894             {
895                 return;
896             }
897         },
898         "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
899         "org.freedesktop.DBus.Properties", "Set",
900         "xyz.openbmc_project.Telemetry.Report", "Enabled",
901         dbus::utility::DbusVariantType{enabled});
902 }
903 
904 inline void setReportTypeAndInterval(
905     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
906     const std::string& reportingType, uint64_t recurrenceInterval)
907 {
908     crow::connections::systemBus->async_method_call(
909         [asyncResp, id = std::string(id)](const boost::system::error_code& ec) {
910             if (!verifyCommonErrors(asyncResp->res, id, ec))
911             {
912                 return;
913             }
914         },
915         "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
916         "xyz.openbmc_project.Telemetry.Report", "SetReportingProperties",
917         reportingType, recurrenceInterval);
918 }
919 
920 inline void
921     setReportUpdates(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
922                      std::string_view id, const std::string& reportUpdates)
923 {
924     crow::connections::systemBus->async_method_call(
925         [asyncResp, id = std::string(id)](const boost::system::error_code& ec) {
926             if (!verifyCommonErrors(asyncResp->res, id, ec))
927             {
928                 return;
929             }
930         },
931         "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
932         "org.freedesktop.DBus.Properties", "Set",
933         "xyz.openbmc_project.Telemetry.Report", "ReportUpdates",
934         dbus::utility::DbusVariantType{reportUpdates});
935 }
936 
937 inline void setReportActions(
938     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
939     const std::vector<std::string>& dbusReportActions)
940 {
941     crow::connections::systemBus->async_method_call(
942         [asyncResp, id = std::string(id)](const boost::system::error_code& ec) {
943             if (!verifyCommonErrors(asyncResp->res, id, ec))
944             {
945                 return;
946             }
947         },
948         "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
949         "org.freedesktop.DBus.Properties", "Set",
950         "xyz.openbmc_project.Telemetry.Report", "ReportActions",
951         dbus::utility::DbusVariantType{dbusReportActions});
952 }
953 
954 inline void setReportMetrics(
955     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
956     std::span<nlohmann::json::object_t> metrics)
957 {
958     sdbusplus::asio::getAllProperties(
959         *crow::connections::systemBus, telemetry::service,
960         telemetry::getDbusReportPath(id), telemetry::reportInterface,
961         [asyncResp, id = std::string(id),
962          redfishMetrics = std::vector<nlohmann::json::object_t>(
963              metrics.begin(), metrics.end())](
964             boost::system::error_code ec,
965             const dbus::utility::DBusPropertiesMap& properties) mutable {
966             if (!redfish::telemetry::verifyCommonErrors(asyncResp->res, id, ec))
967             {
968                 return;
969             }
970 
971             ReadingParameters readingParams;
972 
973             const bool success = sdbusplus::unpackPropertiesNoThrow(
974                 dbus_utils::UnpackErrorPrinter(), properties,
975                 "ReadingParameters", readingParams);
976 
977             if (!success)
978             {
979                 messages::internalError(asyncResp->res);
980                 return;
981             }
982 
983             auto updateMetricsReq =
984                 std::make_shared<UpdateMetrics>(id, asyncResp);
985 
986             boost::container::flat_set<std::pair<std::string, std::string>>
987                 chassisSensors;
988 
989             size_t index = 0;
990             for (nlohmann::json::object_t& metric : redfishMetrics)
991             {
992                 AddReportArgs::MetricArgs metricArgs;
993                 std::vector<
994                     std::tuple<sdbusplus::message::object_path, std::string>>
995                     pathAndUri;
996 
997                 if (index < readingParams.size())
998                 {
999                     const ReadingParameters::value_type& existing =
1000                         readingParams[index];
1001 
1002                     pathAndUri = std::get<0>(existing);
1003                     metricArgs.collectionFunction = std::get<1>(existing);
1004                     metricArgs.collectionTimeScope = std::get<2>(existing);
1005                     metricArgs.collectionDuration = std::get<3>(existing);
1006                 }
1007 
1008                 if (!getUserMetric(asyncResp->res, metric, metricArgs))
1009                 {
1010                     return;
1011                 }
1012 
1013                 std::optional<IncorrectMetricUri> error =
1014                     getChassisSensorNode(metricArgs.uris, chassisSensors);
1015 
1016                 if (error)
1017                 {
1018                     messages::propertyValueIncorrect(
1019                         asyncResp->res, error->uri,
1020                         "MetricProperties/" + std::to_string(error->index));
1021                     return;
1022                 }
1023 
1024                 updateMetricsReq->emplace(pathAndUri, metricArgs);
1025                 index++;
1026             }
1027 
1028             for (const auto& [chassis, sensorType] : chassisSensors)
1029             {
1030                 retrieveUriToDbusMap(
1031                     chassis, sensorType,
1032                     [asyncResp, updateMetricsReq](
1033                         const boost::beast::http::status status,
1034                         const std::map<std::string, std::string>& uriToDbus) {
1035                         if (status != boost::beast::http::status::ok)
1036                         {
1037                             BMCWEB_LOG_ERROR(
1038                                 "Failed to retrieve URI to dbus sensors map with err {}",
1039                                 static_cast<unsigned>(status));
1040                             return;
1041                         }
1042                         updateMetricsReq->insert(uriToDbus);
1043                     });
1044             }
1045         });
1046 }
1047 
1048 inline void handleMetricReportDefinitionCollectionHead(
1049     App& app, const crow::Request& req,
1050     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1051 {
1052     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1053     {
1054         return;
1055     }
1056     asyncResp->res.addHeader(
1057         boost::beast::http::field::link,
1058         "</redfish/v1/JsonSchemas/MetricReportDefinitionCollection/MetricReportDefinitionCollection.json>; rel=describedby");
1059 }
1060 
1061 inline void handleMetricReportDefinitionCollectionGet(
1062     App& app, const crow::Request& req,
1063     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1064 {
1065     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1066     {
1067         return;
1068     }
1069     asyncResp->res.addHeader(
1070         boost::beast::http::field::link,
1071         "</redfish/v1/JsonSchemas/MetricReportDefinition/MetricReportDefinition.json>; rel=describedby");
1072 
1073     asyncResp->res.jsonValue["@odata.type"] =
1074         "#MetricReportDefinitionCollection."
1075         "MetricReportDefinitionCollection";
1076     asyncResp->res.jsonValue["@odata.id"] =
1077         "/redfish/v1/TelemetryService/MetricReportDefinitions";
1078     asyncResp->res.jsonValue["Name"] = "Metric Definition Collection";
1079     constexpr std::array<std::string_view, 1> interfaces{
1080         telemetry::reportInterface};
1081     collection_util::getCollectionMembers(
1082         asyncResp,
1083         boost::urls::url(
1084             "/redfish/v1/TelemetryService/MetricReportDefinitions"),
1085         interfaces, "/xyz/openbmc_project/Telemetry/Reports/TelemetryService");
1086 }
1087 
1088 inline void handleReportPatch(
1089     App& app, const crow::Request& req,
1090     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id)
1091 {
1092     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1093     {
1094         return;
1095     }
1096 
1097     std::optional<std::string> reportingTypeStr;
1098     std::optional<std::string> reportUpdatesStr;
1099     std::optional<bool> metricReportDefinitionEnabled;
1100     std::optional<std::vector<nlohmann::json::object_t>> metrics;
1101     std::optional<std::vector<std::string>> reportActionsStr;
1102     std::optional<std::string> scheduleDurationStr;
1103 
1104     if (!json_util::readJsonPatch(
1105             req, asyncResp->res, "Metrics", metrics,
1106             "MetricReportDefinitionType", reportingTypeStr, "ReportUpdates",
1107             reportUpdatesStr, "ReportActions", reportActionsStr,
1108             "Schedule/RecurrenceInterval", scheduleDurationStr,
1109             "MetricReportDefinitionEnabled", metricReportDefinitionEnabled))
1110     {
1111         return;
1112     }
1113 
1114     if (metricReportDefinitionEnabled)
1115     {
1116         setReportEnabled(asyncResp, id, *metricReportDefinitionEnabled);
1117     }
1118 
1119     if (reportUpdatesStr)
1120     {
1121         std::string dbusReportUpdates = toDbusReportUpdates(*reportUpdatesStr);
1122         if (dbusReportUpdates.empty())
1123         {
1124             messages::propertyValueNotInList(asyncResp->res, *reportUpdatesStr,
1125                                              "ReportUpdates");
1126             return;
1127         }
1128         setReportUpdates(asyncResp, id, dbusReportUpdates);
1129     }
1130 
1131     if (reportActionsStr)
1132     {
1133         std::vector<std::string> dbusReportActions;
1134         if (!toDbusReportActions(asyncResp->res, *reportActionsStr,
1135                                  dbusReportActions))
1136         {
1137             return;
1138         }
1139         setReportActions(asyncResp, id, dbusReportActions);
1140     }
1141 
1142     if (reportingTypeStr || scheduleDurationStr)
1143     {
1144         std::string dbusReportingType;
1145         if (reportingTypeStr)
1146         {
1147             dbusReportingType = toDbusReportingType(*reportingTypeStr);
1148             if (dbusReportingType.empty())
1149             {
1150                 messages::propertyValueNotInList(asyncResp->res,
1151                                                  *reportingTypeStr,
1152                                                  "MetricReportDefinitionType");
1153                 return;
1154             }
1155         }
1156 
1157         uint64_t recurrenceInterval = std::numeric_limits<uint64_t>::max();
1158         if (scheduleDurationStr)
1159         {
1160             std::optional<std::chrono::milliseconds> durationNum =
1161                 time_utils::fromDurationString(*scheduleDurationStr);
1162             if (!durationNum || durationNum->count() < 0)
1163             {
1164                 messages::propertyValueIncorrect(
1165                     asyncResp->res, "RecurrenceInterval", *scheduleDurationStr);
1166                 return;
1167             }
1168 
1169             recurrenceInterval = static_cast<uint64_t>(durationNum->count());
1170         }
1171 
1172         setReportTypeAndInterval(asyncResp, id, dbusReportingType,
1173                                  recurrenceInterval);
1174     }
1175 
1176     if (metrics)
1177     {
1178         setReportMetrics(asyncResp, id, *metrics);
1179     }
1180 }
1181 
1182 inline void handleReportDelete(
1183     App& app, const crow::Request& req,
1184     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id)
1185 {
1186     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1187     {
1188         return;
1189     }
1190 
1191     const std::string reportPath = getDbusReportPath(id);
1192 
1193     crow::connections::systemBus->async_method_call(
1194         [asyncResp,
1195          reportId = std::string(id)](const boost::system::error_code& ec) {
1196             if (!verifyCommonErrors(asyncResp->res, reportId, ec))
1197             {
1198                 return;
1199             }
1200             asyncResp->res.result(boost::beast::http::status::no_content);
1201         },
1202         service, reportPath, "xyz.openbmc_project.Object.Delete", "Delete");
1203 }
1204 } // namespace telemetry
1205 
1206 inline void afterRetrieveUriToDbusMap(
1207     const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
1208     const std::shared_ptr<telemetry::AddReport>& addReportReq,
1209     const boost::beast::http::status status,
1210     const std::map<std::string, std::string>& uriToDbus)
1211 {
1212     if (status != boost::beast::http::status::ok)
1213     {
1214         BMCWEB_LOG_ERROR(
1215             "Failed to retrieve URI to dbus sensors map with err {}",
1216             static_cast<unsigned>(status));
1217         return;
1218     }
1219     addReportReq->insert(uriToDbus);
1220 }
1221 
1222 inline void handleMetricReportDefinitionsPost(
1223     App& app, const crow::Request& req,
1224     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1225 {
1226     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1227     {
1228         return;
1229     }
1230 
1231     telemetry::AddReportArgs args;
1232     if (!telemetry::getUserParameters(asyncResp->res, req, args))
1233     {
1234         return;
1235     }
1236 
1237     boost::container::flat_set<std::pair<std::string, std::string>>
1238         chassisSensors;
1239     if (!telemetry::getChassisSensorNodeFromMetrics(asyncResp, args.metrics,
1240                                                     chassisSensors))
1241     {
1242         return;
1243     }
1244 
1245     auto addReportReq =
1246         std::make_shared<telemetry::AddReport>(std::move(args), asyncResp);
1247     for (const auto& [chassis, sensorType] : chassisSensors)
1248     {
1249         retrieveUriToDbusMap(chassis, sensorType,
1250                              std::bind_front(afterRetrieveUriToDbusMap,
1251                                              asyncResp, addReportReq));
1252     }
1253 }
1254 
1255 inline void
1256     handleMetricReportHead(App& app, const crow::Request& req,
1257                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1258                            const std::string& /*id*/)
1259 {
1260     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1261     {
1262         return;
1263     }
1264     asyncResp->res.addHeader(
1265         boost::beast::http::field::link,
1266         "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby");
1267 }
1268 
1269 inline void handleMetricReportGet(
1270     App& app, const crow::Request& req,
1271     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1272 {
1273     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1274     {
1275         return;
1276     }
1277     asyncResp->res.addHeader(
1278         boost::beast::http::field::link,
1279         "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby");
1280 
1281     sdbusplus::asio::getAllProperties(
1282         *crow::connections::systemBus, telemetry::service,
1283         telemetry::getDbusReportPath(id), telemetry::reportInterface,
1284         [asyncResp, id](const boost::system::error_code& ec,
1285                         const dbus::utility::DBusPropertiesMap& properties) {
1286             if (!redfish::telemetry::verifyCommonErrors(asyncResp->res, id, ec))
1287             {
1288                 return;
1289             }
1290 
1291             telemetry::fillReportDefinition(asyncResp, id, properties);
1292         });
1293 }
1294 
1295 inline void handleMetricReportDelete(
1296     App& app, const crow::Request& req,
1297     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1298 
1299 {
1300     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1301     {
1302         return;
1303     }
1304 
1305     const std::string reportPath = telemetry::getDbusReportPath(id);
1306 
1307     crow::connections::systemBus->async_method_call(
1308         [asyncResp, id](const boost::system::error_code& ec) {
1309             /*
1310              * boost::system::errc and std::errc are missing value
1311              * for EBADR error that is defined in Linux.
1312              */
1313             if (ec.value() == EBADR)
1314             {
1315                 messages::resourceNotFound(asyncResp->res,
1316                                            "MetricReportDefinition", id);
1317                 return;
1318             }
1319 
1320             if (ec)
1321             {
1322                 BMCWEB_LOG_ERROR("respHandler DBus error {}", ec);
1323                 messages::internalError(asyncResp->res);
1324                 return;
1325             }
1326 
1327             asyncResp->res.result(boost::beast::http::status::no_content);
1328         },
1329         telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete",
1330         "Delete");
1331 }
1332 
1333 inline void requestRoutesMetricReportDefinitionCollection(App& app)
1334 {
1335     BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1336         .privileges(redfish::privileges::headMetricReportDefinitionCollection)
1337         .methods(boost::beast::http::verb::head)(std::bind_front(
1338             telemetry::handleMetricReportDefinitionCollectionHead,
1339             std::ref(app)));
1340 
1341     BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1342         .privileges(redfish::privileges::getMetricReportDefinitionCollection)
1343         .methods(boost::beast::http::verb::get)(std::bind_front(
1344             telemetry::handleMetricReportDefinitionCollectionGet,
1345             std::ref(app)));
1346 
1347     BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1348         .privileges(redfish::privileges::postMetricReportDefinitionCollection)
1349         .methods(boost::beast::http::verb::post)(
1350             std::bind_front(handleMetricReportDefinitionsPost, std::ref(app)));
1351 }
1352 
1353 inline void requestRoutesMetricReportDefinition(App& app)
1354 {
1355     BMCWEB_ROUTE(app,
1356                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1357         .privileges(redfish::privileges::getMetricReportDefinition)
1358         .methods(boost::beast::http::verb::head)(
1359             std::bind_front(handleMetricReportHead, std::ref(app)));
1360 
1361     BMCWEB_ROUTE(app,
1362                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1363         .privileges(redfish::privileges::getMetricReportDefinition)
1364         .methods(boost::beast::http::verb::get)(
1365             std::bind_front(handleMetricReportGet, std::ref(app)));
1366 
1367     BMCWEB_ROUTE(app,
1368                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1369         .privileges(redfish::privileges::deleteMetricReportDefinition)
1370         .methods(boost::beast::http::verb::delete_)(
1371             std::bind_front(handleMetricReportDelete, std::ref(app)));
1372 
1373     BMCWEB_ROUTE(app,
1374                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1375         .privileges(redfish::privileges::patchMetricReportDefinition)
1376         .methods(boost::beast::http::verb::patch)(
1377             std::bind_front(telemetry::handleReportPatch, std::ref(app)));
1378 }
1379 } // namespace redfish
1380