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