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