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