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