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 
verifyCommonErrors(crow::Response & res,const std::string & id,const boost::system::error_code & ec)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
toRedfishReportAction(std::string_view dbusValue)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 
toDbusReportAction(std::string_view redfishValue)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
toRedfishReportingType(std::string_view dbusValue)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 
toDbusReportingType(std::string_view redfishValue)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
toRedfishCollectionTimeScope(std::string_view dbusValue)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 
toDbusCollectionTimeScope(std::string_view redfishValue)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
toRedfishReportUpdates(std::string_view dbusValue)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 
toDbusReportUpdates(std::string_view redfishValue)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 
getLinkedTriggers(std::span<const sdbusplus::message::object_path> triggerPaths)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 
fillReportDefinition(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id,const dbus::utility::DBusPropertiesMap & properties)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 
toDbusReportActions(crow::Response & res,const std::vector<std::string> & actions,std::vector<std::string> & outReportActions)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 
getUserMetric(crow::Response & res,nlohmann::json::object_t & metric,AddReportArgs::MetricArgs & metricArgs)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 
getUserMetrics(crow::Response & res,std::span<nlohmann::json::object_t> metrics,std::vector<AddReportArgs::MetricArgs> & result)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 
getUserParameters(crow::Response & res,const crow::Request & req,AddReportArgs & args)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 
getChassisSensorNodeFromMetrics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::span<const AddReportArgs::MetricArgs> metrics,boost::container::flat_set<std::pair<std::string,std::string>> & matched)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 
toRedfishProperty(std::string_view dbusMessage)653 inline std::string toRedfishProperty(std::string_view dbusMessage)
654 {
655     if (dbusMessage == "Id")
656     {
657         return "Id";
658     }
659     if (dbusMessage == "Name")
660     {
661         return "Name";
662     }
663     if (dbusMessage == "ReportingType")
664     {
665         return "MetricReportDefinitionType";
666     }
667     if (dbusMessage == "AppendLimit")
668     {
669         return "AppendLimit";
670     }
671     if (dbusMessage == "ReportActions")
672     {
673         return "ReportActions";
674     }
675     if (dbusMessage == "Interval")
676     {
677         return "RecurrenceInterval";
678     }
679     if (dbusMessage == "ReportUpdates")
680     {
681         return "ReportUpdates";
682     }
683     if (dbusMessage == "ReadingParameters")
684     {
685         return "Metrics";
686     }
687     return "";
688 }
689 
handleParamError(crow::Response & res,const char * errorMessage,std::string_view key)690 inline bool handleParamError(crow::Response& res, const char* errorMessage,
691                              std::string_view key)
692 {
693     if (errorMessage == nullptr)
694     {
695         BMCWEB_LOG_ERROR("errorMessage was null");
696         return true;
697     }
698     std::string_view errorMessageSv(errorMessage);
699     if (errorMessageSv.starts_with(key))
700     {
701         std::string redfishProperty = toRedfishProperty(key);
702         if (redfishProperty.empty())
703         {
704             // Getting here means most possibly that toRedfishProperty has
705             // incomplete implementation. Return internal error for now.
706             BMCWEB_LOG_ERROR("{} has no corresponding Redfish property", key);
707             messages::internalError(res);
708             return false;
709         }
710         messages::propertyValueError(res, redfishProperty);
711         return false;
712     }
713 
714     return true;
715 }
716 
afterAddReport(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const AddReportArgs & args,const boost::system::error_code & ec,const sdbusplus::message_t & msg)717 inline void afterAddReport(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
718                            const AddReportArgs& args,
719                            const boost::system::error_code& ec,
720                            const sdbusplus::message_t& msg)
721 {
722     if (!ec)
723     {
724         messages::created(asyncResp->res);
725         return;
726     }
727 
728     if (ec == boost::system::errc::invalid_argument)
729     {
730         const sd_bus_error* errorMessage = msg.get_error();
731         if (errorMessage != nullptr)
732         {
733             for (const auto& arg :
734                  {"Id", "Name", "ReportingType", "AppendLimit", "ReportActions",
735                   "Interval", "ReportUpdates", "ReadingParameters"})
736             {
737                 if (!handleParamError(asyncResp->res, errorMessage->message,
738                                       arg))
739                 {
740                     return;
741                 }
742             }
743         }
744     }
745     if (!verifyCommonErrors(asyncResp->res, args.id, ec))
746     {
747         return;
748     }
749     messages::internalError(asyncResp->res);
750 }
751 
752 class AddReport
753 {
754   public:
AddReport(AddReportArgs && argsIn,const std::shared_ptr<bmcweb::AsyncResp> & asyncRespIn)755     AddReport(AddReportArgs&& argsIn,
756               const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
757         asyncResp(asyncRespIn), args(std::move(argsIn))
758     {}
759 
~AddReport()760     ~AddReport()
761     {
762         boost::asio::post(crow::connections::systemBus->get_io_context(),
763                           std::bind_front(&performAddReport, asyncResp, args,
764                                           std::move(uriToDbus)));
765     }
766 
performAddReport(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const AddReportArgs & args,const boost::container::flat_map<std::string,std::string> & uriToDbus)767     static void performAddReport(
768         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
769         const AddReportArgs& args,
770         const boost::container::flat_map<std::string, std::string>& uriToDbus)
771     {
772         if (asyncResp->res.result() != boost::beast::http::status::ok)
773         {
774             return;
775         }
776 
777         telemetry::ReadingParameters readingParams;
778         readingParams.reserve(args.metrics.size());
779 
780         for (const auto& metric : args.metrics)
781         {
782             std::vector<
783                 std::tuple<sdbusplus::message::object_path, std::string>>
784                 sensorParams;
785             sensorParams.reserve(metric.uris.size());
786 
787             for (size_t i = 0; i < metric.uris.size(); i++)
788             {
789                 const std::string& uri = metric.uris[i];
790                 auto el = uriToDbus.find(uri);
791                 if (el == uriToDbus.end())
792                 {
793                     BMCWEB_LOG_ERROR(
794                         "Failed to find DBus sensor corresponding to URI {}",
795                         uri);
796                     messages::propertyValueNotInList(
797                         asyncResp->res, uri,
798                         "MetricProperties/" + std::to_string(i));
799                     return;
800                 }
801 
802                 const std::string& dbusPath = el->second;
803                 sensorParams.emplace_back(dbusPath, uri);
804             }
805 
806             readingParams.emplace_back(
807                 std::move(sensorParams), metric.collectionFunction,
808                 metric.collectionTimeScope, metric.collectionDuration);
809         }
810         crow::connections::systemBus->async_method_call(
811             [asyncResp, args](const boost::system::error_code& ec,
812                               const sdbusplus::message_t& msg,
813                               const std::string& /*arg1*/) {
814                 afterAddReport(asyncResp, args, ec, msg);
815             },
816             telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
817             "xyz.openbmc_project.Telemetry.ReportManager", "AddReport",
818             "TelemetryService/" + args.id, args.name, args.reportingType,
819             args.reportUpdates, args.appendLimit, args.reportActions,
820             args.interval, readingParams, args.metricReportDefinitionEnabled);
821     }
822 
823     AddReport(const AddReport&) = delete;
824     AddReport(AddReport&&) = delete;
825     AddReport& operator=(const AddReport&) = delete;
826     AddReport& operator=(AddReport&&) = delete;
827 
insert(const std::map<std::string,std::string> & el)828     void insert(const std::map<std::string, std::string>& el)
829     {
830         uriToDbus.insert(el.begin(), el.end());
831     }
832 
833   private:
834     const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
835     AddReportArgs args;
836     boost::container::flat_map<std::string, std::string> uriToDbus;
837 };
838 
839 inline std::optional<
840     std::vector<std::tuple<sdbusplus::message::object_path, std::string>>>
sensorPathToUri(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::span<const std::string> uris,const std::map<std::string,std::string> & metricPropertyToDbusPaths)841     sensorPathToUri(
842         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
843         std::span<const std::string> uris,
844         const std::map<std::string, std::string>& metricPropertyToDbusPaths)
845 {
846     std::vector<std::tuple<sdbusplus::message::object_path, std::string>>
847         result;
848 
849     for (const std::string& uri : uris)
850     {
851         auto it = metricPropertyToDbusPaths.find(uri);
852         if (it == metricPropertyToDbusPaths.end())
853         {
854             messages::propertyValueNotInList(asyncResp->res, uri,
855                                              "MetricProperties");
856             return {};
857         }
858         result.emplace_back(it->second, uri);
859     }
860 
861     return result;
862 }
863 
afterSetReadingParams(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & reportId,const boost::system::error_code & ec,const sdbusplus::message_t & msg)864 inline void afterSetReadingParams(
865     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
866     const std::string& reportId, const boost::system::error_code& ec,
867     const sdbusplus::message_t& msg)
868 {
869     if (!ec)
870     {
871         messages::success(asyncResp->res);
872         return;
873     }
874     if (ec == boost::system::errc::invalid_argument)
875     {
876         const sd_bus_error* errorMessage = msg.get_error();
877         if (errorMessage != nullptr)
878         {
879             for (const auto& arg : {"Id", "ReadingParameters"})
880             {
881                 if (!handleParamError(asyncResp->res, errorMessage->message,
882                                       arg))
883                 {
884                     return;
885                 }
886             }
887         }
888     }
889     if (!verifyCommonErrors(asyncResp->res, reportId, ec))
890     {
891         return;
892     }
893     messages::internalError(asyncResp->res);
894 }
895 
setReadingParams(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & reportId,ReadingParameters readingParams,const std::vector<std::vector<std::string>> & readingParamsUris,const std::map<std::string,std::string> & metricPropertyToDbusPaths)896 inline void setReadingParams(
897     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
898     const std::string& reportId, ReadingParameters readingParams,
899     const std::vector<std::vector<std::string>>& readingParamsUris,
900     const std::map<std::string, std::string>& metricPropertyToDbusPaths)
901 {
902     if (asyncResp->res.result() != boost::beast::http::status::ok)
903     {
904         return;
905     }
906 
907     for (size_t index = 0; index < readingParamsUris.size(); ++index)
908     {
909         std::span<const std::string> newUris = readingParamsUris[index];
910 
911         const std::optional<std::vector<
912             std::tuple<sdbusplus::message::object_path, std::string>>>
913             readingParam =
914                 sensorPathToUri(asyncResp, newUris, metricPropertyToDbusPaths);
915 
916         if (!readingParam)
917         {
918             return;
919         }
920 
921         for (const std::tuple<sdbusplus::message::object_path, std::string>&
922                  value : *readingParam)
923         {
924             std::get<0>(readingParams[index]).emplace_back(value);
925         }
926     }
927 
928     crow::connections::systemBus->async_method_call(
929         [asyncResp, reportId](const boost::system::error_code& ec,
930                               const sdbusplus::message_t& msg) {
931             afterSetReadingParams(asyncResp, reportId, ec, msg);
932         },
933         "xyz.openbmc_project.Telemetry", getDbusReportPath(reportId),
934         "org.freedesktop.DBus.Properties", "Set",
935         "xyz.openbmc_project.Telemetry.Report", "ReadingParameters",
936         dbus::utility::DbusVariantType{readingParams});
937 }
938 
939 class UpdateMetrics
940 {
941   public:
UpdateMetrics(std::string_view idIn,const std::shared_ptr<bmcweb::AsyncResp> & asyncRespIn)942     UpdateMetrics(std::string_view idIn,
943                   const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
944         id(idIn), asyncResp(asyncRespIn)
945     {}
946 
~UpdateMetrics()947     ~UpdateMetrics()
948     {
949         boost::asio::post(
950             crow::connections::systemBus->get_io_context(),
951             std::bind_front(&setReadingParams, asyncResp, id,
952                             std::move(readingParams), readingParamsUris,
953                             metricPropertyToDbusPaths));
954     }
955 
956     UpdateMetrics(const UpdateMetrics&) = delete;
957     UpdateMetrics(UpdateMetrics&&) = delete;
958     UpdateMetrics& operator=(const UpdateMetrics&) = delete;
959     UpdateMetrics& operator=(UpdateMetrics&&) = delete;
960 
961     std::string id;
962     std::map<std::string, std::string> metricPropertyToDbusPaths;
963 
insert(const std::map<std::string,std::string> & additionalMetricPropertyToDbusPaths)964     void insert(const std::map<std::string, std::string>&
965                     additionalMetricPropertyToDbusPaths)
966     {
967         metricPropertyToDbusPaths.insert(
968             additionalMetricPropertyToDbusPaths.begin(),
969             additionalMetricPropertyToDbusPaths.end());
970     }
971 
emplace(std::span<const std::tuple<sdbusplus::message::object_path,std::string>> pathAndUri,const AddReportArgs::MetricArgs & metricArgs)972     void emplace(
973         std::span<
974             const std::tuple<sdbusplus::message::object_path, std::string>>
975             pathAndUri,
976         const AddReportArgs::MetricArgs& metricArgs)
977     {
978         readingParamsUris.emplace_back(metricArgs.uris);
979         readingParams.emplace_back(
980             std::vector(pathAndUri.begin(), pathAndUri.end()),
981             metricArgs.collectionFunction, metricArgs.collectionTimeScope,
982             metricArgs.collectionDuration);
983     }
984 
985   private:
986     const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
987     std::vector<std::vector<std::string>> readingParamsUris;
988     ReadingParameters readingParams;
989 };
990 
991 inline void
setReportEnabled(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,bool enabled)992     setReportEnabled(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
993                      std::string_view id, bool enabled)
994 {
995     crow::connections::systemBus->async_method_call(
996         [asyncResp, id = std::string(id)](const boost::system::error_code& ec) {
997             if (!verifyCommonErrors(asyncResp->res, id, ec))
998             {
999                 return;
1000             }
1001         },
1002         "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1003         "org.freedesktop.DBus.Properties", "Set",
1004         "xyz.openbmc_project.Telemetry.Report", "Enabled",
1005         dbus::utility::DbusVariantType{enabled});
1006 }
1007 
afterSetReportingProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id,const boost::system::error_code & ec,const sdbusplus::message_t & msg)1008 inline void afterSetReportingProperties(
1009     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
1010     const boost::system::error_code& ec, const sdbusplus::message_t& msg)
1011 {
1012     if (!ec)
1013     {
1014         asyncResp->res.result(boost::beast::http::status::no_content);
1015         return;
1016     }
1017 
1018     if (ec == boost::system::errc::invalid_argument)
1019     {
1020         const sd_bus_error* errorMessage = msg.get_error();
1021         if (errorMessage != nullptr)
1022         {
1023             for (const auto& arg : {"Id", "ReportingType", "Interval"})
1024             {
1025                 if (!handleParamError(asyncResp->res, errorMessage->message,
1026                                       arg))
1027                 {
1028                     return;
1029                 }
1030             }
1031         }
1032     }
1033     if (!verifyCommonErrors(asyncResp->res, id, ec))
1034     {
1035         return;
1036     }
1037     messages::internalError(asyncResp->res);
1038 }
1039 
setReportTypeAndInterval(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,const std::optional<std::string> & reportingType,const std::optional<std::string> & recurrenceIntervalStr)1040 inline void setReportTypeAndInterval(
1041     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
1042     const std::optional<std::string>& reportingType,
1043     const std::optional<std::string>& recurrenceIntervalStr)
1044 {
1045     std::string dbusReportingType;
1046     if (reportingType)
1047     {
1048         dbusReportingType = toDbusReportingType(*reportingType);
1049         if (dbusReportingType.empty())
1050         {
1051             messages::propertyValueNotInList(asyncResp->res, *reportingType,
1052                                              "MetricReportDefinitionType");
1053             return;
1054         }
1055     }
1056 
1057     uint64_t recurrenceInterval = std::numeric_limits<uint64_t>::max();
1058     if (recurrenceIntervalStr)
1059     {
1060         std::optional<std::chrono::milliseconds> durationNum =
1061             time_utils::fromDurationString(*recurrenceIntervalStr);
1062         if (!durationNum || durationNum->count() < 0)
1063         {
1064             messages::propertyValueIncorrect(
1065                 asyncResp->res, "RecurrenceInterval", *recurrenceIntervalStr);
1066             return;
1067         }
1068 
1069         recurrenceInterval = static_cast<uint64_t>(durationNum->count());
1070     }
1071 
1072     crow::connections::systemBus->async_method_call(
1073         [asyncResp, id = std::string(id)](const boost::system::error_code& ec,
1074                                           const sdbusplus::message_t& msg) {
1075             afterSetReportingProperties(asyncResp, id, ec, msg);
1076         },
1077         "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1078         "xyz.openbmc_project.Telemetry.Report", "SetReportingProperties",
1079         dbusReportingType, recurrenceInterval);
1080 }
1081 
afterSetReportUpdates(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id,const boost::system::error_code & ec,const sdbusplus::message_t & msg)1082 inline void afterSetReportUpdates(
1083     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
1084     const boost::system::error_code& ec, const sdbusplus::message_t& msg)
1085 {
1086     if (!ec)
1087     {
1088         asyncResp->res.result(boost::beast::http::status::no_content);
1089         return;
1090     }
1091     if (ec == boost::system::errc::invalid_argument)
1092     {
1093         const sd_bus_error* errorMessage = msg.get_error();
1094         if (errorMessage != nullptr)
1095         {
1096             for (const auto& arg : {"Id", "ReportUpdates"})
1097             {
1098                 if (!handleParamError(asyncResp->res, errorMessage->message,
1099                                       arg))
1100                 {
1101                     return;
1102                 }
1103             }
1104         }
1105     }
1106     if (!verifyCommonErrors(asyncResp->res, id, ec))
1107     {
1108         return;
1109     }
1110 }
1111 
1112 inline void
setReportUpdates(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,const std::string & reportUpdates)1113     setReportUpdates(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1114                      std::string_view id, const std::string& reportUpdates)
1115 {
1116     std::string dbusReportUpdates = toDbusReportUpdates(reportUpdates);
1117     if (dbusReportUpdates.empty())
1118     {
1119         messages::propertyValueNotInList(asyncResp->res, reportUpdates,
1120                                          "ReportUpdates");
1121         return;
1122     }
1123     crow::connections::systemBus->async_method_call(
1124         [asyncResp, id = std::string(id)](const boost::system::error_code& ec,
1125                                           const sdbusplus::message_t& msg) {
1126             afterSetReportUpdates(asyncResp, id, ec, msg);
1127         },
1128         "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1129         "org.freedesktop.DBus.Properties", "Set",
1130         "xyz.openbmc_project.Telemetry.Report", "ReportUpdates",
1131         dbus::utility::DbusVariantType{dbusReportUpdates});
1132 }
1133 
afterSetReportActions(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id,const boost::system::error_code & ec,const sdbusplus::message_t & msg)1134 inline void afterSetReportActions(
1135     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
1136     const boost::system::error_code& ec, const sdbusplus::message_t& msg)
1137 {
1138     if (ec == boost::system::errc::invalid_argument)
1139     {
1140         const sd_bus_error* errorMessage = msg.get_error();
1141         if (errorMessage != nullptr)
1142         {
1143             for (const auto& arg : {"Id", "ReportActions"})
1144             {
1145                 if (!handleParamError(asyncResp->res, errorMessage->message,
1146                                       arg))
1147                 {
1148                     return;
1149                 }
1150             }
1151         }
1152     }
1153 
1154     if (!verifyCommonErrors(asyncResp->res, id, ec))
1155     {
1156         return;
1157     }
1158 
1159     messages::internalError(asyncResp->res);
1160 }
1161 
setReportActions(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,const std::vector<std::string> & reportActions)1162 inline void setReportActions(
1163     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
1164     const std::vector<std::string>& reportActions)
1165 {
1166     std::vector<std::string> dbusReportActions;
1167     if (!toDbusReportActions(asyncResp->res, reportActions, dbusReportActions))
1168     {
1169         return;
1170     }
1171 
1172     crow::connections::systemBus->async_method_call(
1173         [asyncResp, id = std::string(id)](const boost::system::error_code& ec,
1174                                           const sdbusplus::message_t& msg) {
1175             afterSetReportActions(asyncResp, id, ec, msg);
1176         },
1177         "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1178         "org.freedesktop.DBus.Properties", "Set",
1179         "xyz.openbmc_project.Telemetry.Report", "ReportActions",
1180         dbus::utility::DbusVariantType{dbusReportActions});
1181 }
1182 
setReportMetrics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,std::vector<nlohmann::json::object_t> && metrics)1183 inline void setReportMetrics(
1184     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
1185     std::vector<nlohmann::json::object_t>&& metrics)
1186 {
1187     sdbusplus::asio::getAllProperties(
1188         *crow::connections::systemBus, telemetry::service,
1189         telemetry::getDbusReportPath(id), telemetry::reportInterface,
1190         [asyncResp, id = std::string(id), redfishMetrics = std::move(metrics)](
1191             boost::system::error_code ec,
1192             const dbus::utility::DBusPropertiesMap& properties) mutable {
1193             if (!verifyCommonErrors(asyncResp->res, id, ec))
1194             {
1195                 return;
1196             }
1197 
1198             ReadingParameters readingParams;
1199 
1200             const bool success = sdbusplus::unpackPropertiesNoThrow(
1201                 dbus_utils::UnpackErrorPrinter(), properties,
1202                 "ReadingParameters", readingParams);
1203 
1204             if (!success)
1205             {
1206                 messages::internalError(asyncResp->res);
1207                 return;
1208             }
1209 
1210             auto updateMetricsReq =
1211                 std::make_shared<UpdateMetrics>(id, asyncResp);
1212 
1213             boost::container::flat_set<std::pair<std::string, std::string>>
1214                 chassisSensors;
1215 
1216             size_t index = 0;
1217             for (nlohmann::json::object_t& metric : redfishMetrics)
1218             {
1219                 AddReportArgs::MetricArgs metricArgs;
1220                 std::vector<
1221                     std::tuple<sdbusplus::message::object_path, std::string>>
1222                     pathAndUri;
1223 
1224                 if (index < readingParams.size())
1225                 {
1226                     const ReadingParameters::value_type& existing =
1227                         readingParams[index];
1228 
1229                     pathAndUri = std::get<0>(existing);
1230                     metricArgs.collectionFunction = std::get<1>(existing);
1231                     metricArgs.collectionTimeScope = std::get<2>(existing);
1232                     metricArgs.collectionDuration = std::get<3>(existing);
1233                 }
1234 
1235                 if (!getUserMetric(asyncResp->res, metric, metricArgs))
1236                 {
1237                     return;
1238                 }
1239 
1240                 std::optional<IncorrectMetricUri> error =
1241                     getChassisSensorNode(metricArgs.uris, chassisSensors);
1242 
1243                 if (error)
1244                 {
1245                     messages::propertyValueIncorrect(
1246                         asyncResp->res, error->uri,
1247                         "MetricProperties/" + std::to_string(error->index));
1248                     return;
1249                 }
1250 
1251                 updateMetricsReq->emplace(pathAndUri, metricArgs);
1252                 index++;
1253             }
1254 
1255             for (const auto& [chassis, sensorType] : chassisSensors)
1256             {
1257                 retrieveUriToDbusMap(
1258                     chassis, sensorType,
1259                     [asyncResp, updateMetricsReq](
1260                         const boost::beast::http::status status,
1261                         const std::map<std::string, std::string>& uriToDbus) {
1262                         if (status != boost::beast::http::status::ok)
1263                         {
1264                             BMCWEB_LOG_ERROR(
1265                                 "Failed to retrieve URI to dbus sensors map with err {}",
1266                                 static_cast<unsigned>(status));
1267                             return;
1268                         }
1269                         updateMetricsReq->insert(uriToDbus);
1270                     });
1271             }
1272         });
1273 }
1274 
handleMetricReportDefinitionCollectionHead(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1275 inline void handleMetricReportDefinitionCollectionHead(
1276     App& app, const crow::Request& req,
1277     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1278 {
1279     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1280     {
1281         return;
1282     }
1283     asyncResp->res.addHeader(
1284         boost::beast::http::field::link,
1285         "</redfish/v1/JsonSchemas/MetricReportDefinitionCollection/MetricReportDefinitionCollection.json>; rel=describedby");
1286 }
1287 
handleMetricReportDefinitionCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1288 inline void handleMetricReportDefinitionCollectionGet(
1289     App& app, const crow::Request& req,
1290     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1291 {
1292     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1293     {
1294         return;
1295     }
1296     asyncResp->res.addHeader(
1297         boost::beast::http::field::link,
1298         "</redfish/v1/JsonSchemas/MetricReportDefinition/MetricReportDefinition.json>; rel=describedby");
1299 
1300     asyncResp->res.jsonValue["@odata.type"] =
1301         "#MetricReportDefinitionCollection."
1302         "MetricReportDefinitionCollection";
1303     asyncResp->res.jsonValue["@odata.id"] =
1304         "/redfish/v1/TelemetryService/MetricReportDefinitions";
1305     asyncResp->res.jsonValue["Name"] = "Metric Definition Collection";
1306     constexpr std::array<std::string_view, 1> interfaces{
1307         telemetry::reportInterface};
1308     collection_util::getCollectionMembers(
1309         asyncResp,
1310         boost::urls::url(
1311             "/redfish/v1/TelemetryService/MetricReportDefinitions"),
1312         interfaces, "/xyz/openbmc_project/Telemetry/Reports/TelemetryService");
1313 }
1314 
handleReportPatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id)1315 inline void handleReportPatch(
1316     App& app, const crow::Request& req,
1317     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id)
1318 {
1319     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1320     {
1321         return;
1322     }
1323 
1324     std::optional<std::string> reportingTypeStr;
1325     std::optional<std::string> reportUpdatesStr;
1326     std::optional<bool> metricReportDefinitionEnabled;
1327     std::optional<std::vector<nlohmann::json::object_t>> metrics;
1328     std::optional<std::vector<std::string>> reportActionsStr;
1329     std::optional<std::string> scheduleDurationStr;
1330 
1331     if (!json_util::readJsonPatch(
1332             req, asyncResp->res, "Metrics", metrics,
1333             "MetricReportDefinitionType", reportingTypeStr, "ReportUpdates",
1334             reportUpdatesStr, "ReportActions", reportActionsStr,
1335             "Schedule/RecurrenceInterval", scheduleDurationStr,
1336             "MetricReportDefinitionEnabled", metricReportDefinitionEnabled))
1337     {
1338         return;
1339     }
1340 
1341     if (metricReportDefinitionEnabled)
1342     {
1343         setReportEnabled(asyncResp, id, *metricReportDefinitionEnabled);
1344     }
1345 
1346     if (reportUpdatesStr)
1347     {
1348         setReportUpdates(asyncResp, id, *reportUpdatesStr);
1349     }
1350 
1351     if (reportActionsStr)
1352     {
1353         setReportActions(asyncResp, id, *reportActionsStr);
1354     }
1355 
1356     if (reportingTypeStr || scheduleDurationStr)
1357     {
1358         setReportTypeAndInterval(asyncResp, id, reportingTypeStr,
1359                                  scheduleDurationStr);
1360     }
1361 
1362     if (metrics)
1363     {
1364         setReportMetrics(asyncResp, id, std::move(*metrics));
1365     }
1366 }
1367 
handleReportDelete(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id)1368 inline void handleReportDelete(
1369     App& app, const crow::Request& req,
1370     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id)
1371 {
1372     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1373     {
1374         return;
1375     }
1376 
1377     const std::string reportPath = getDbusReportPath(id);
1378 
1379     crow::connections::systemBus->async_method_call(
1380         [asyncResp,
1381          reportId = std::string(id)](const boost::system::error_code& ec) {
1382             if (!verifyCommonErrors(asyncResp->res, reportId, ec))
1383             {
1384                 return;
1385             }
1386             asyncResp->res.result(boost::beast::http::status::no_content);
1387         },
1388         service, reportPath, "xyz.openbmc_project.Object.Delete", "Delete");
1389 }
1390 } // namespace telemetry
1391 
afterRetrieveUriToDbusMap(const std::shared_ptr<bmcweb::AsyncResp> &,const std::shared_ptr<telemetry::AddReport> & addReportReq,const boost::beast::http::status status,const std::map<std::string,std::string> & uriToDbus)1392 inline void afterRetrieveUriToDbusMap(
1393     const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
1394     const std::shared_ptr<telemetry::AddReport>& addReportReq,
1395     const boost::beast::http::status status,
1396     const std::map<std::string, std::string>& uriToDbus)
1397 {
1398     if (status != boost::beast::http::status::ok)
1399     {
1400         BMCWEB_LOG_ERROR(
1401             "Failed to retrieve URI to dbus sensors map with err {}",
1402             static_cast<unsigned>(status));
1403         return;
1404     }
1405     addReportReq->insert(uriToDbus);
1406 }
1407 
handleMetricReportDefinitionsPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1408 inline void handleMetricReportDefinitionsPost(
1409     App& app, const crow::Request& req,
1410     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1411 {
1412     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1413     {
1414         return;
1415     }
1416 
1417     telemetry::AddReportArgs args;
1418     if (!telemetry::getUserParameters(asyncResp->res, req, args))
1419     {
1420         return;
1421     }
1422 
1423     boost::container::flat_set<std::pair<std::string, std::string>>
1424         chassisSensors;
1425     if (!telemetry::getChassisSensorNodeFromMetrics(asyncResp, args.metrics,
1426                                                     chassisSensors))
1427     {
1428         return;
1429     }
1430 
1431     auto addReportReq =
1432         std::make_shared<telemetry::AddReport>(std::move(args), asyncResp);
1433     for (const auto& [chassis, sensorType] : chassisSensors)
1434     {
1435         retrieveUriToDbusMap(chassis, sensorType,
1436                              std::bind_front(afterRetrieveUriToDbusMap,
1437                                              asyncResp, addReportReq));
1438     }
1439 }
1440 
1441 inline void
handleMetricReportHead(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string &)1442     handleMetricReportHead(App& app, const crow::Request& req,
1443                            const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1444                            const std::string& /*id*/)
1445 {
1446     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1447     {
1448         return;
1449     }
1450     asyncResp->res.addHeader(
1451         boost::beast::http::field::link,
1452         "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby");
1453 }
1454 
handleMetricReportGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id)1455 inline void handleMetricReportGet(
1456     App& app, const crow::Request& req,
1457     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1458 {
1459     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1460     {
1461         return;
1462     }
1463     asyncResp->res.addHeader(
1464         boost::beast::http::field::link,
1465         "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby");
1466 
1467     sdbusplus::asio::getAllProperties(
1468         *crow::connections::systemBus, telemetry::service,
1469         telemetry::getDbusReportPath(id), telemetry::reportInterface,
1470         [asyncResp, id](const boost::system::error_code& ec,
1471                         const dbus::utility::DBusPropertiesMap& properties) {
1472             if (!redfish::telemetry::verifyCommonErrors(asyncResp->res, id, ec))
1473             {
1474                 return;
1475             }
1476 
1477             telemetry::fillReportDefinition(asyncResp, id, properties);
1478         });
1479 }
1480 
handleMetricReportDelete(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id)1481 inline void handleMetricReportDelete(
1482     App& app, const crow::Request& req,
1483     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1484 
1485 {
1486     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1487     {
1488         return;
1489     }
1490 
1491     const std::string reportPath = telemetry::getDbusReportPath(id);
1492 
1493     crow::connections::systemBus->async_method_call(
1494         [asyncResp, id](const boost::system::error_code& ec) {
1495             /*
1496              * boost::system::errc and std::errc are missing value
1497              * for EBADR error that is defined in Linux.
1498              */
1499             if (ec.value() == EBADR)
1500             {
1501                 messages::resourceNotFound(asyncResp->res,
1502                                            "MetricReportDefinition", id);
1503                 return;
1504             }
1505 
1506             if (ec)
1507             {
1508                 BMCWEB_LOG_ERROR("respHandler DBus error {}", ec);
1509                 messages::internalError(asyncResp->res);
1510                 return;
1511             }
1512 
1513             asyncResp->res.result(boost::beast::http::status::no_content);
1514         },
1515         telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete",
1516         "Delete");
1517 }
1518 
requestRoutesMetricReportDefinitionCollection(App & app)1519 inline void requestRoutesMetricReportDefinitionCollection(App& app)
1520 {
1521     BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1522         .privileges(redfish::privileges::headMetricReportDefinitionCollection)
1523         .methods(boost::beast::http::verb::head)(std::bind_front(
1524             telemetry::handleMetricReportDefinitionCollectionHead,
1525             std::ref(app)));
1526 
1527     BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1528         .privileges(redfish::privileges::getMetricReportDefinitionCollection)
1529         .methods(boost::beast::http::verb::get)(std::bind_front(
1530             telemetry::handleMetricReportDefinitionCollectionGet,
1531             std::ref(app)));
1532 
1533     BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1534         .privileges(redfish::privileges::postMetricReportDefinitionCollection)
1535         .methods(boost::beast::http::verb::post)(
1536             std::bind_front(handleMetricReportDefinitionsPost, std::ref(app)));
1537 }
1538 
requestRoutesMetricReportDefinition(App & app)1539 inline void requestRoutesMetricReportDefinition(App& app)
1540 {
1541     BMCWEB_ROUTE(app,
1542                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1543         .privileges(redfish::privileges::getMetricReportDefinition)
1544         .methods(boost::beast::http::verb::head)(
1545             std::bind_front(handleMetricReportHead, std::ref(app)));
1546 
1547     BMCWEB_ROUTE(app,
1548                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1549         .privileges(redfish::privileges::getMetricReportDefinition)
1550         .methods(boost::beast::http::verb::get)(
1551             std::bind_front(handleMetricReportGet, std::ref(app)));
1552 
1553     BMCWEB_ROUTE(app,
1554                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1555         .privileges(redfish::privileges::deleteMetricReportDefinition)
1556         .methods(boost::beast::http::verb::delete_)(
1557             std::bind_front(handleMetricReportDelete, std::ref(app)));
1558 
1559     BMCWEB_ROUTE(app,
1560                  "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1561         .privileges(redfish::privileges::patchMetricReportDefinition)
1562         .methods(boost::beast::http::verb::patch)(
1563             std::bind_front(telemetry::handleReportPatch, std::ref(app)));
1564 }
1565 } // namespace redfish
1566