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