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