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, "MetricProperties", uris, "CollectionFunction",
442 collectionFunction, "CollectionTimeScope", collectionTimeScopeStr,
443 "CollectionDuration", collectionDurationStr))
444 {
445 return false;
446 }
447
448 if (uris)
449 {
450 metricArgs.uris = std::move(*uris);
451 }
452
453 if (collectionFunction)
454 {
455 std::string dbusCollectionFunction =
456 telemetry::toDbusCollectionFunction(*collectionFunction);
457 if (dbusCollectionFunction.empty())
458 {
459 messages::propertyValueIncorrect(res, "CollectionFunction",
460 *collectionFunction);
461 return false;
462 }
463 metricArgs.collectionFunction = std::move(dbusCollectionFunction);
464 }
465
466 if (collectionTimeScopeStr)
467 {
468 std::string dbusCollectionTimeScope =
469 toDbusCollectionTimeScope(*collectionTimeScopeStr);
470 if (dbusCollectionTimeScope.empty())
471 {
472 messages::propertyValueIncorrect(res, "CollectionTimeScope",
473 *collectionTimeScopeStr);
474 return false;
475 }
476 metricArgs.collectionTimeScope = std::move(dbusCollectionTimeScope);
477 }
478
479 if (collectionDurationStr)
480 {
481 std::optional<std::chrono::milliseconds> duration =
482 time_utils::fromDurationString(*collectionDurationStr);
483
484 if (!duration || duration->count() < 0)
485 {
486 messages::propertyValueIncorrect(res, "CollectionDuration",
487 *collectionDurationStr);
488 return false;
489 }
490
491 metricArgs.collectionDuration =
492 static_cast<uint64_t>(duration->count());
493 }
494
495 return true;
496 }
497
getUserMetrics(crow::Response & res,std::span<nlohmann::json::object_t> metrics,std::vector<AddReportArgs::MetricArgs> & result)498 inline bool getUserMetrics(crow::Response& res,
499 std::span<nlohmann::json::object_t> metrics,
500 std::vector<AddReportArgs::MetricArgs>& result)
501 {
502 result.reserve(metrics.size());
503
504 for (nlohmann::json::object_t& m : metrics)
505 {
506 AddReportArgs::MetricArgs metricArgs;
507
508 if (!getUserMetric(res, m, metricArgs))
509 {
510 return false;
511 }
512
513 result.emplace_back(std::move(metricArgs));
514 }
515
516 return true;
517 }
518
getUserParameters(crow::Response & res,const crow::Request & req,AddReportArgs & args)519 inline bool getUserParameters(crow::Response& res, const crow::Request& req,
520 AddReportArgs& args)
521 {
522 std::optional<std::string> id;
523 std::optional<std::string> name;
524 std::optional<std::string> reportingTypeStr;
525 std::optional<std::string> reportUpdatesStr;
526 std::optional<uint64_t> appendLimit;
527 std::optional<bool> metricReportDefinitionEnabled;
528 std::optional<std::vector<nlohmann::json::object_t>> metrics;
529 std::optional<std::vector<std::string>> reportActionsStr;
530 std::optional<std::string> scheduleDurationStr;
531
532 if (!json_util::readJsonPatch(
533 req, res, "Id", id, "Name", name, "Metrics", metrics,
534 "MetricReportDefinitionType", reportingTypeStr, "ReportUpdates",
535 reportUpdatesStr, "AppendLimit", appendLimit, "ReportActions",
536 reportActionsStr, "Schedule/RecurrenceInterval",
537 scheduleDurationStr, "MetricReportDefinitionEnabled",
538 metricReportDefinitionEnabled))
539 {
540 return false;
541 }
542
543 if (id)
544 {
545 constexpr const char* allowedCharactersInId =
546 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
547 if (id->empty() ||
548 id->find_first_not_of(allowedCharactersInId) != std::string::npos)
549 {
550 messages::propertyValueIncorrect(res, "Id", *id);
551 return false;
552 }
553 args.id = *id;
554 }
555
556 if (name)
557 {
558 args.name = *name;
559 }
560
561 if (reportingTypeStr)
562 {
563 std::string dbusReportingType = toDbusReportingType(*reportingTypeStr);
564 if (dbusReportingType.empty())
565 {
566 messages::propertyValueNotInList(res, *reportingTypeStr,
567 "MetricReportDefinitionType");
568 return false;
569 }
570 args.reportingType = dbusReportingType;
571 }
572
573 if (reportUpdatesStr)
574 {
575 std::string dbusReportUpdates = toDbusReportUpdates(*reportUpdatesStr);
576 if (dbusReportUpdates.empty())
577 {
578 messages::propertyValueNotInList(res, *reportUpdatesStr,
579 "ReportUpdates");
580 return false;
581 }
582 args.reportUpdates = dbusReportUpdates;
583 }
584
585 if (appendLimit)
586 {
587 args.appendLimit = *appendLimit;
588 }
589
590 if (metricReportDefinitionEnabled)
591 {
592 args.metricReportDefinitionEnabled = *metricReportDefinitionEnabled;
593 }
594
595 if (reportActionsStr)
596 {
597 if (!toDbusReportActions(res, *reportActionsStr, args.reportActions))
598 {
599 return false;
600 }
601 }
602
603 if (reportingTypeStr == "Periodic")
604 {
605 if (!scheduleDurationStr)
606 {
607 messages::createFailedMissingReqProperties(res, "Schedule");
608 return false;
609 }
610
611 std::optional<std::chrono::milliseconds> durationNum =
612 time_utils::fromDurationString(*scheduleDurationStr);
613 if (!durationNum || durationNum->count() < 0)
614 {
615 messages::propertyValueIncorrect(res, "RecurrenceInterval",
616 *scheduleDurationStr);
617 return false;
618 }
619 args.interval = static_cast<uint64_t>(durationNum->count());
620 }
621
622 if (metrics)
623 {
624 if (!getUserMetrics(res, *metrics, args.metrics))
625 {
626 return false;
627 }
628 }
629
630 return true;
631 }
632
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)633 inline bool getChassisSensorNodeFromMetrics(
634 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
635 std::span<const AddReportArgs::MetricArgs> metrics,
636 boost::container::flat_set<std::pair<std::string, std::string>>& matched)
637 {
638 for (const auto& metric : metrics)
639 {
640 std::optional<IncorrectMetricUri> error =
641 getChassisSensorNode(metric.uris, matched);
642 if (error)
643 {
644 messages::propertyValueIncorrect(
645 asyncResp->res, error->uri,
646 "MetricProperties/" + std::to_string(error->index));
647 return false;
648 }
649 }
650 return true;
651 }
652
toRedfishProperty(std::string_view dbusMessage)653 inline std::string toRedfishProperty(std::string_view dbusMessage)
654 {
655 if (dbusMessage == "Id")
656 {
657 return "Id";
658 }
659 if (dbusMessage == "Name")
660 {
661 return "Name";
662 }
663 if (dbusMessage == "ReportingType")
664 {
665 return "MetricReportDefinitionType";
666 }
667 if (dbusMessage == "AppendLimit")
668 {
669 return "AppendLimit";
670 }
671 if (dbusMessage == "ReportActions")
672 {
673 return "ReportActions";
674 }
675 if (dbusMessage == "Interval")
676 {
677 return "RecurrenceInterval";
678 }
679 if (dbusMessage == "ReportUpdates")
680 {
681 return "ReportUpdates";
682 }
683 if (dbusMessage == "ReadingParameters")
684 {
685 return "Metrics";
686 }
687 return "";
688 }
689
handleParamError(crow::Response & res,const char * errorMessage,std::string_view key)690 inline bool handleParamError(crow::Response& res, const char* errorMessage,
691 std::string_view key)
692 {
693 if (errorMessage == nullptr)
694 {
695 BMCWEB_LOG_ERROR("errorMessage was null");
696 return true;
697 }
698 std::string_view errorMessageSv(errorMessage);
699 if (errorMessageSv.starts_with(key))
700 {
701 std::string redfishProperty = toRedfishProperty(key);
702 if (redfishProperty.empty())
703 {
704 // Getting here means most possibly that toRedfishProperty has
705 // incomplete implementation. Return internal error for now.
706 BMCWEB_LOG_ERROR("{} has no corresponding Redfish property", key);
707 messages::internalError(res);
708 return false;
709 }
710 messages::propertyValueError(res, redfishProperty);
711 return false;
712 }
713
714 return true;
715 }
716
afterAddReport(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const AddReportArgs & args,const boost::system::error_code & ec,const sdbusplus::message_t & msg)717 inline void afterAddReport(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
718 const AddReportArgs& args,
719 const boost::system::error_code& ec,
720 const sdbusplus::message_t& msg)
721 {
722 if (!ec)
723 {
724 messages::created(asyncResp->res);
725 return;
726 }
727
728 if (ec == boost::system::errc::invalid_argument)
729 {
730 const sd_bus_error* errorMessage = msg.get_error();
731 if (errorMessage != nullptr)
732 {
733 for (const auto& arg :
734 {"Id", "Name", "ReportingType", "AppendLimit", "ReportActions",
735 "Interval", "ReportUpdates", "ReadingParameters"})
736 {
737 if (!handleParamError(asyncResp->res, errorMessage->message,
738 arg))
739 {
740 return;
741 }
742 }
743 }
744 }
745 if (!verifyCommonErrors(asyncResp->res, args.id, ec))
746 {
747 return;
748 }
749 messages::internalError(asyncResp->res);
750 }
751
752 class AddReport
753 {
754 public:
AddReport(AddReportArgs && argsIn,const std::shared_ptr<bmcweb::AsyncResp> & asyncRespIn)755 AddReport(AddReportArgs&& argsIn,
756 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
757 asyncResp(asyncRespIn), args(std::move(argsIn))
758 {}
759
~AddReport()760 ~AddReport()
761 {
762 boost::asio::post(crow::connections::systemBus->get_io_context(),
763 std::bind_front(&performAddReport, asyncResp, args,
764 std::move(uriToDbus)));
765 }
766
performAddReport(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const AddReportArgs & args,const boost::container::flat_map<std::string,std::string> & uriToDbus)767 static void performAddReport(
768 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
769 const AddReportArgs& args,
770 const boost::container::flat_map<std::string, std::string>& uriToDbus)
771 {
772 if (asyncResp->res.result() != boost::beast::http::status::ok)
773 {
774 return;
775 }
776
777 telemetry::ReadingParameters readingParams;
778 readingParams.reserve(args.metrics.size());
779
780 for (const auto& metric : args.metrics)
781 {
782 std::vector<
783 std::tuple<sdbusplus::message::object_path, std::string>>
784 sensorParams;
785 sensorParams.reserve(metric.uris.size());
786
787 for (size_t i = 0; i < metric.uris.size(); i++)
788 {
789 const std::string& uri = metric.uris[i];
790 auto el = uriToDbus.find(uri);
791 if (el == uriToDbus.end())
792 {
793 BMCWEB_LOG_ERROR(
794 "Failed to find DBus sensor corresponding to URI {}",
795 uri);
796 messages::propertyValueNotInList(
797 asyncResp->res, uri,
798 "MetricProperties/" + std::to_string(i));
799 return;
800 }
801
802 const std::string& dbusPath = el->second;
803 sensorParams.emplace_back(dbusPath, uri);
804 }
805
806 readingParams.emplace_back(
807 std::move(sensorParams), metric.collectionFunction,
808 metric.collectionTimeScope, metric.collectionDuration);
809 }
810 crow::connections::systemBus->async_method_call(
811 [asyncResp, args](const boost::system::error_code& ec,
812 const sdbusplus::message_t& msg,
813 const std::string& /*arg1*/) {
814 afterAddReport(asyncResp, args, ec, msg);
815 },
816 telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
817 "xyz.openbmc_project.Telemetry.ReportManager", "AddReport",
818 "TelemetryService/" + args.id, args.name, args.reportingType,
819 args.reportUpdates, args.appendLimit, args.reportActions,
820 args.interval, readingParams, args.metricReportDefinitionEnabled);
821 }
822
823 AddReport(const AddReport&) = delete;
824 AddReport(AddReport&&) = delete;
825 AddReport& operator=(const AddReport&) = delete;
826 AddReport& operator=(AddReport&&) = delete;
827
insert(const std::map<std::string,std::string> & el)828 void insert(const std::map<std::string, std::string>& el)
829 {
830 uriToDbus.insert(el.begin(), el.end());
831 }
832
833 private:
834 const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
835 AddReportArgs args;
836 boost::container::flat_map<std::string, std::string> uriToDbus;
837 };
838
839 inline std::optional<
840 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)841 sensorPathToUri(
842 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
843 std::span<const std::string> uris,
844 const std::map<std::string, std::string>& metricPropertyToDbusPaths)
845 {
846 std::vector<std::tuple<sdbusplus::message::object_path, std::string>>
847 result;
848
849 for (const std::string& uri : uris)
850 {
851 auto it = metricPropertyToDbusPaths.find(uri);
852 if (it == metricPropertyToDbusPaths.end())
853 {
854 messages::propertyValueNotInList(asyncResp->res, uri,
855 "MetricProperties");
856 return {};
857 }
858 result.emplace_back(it->second, uri);
859 }
860
861 return result;
862 }
863
afterSetReadingParams(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & reportId,const boost::system::error_code & ec,const sdbusplus::message_t & msg)864 inline void afterSetReadingParams(
865 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
866 const std::string& reportId, const boost::system::error_code& ec,
867 const sdbusplus::message_t& msg)
868 {
869 if (!ec)
870 {
871 messages::success(asyncResp->res);
872 return;
873 }
874 if (ec == boost::system::errc::invalid_argument)
875 {
876 const sd_bus_error* errorMessage = msg.get_error();
877 if (errorMessage != nullptr)
878 {
879 for (const auto& arg : {"Id", "ReadingParameters"})
880 {
881 if (!handleParamError(asyncResp->res, errorMessage->message,
882 arg))
883 {
884 return;
885 }
886 }
887 }
888 }
889 if (!verifyCommonErrors(asyncResp->res, reportId, ec))
890 {
891 return;
892 }
893 messages::internalError(asyncResp->res);
894 }
895
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)896 inline void setReadingParams(
897 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
898 const std::string& reportId, ReadingParameters readingParams,
899 const std::vector<std::vector<std::string>>& readingParamsUris,
900 const std::map<std::string, std::string>& metricPropertyToDbusPaths)
901 {
902 if (asyncResp->res.result() != boost::beast::http::status::ok)
903 {
904 return;
905 }
906
907 for (size_t index = 0; index < readingParamsUris.size(); ++index)
908 {
909 std::span<const std::string> newUris = readingParamsUris[index];
910
911 const std::optional<std::vector<
912 std::tuple<sdbusplus::message::object_path, std::string>>>
913 readingParam =
914 sensorPathToUri(asyncResp, newUris, metricPropertyToDbusPaths);
915
916 if (!readingParam)
917 {
918 return;
919 }
920
921 for (const std::tuple<sdbusplus::message::object_path, std::string>&
922 value : *readingParam)
923 {
924 std::get<0>(readingParams[index]).emplace_back(value);
925 }
926 }
927
928 crow::connections::systemBus->async_method_call(
929 [asyncResp, reportId](const boost::system::error_code& ec,
930 const sdbusplus::message_t& msg) {
931 afterSetReadingParams(asyncResp, reportId, ec, msg);
932 },
933 "xyz.openbmc_project.Telemetry", getDbusReportPath(reportId),
934 "org.freedesktop.DBus.Properties", "Set",
935 "xyz.openbmc_project.Telemetry.Report", "ReadingParameters",
936 dbus::utility::DbusVariantType{readingParams});
937 }
938
939 class UpdateMetrics
940 {
941 public:
UpdateMetrics(std::string_view idIn,const std::shared_ptr<bmcweb::AsyncResp> & asyncRespIn)942 UpdateMetrics(std::string_view idIn,
943 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
944 id(idIn), asyncResp(asyncRespIn)
945 {}
946
~UpdateMetrics()947 ~UpdateMetrics()
948 {
949 boost::asio::post(
950 crow::connections::systemBus->get_io_context(),
951 std::bind_front(&setReadingParams, asyncResp, id,
952 std::move(readingParams), readingParamsUris,
953 metricPropertyToDbusPaths));
954 }
955
956 UpdateMetrics(const UpdateMetrics&) = delete;
957 UpdateMetrics(UpdateMetrics&&) = delete;
958 UpdateMetrics& operator=(const UpdateMetrics&) = delete;
959 UpdateMetrics& operator=(UpdateMetrics&&) = delete;
960
961 std::string id;
962 std::map<std::string, std::string> metricPropertyToDbusPaths;
963
insert(const std::map<std::string,std::string> & additionalMetricPropertyToDbusPaths)964 void insert(const std::map<std::string, std::string>&
965 additionalMetricPropertyToDbusPaths)
966 {
967 metricPropertyToDbusPaths.insert(
968 additionalMetricPropertyToDbusPaths.begin(),
969 additionalMetricPropertyToDbusPaths.end());
970 }
971
emplace(std::span<const std::tuple<sdbusplus::message::object_path,std::string>> pathAndUri,const AddReportArgs::MetricArgs & metricArgs)972 void emplace(
973 std::span<
974 const std::tuple<sdbusplus::message::object_path, std::string>>
975 pathAndUri,
976 const AddReportArgs::MetricArgs& metricArgs)
977 {
978 readingParamsUris.emplace_back(metricArgs.uris);
979 readingParams.emplace_back(
980 std::vector(pathAndUri.begin(), pathAndUri.end()),
981 metricArgs.collectionFunction, metricArgs.collectionTimeScope,
982 metricArgs.collectionDuration);
983 }
984
985 private:
986 const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
987 std::vector<std::vector<std::string>> readingParamsUris;
988 ReadingParameters readingParams;
989 };
990
991 inline void
setReportEnabled(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,bool enabled)992 setReportEnabled(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
993 std::string_view id, bool enabled)
994 {
995 crow::connections::systemBus->async_method_call(
996 [asyncResp, id = std::string(id)](const boost::system::error_code& ec) {
997 if (!verifyCommonErrors(asyncResp->res, id, ec))
998 {
999 return;
1000 }
1001 },
1002 "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1003 "org.freedesktop.DBus.Properties", "Set",
1004 "xyz.openbmc_project.Telemetry.Report", "Enabled",
1005 dbus::utility::DbusVariantType{enabled});
1006 }
1007
afterSetReportingProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id,const boost::system::error_code & ec,const sdbusplus::message_t & msg)1008 inline void afterSetReportingProperties(
1009 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
1010 const boost::system::error_code& ec, const sdbusplus::message_t& msg)
1011 {
1012 if (!ec)
1013 {
1014 asyncResp->res.result(boost::beast::http::status::no_content);
1015 return;
1016 }
1017
1018 if (ec == boost::system::errc::invalid_argument)
1019 {
1020 const sd_bus_error* errorMessage = msg.get_error();
1021 if (errorMessage != nullptr)
1022 {
1023 for (const auto& arg : {"Id", "ReportingType", "Interval"})
1024 {
1025 if (!handleParamError(asyncResp->res, errorMessage->message,
1026 arg))
1027 {
1028 return;
1029 }
1030 }
1031 }
1032 }
1033 if (!verifyCommonErrors(asyncResp->res, id, ec))
1034 {
1035 return;
1036 }
1037 messages::internalError(asyncResp->res);
1038 }
1039
setReportTypeAndInterval(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,const std::optional<std::string> & reportingType,const std::optional<std::string> & recurrenceIntervalStr)1040 inline void setReportTypeAndInterval(
1041 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
1042 const std::optional<std::string>& reportingType,
1043 const std::optional<std::string>& recurrenceIntervalStr)
1044 {
1045 std::string dbusReportingType;
1046 if (reportingType)
1047 {
1048 dbusReportingType = toDbusReportingType(*reportingType);
1049 if (dbusReportingType.empty())
1050 {
1051 messages::propertyValueNotInList(asyncResp->res, *reportingType,
1052 "MetricReportDefinitionType");
1053 return;
1054 }
1055 }
1056
1057 uint64_t recurrenceInterval = std::numeric_limits<uint64_t>::max();
1058 if (recurrenceIntervalStr)
1059 {
1060 std::optional<std::chrono::milliseconds> durationNum =
1061 time_utils::fromDurationString(*recurrenceIntervalStr);
1062 if (!durationNum || durationNum->count() < 0)
1063 {
1064 messages::propertyValueIncorrect(
1065 asyncResp->res, "RecurrenceInterval", *recurrenceIntervalStr);
1066 return;
1067 }
1068
1069 recurrenceInterval = static_cast<uint64_t>(durationNum->count());
1070 }
1071
1072 crow::connections::systemBus->async_method_call(
1073 [asyncResp, id = std::string(id)](const boost::system::error_code& ec,
1074 const sdbusplus::message_t& msg) {
1075 afterSetReportingProperties(asyncResp, id, ec, msg);
1076 },
1077 "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1078 "xyz.openbmc_project.Telemetry.Report", "SetReportingProperties",
1079 dbusReportingType, recurrenceInterval);
1080 }
1081
afterSetReportUpdates(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id,const boost::system::error_code & ec,const sdbusplus::message_t & msg)1082 inline void afterSetReportUpdates(
1083 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
1084 const boost::system::error_code& ec, const sdbusplus::message_t& msg)
1085 {
1086 if (!ec)
1087 {
1088 asyncResp->res.result(boost::beast::http::status::no_content);
1089 return;
1090 }
1091 if (ec == boost::system::errc::invalid_argument)
1092 {
1093 const sd_bus_error* errorMessage = msg.get_error();
1094 if (errorMessage != nullptr)
1095 {
1096 for (const auto& arg : {"Id", "ReportUpdates"})
1097 {
1098 if (!handleParamError(asyncResp->res, errorMessage->message,
1099 arg))
1100 {
1101 return;
1102 }
1103 }
1104 }
1105 }
1106 if (!verifyCommonErrors(asyncResp->res, id, ec))
1107 {
1108 return;
1109 }
1110 }
1111
1112 inline void
setReportUpdates(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,const std::string & reportUpdates)1113 setReportUpdates(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1114 std::string_view id, const std::string& reportUpdates)
1115 {
1116 std::string dbusReportUpdates = toDbusReportUpdates(reportUpdates);
1117 if (dbusReportUpdates.empty())
1118 {
1119 messages::propertyValueNotInList(asyncResp->res, reportUpdates,
1120 "ReportUpdates");
1121 return;
1122 }
1123 crow::connections::systemBus->async_method_call(
1124 [asyncResp, id = std::string(id)](const boost::system::error_code& ec,
1125 const sdbusplus::message_t& msg) {
1126 afterSetReportUpdates(asyncResp, id, ec, msg);
1127 },
1128 "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1129 "org.freedesktop.DBus.Properties", "Set",
1130 "xyz.openbmc_project.Telemetry.Report", "ReportUpdates",
1131 dbus::utility::DbusVariantType{dbusReportUpdates});
1132 }
1133
afterSetReportActions(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id,const boost::system::error_code & ec,const sdbusplus::message_t & msg)1134 inline void afterSetReportActions(
1135 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
1136 const boost::system::error_code& ec, const sdbusplus::message_t& msg)
1137 {
1138 if (ec == boost::system::errc::invalid_argument)
1139 {
1140 const sd_bus_error* errorMessage = msg.get_error();
1141 if (errorMessage != nullptr)
1142 {
1143 for (const auto& arg : {"Id", "ReportActions"})
1144 {
1145 if (!handleParamError(asyncResp->res, errorMessage->message,
1146 arg))
1147 {
1148 return;
1149 }
1150 }
1151 }
1152 }
1153
1154 if (!verifyCommonErrors(asyncResp->res, id, ec))
1155 {
1156 return;
1157 }
1158
1159 messages::internalError(asyncResp->res);
1160 }
1161
setReportActions(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,const std::vector<std::string> & reportActions)1162 inline void setReportActions(
1163 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
1164 const std::vector<std::string>& reportActions)
1165 {
1166 std::vector<std::string> dbusReportActions;
1167 if (!toDbusReportActions(asyncResp->res, reportActions, dbusReportActions))
1168 {
1169 return;
1170 }
1171
1172 crow::connections::systemBus->async_method_call(
1173 [asyncResp, id = std::string(id)](const boost::system::error_code& ec,
1174 const sdbusplus::message_t& msg) {
1175 afterSetReportActions(asyncResp, id, ec, msg);
1176 },
1177 "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1178 "org.freedesktop.DBus.Properties", "Set",
1179 "xyz.openbmc_project.Telemetry.Report", "ReportActions",
1180 dbus::utility::DbusVariantType{dbusReportActions});
1181 }
1182
setReportMetrics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,std::vector<nlohmann::json::object_t> && metrics)1183 inline void setReportMetrics(
1184 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
1185 std::vector<nlohmann::json::object_t>&& metrics)
1186 {
1187 sdbusplus::asio::getAllProperties(
1188 *crow::connections::systemBus, telemetry::service,
1189 telemetry::getDbusReportPath(id), telemetry::reportInterface,
1190 [asyncResp, id = std::string(id), redfishMetrics = std::move(metrics)](
1191 boost::system::error_code ec,
1192 const dbus::utility::DBusPropertiesMap& properties) mutable {
1193 if (!verifyCommonErrors(asyncResp->res, id, ec))
1194 {
1195 return;
1196 }
1197
1198 ReadingParameters readingParams;
1199
1200 const bool success = sdbusplus::unpackPropertiesNoThrow(
1201 dbus_utils::UnpackErrorPrinter(), properties,
1202 "ReadingParameters", readingParams);
1203
1204 if (!success)
1205 {
1206 messages::internalError(asyncResp->res);
1207 return;
1208 }
1209
1210 auto updateMetricsReq =
1211 std::make_shared<UpdateMetrics>(id, asyncResp);
1212
1213 boost::container::flat_set<std::pair<std::string, std::string>>
1214 chassisSensors;
1215
1216 size_t index = 0;
1217 for (nlohmann::json::object_t& metric : redfishMetrics)
1218 {
1219 AddReportArgs::MetricArgs metricArgs;
1220 std::vector<
1221 std::tuple<sdbusplus::message::object_path, std::string>>
1222 pathAndUri;
1223
1224 if (index < readingParams.size())
1225 {
1226 const ReadingParameters::value_type& existing =
1227 readingParams[index];
1228
1229 pathAndUri = std::get<0>(existing);
1230 metricArgs.collectionFunction = std::get<1>(existing);
1231 metricArgs.collectionTimeScope = std::get<2>(existing);
1232 metricArgs.collectionDuration = std::get<3>(existing);
1233 }
1234
1235 if (!getUserMetric(asyncResp->res, metric, metricArgs))
1236 {
1237 return;
1238 }
1239
1240 std::optional<IncorrectMetricUri> error =
1241 getChassisSensorNode(metricArgs.uris, chassisSensors);
1242
1243 if (error)
1244 {
1245 messages::propertyValueIncorrect(
1246 asyncResp->res, error->uri,
1247 "MetricProperties/" + std::to_string(error->index));
1248 return;
1249 }
1250
1251 updateMetricsReq->emplace(pathAndUri, metricArgs);
1252 index++;
1253 }
1254
1255 for (const auto& [chassis, sensorType] : chassisSensors)
1256 {
1257 retrieveUriToDbusMap(
1258 chassis, sensorType,
1259 [asyncResp, updateMetricsReq](
1260 const boost::beast::http::status status,
1261 const std::map<std::string, std::string>& uriToDbus) {
1262 if (status != boost::beast::http::status::ok)
1263 {
1264 BMCWEB_LOG_ERROR(
1265 "Failed to retrieve URI to dbus sensors map with err {}",
1266 static_cast<unsigned>(status));
1267 return;
1268 }
1269 updateMetricsReq->insert(uriToDbus);
1270 });
1271 }
1272 });
1273 }
1274
handleMetricReportDefinitionCollectionHead(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1275 inline void handleMetricReportDefinitionCollectionHead(
1276 App& app, const crow::Request& req,
1277 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1278 {
1279 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1280 {
1281 return;
1282 }
1283 asyncResp->res.addHeader(
1284 boost::beast::http::field::link,
1285 "</redfish/v1/JsonSchemas/MetricReportDefinitionCollection/MetricReportDefinitionCollection.json>; rel=describedby");
1286 }
1287
handleMetricReportDefinitionCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1288 inline void handleMetricReportDefinitionCollectionGet(
1289 App& app, const crow::Request& req,
1290 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1291 {
1292 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1293 {
1294 return;
1295 }
1296 asyncResp->res.addHeader(
1297 boost::beast::http::field::link,
1298 "</redfish/v1/JsonSchemas/MetricReportDefinition/MetricReportDefinition.json>; rel=describedby");
1299
1300 asyncResp->res.jsonValue["@odata.type"] =
1301 "#MetricReportDefinitionCollection."
1302 "MetricReportDefinitionCollection";
1303 asyncResp->res.jsonValue["@odata.id"] =
1304 "/redfish/v1/TelemetryService/MetricReportDefinitions";
1305 asyncResp->res.jsonValue["Name"] = "Metric Definition Collection";
1306 constexpr std::array<std::string_view, 1> interfaces{
1307 telemetry::reportInterface};
1308 collection_util::getCollectionMembers(
1309 asyncResp,
1310 boost::urls::url(
1311 "/redfish/v1/TelemetryService/MetricReportDefinitions"),
1312 interfaces, "/xyz/openbmc_project/Telemetry/Reports/TelemetryService");
1313 }
1314
handleReportPatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id)1315 inline void handleReportPatch(
1316 App& app, const crow::Request& req,
1317 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id)
1318 {
1319 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1320 {
1321 return;
1322 }
1323
1324 std::optional<std::string> reportingTypeStr;
1325 std::optional<std::string> reportUpdatesStr;
1326 std::optional<bool> metricReportDefinitionEnabled;
1327 std::optional<std::vector<nlohmann::json::object_t>> metrics;
1328 std::optional<std::vector<std::string>> reportActionsStr;
1329 std::optional<std::string> scheduleDurationStr;
1330
1331 if (!json_util::readJsonPatch(
1332 req, asyncResp->res, "Metrics", metrics,
1333 "MetricReportDefinitionType", reportingTypeStr, "ReportUpdates",
1334 reportUpdatesStr, "ReportActions", reportActionsStr,
1335 "Schedule/RecurrenceInterval", scheduleDurationStr,
1336 "MetricReportDefinitionEnabled", metricReportDefinitionEnabled))
1337 {
1338 return;
1339 }
1340
1341 if (metricReportDefinitionEnabled)
1342 {
1343 setReportEnabled(asyncResp, id, *metricReportDefinitionEnabled);
1344 }
1345
1346 if (reportUpdatesStr)
1347 {
1348 setReportUpdates(asyncResp, id, *reportUpdatesStr);
1349 }
1350
1351 if (reportActionsStr)
1352 {
1353 setReportActions(asyncResp, id, *reportActionsStr);
1354 }
1355
1356 if (reportingTypeStr || scheduleDurationStr)
1357 {
1358 setReportTypeAndInterval(asyncResp, id, reportingTypeStr,
1359 scheduleDurationStr);
1360 }
1361
1362 if (metrics)
1363 {
1364 setReportMetrics(asyncResp, id, std::move(*metrics));
1365 }
1366 }
1367
handleReportDelete(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id)1368 inline void handleReportDelete(
1369 App& app, const crow::Request& req,
1370 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id)
1371 {
1372 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1373 {
1374 return;
1375 }
1376
1377 const std::string reportPath = getDbusReportPath(id);
1378
1379 crow::connections::systemBus->async_method_call(
1380 [asyncResp,
1381 reportId = std::string(id)](const boost::system::error_code& ec) {
1382 if (!verifyCommonErrors(asyncResp->res, reportId, ec))
1383 {
1384 return;
1385 }
1386 asyncResp->res.result(boost::beast::http::status::no_content);
1387 },
1388 service, reportPath, "xyz.openbmc_project.Object.Delete", "Delete");
1389 }
1390 } // namespace telemetry
1391
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)1392 inline void afterRetrieveUriToDbusMap(
1393 const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
1394 const std::shared_ptr<telemetry::AddReport>& addReportReq,
1395 const boost::beast::http::status status,
1396 const std::map<std::string, std::string>& uriToDbus)
1397 {
1398 if (status != boost::beast::http::status::ok)
1399 {
1400 BMCWEB_LOG_ERROR(
1401 "Failed to retrieve URI to dbus sensors map with err {}",
1402 static_cast<unsigned>(status));
1403 return;
1404 }
1405 addReportReq->insert(uriToDbus);
1406 }
1407
handleMetricReportDefinitionsPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1408 inline void handleMetricReportDefinitionsPost(
1409 App& app, const crow::Request& req,
1410 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1411 {
1412 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1413 {
1414 return;
1415 }
1416
1417 telemetry::AddReportArgs args;
1418 if (!telemetry::getUserParameters(asyncResp->res, req, args))
1419 {
1420 return;
1421 }
1422
1423 boost::container::flat_set<std::pair<std::string, std::string>>
1424 chassisSensors;
1425 if (!telemetry::getChassisSensorNodeFromMetrics(asyncResp, args.metrics,
1426 chassisSensors))
1427 {
1428 return;
1429 }
1430
1431 auto addReportReq =
1432 std::make_shared<telemetry::AddReport>(std::move(args), asyncResp);
1433 for (const auto& [chassis, sensorType] : chassisSensors)
1434 {
1435 retrieveUriToDbusMap(chassis, sensorType,
1436 std::bind_front(afterRetrieveUriToDbusMap,
1437 asyncResp, addReportReq));
1438 }
1439 }
1440
1441 inline void
handleMetricReportHead(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string &)1442 handleMetricReportHead(App& app, const crow::Request& req,
1443 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1444 const std::string& /*id*/)
1445 {
1446 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1447 {
1448 return;
1449 }
1450 asyncResp->res.addHeader(
1451 boost::beast::http::field::link,
1452 "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby");
1453 }
1454
handleMetricReportGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id)1455 inline void handleMetricReportGet(
1456 App& app, const crow::Request& req,
1457 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1458 {
1459 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1460 {
1461 return;
1462 }
1463 asyncResp->res.addHeader(
1464 boost::beast::http::field::link,
1465 "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby");
1466
1467 sdbusplus::asio::getAllProperties(
1468 *crow::connections::systemBus, telemetry::service,
1469 telemetry::getDbusReportPath(id), telemetry::reportInterface,
1470 [asyncResp, id](const boost::system::error_code& ec,
1471 const dbus::utility::DBusPropertiesMap& properties) {
1472 if (!redfish::telemetry::verifyCommonErrors(asyncResp->res, id, ec))
1473 {
1474 return;
1475 }
1476
1477 telemetry::fillReportDefinition(asyncResp, id, properties);
1478 });
1479 }
1480
handleMetricReportDelete(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id)1481 inline void handleMetricReportDelete(
1482 App& app, const crow::Request& req,
1483 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1484
1485 {
1486 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1487 {
1488 return;
1489 }
1490
1491 const std::string reportPath = telemetry::getDbusReportPath(id);
1492
1493 crow::connections::systemBus->async_method_call(
1494 [asyncResp, id](const boost::system::error_code& ec) {
1495 /*
1496 * boost::system::errc and std::errc are missing value
1497 * for EBADR error that is defined in Linux.
1498 */
1499 if (ec.value() == EBADR)
1500 {
1501 messages::resourceNotFound(asyncResp->res,
1502 "MetricReportDefinition", id);
1503 return;
1504 }
1505
1506 if (ec)
1507 {
1508 BMCWEB_LOG_ERROR("respHandler DBus error {}", ec);
1509 messages::internalError(asyncResp->res);
1510 return;
1511 }
1512
1513 asyncResp->res.result(boost::beast::http::status::no_content);
1514 },
1515 telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete",
1516 "Delete");
1517 }
1518
requestRoutesMetricReportDefinitionCollection(App & app)1519 inline void requestRoutesMetricReportDefinitionCollection(App& app)
1520 {
1521 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1522 .privileges(redfish::privileges::headMetricReportDefinitionCollection)
1523 .methods(boost::beast::http::verb::head)(std::bind_front(
1524 telemetry::handleMetricReportDefinitionCollectionHead,
1525 std::ref(app)));
1526
1527 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1528 .privileges(redfish::privileges::getMetricReportDefinitionCollection)
1529 .methods(boost::beast::http::verb::get)(std::bind_front(
1530 telemetry::handleMetricReportDefinitionCollectionGet,
1531 std::ref(app)));
1532
1533 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1534 .privileges(redfish::privileges::postMetricReportDefinitionCollection)
1535 .methods(boost::beast::http::verb::post)(
1536 std::bind_front(handleMetricReportDefinitionsPost, std::ref(app)));
1537 }
1538
requestRoutesMetricReportDefinition(App & app)1539 inline void requestRoutesMetricReportDefinition(App& app)
1540 {
1541 BMCWEB_ROUTE(app,
1542 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1543 .privileges(redfish::privileges::getMetricReportDefinition)
1544 .methods(boost::beast::http::verb::head)(
1545 std::bind_front(handleMetricReportHead, std::ref(app)));
1546
1547 BMCWEB_ROUTE(app,
1548 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1549 .privileges(redfish::privileges::getMetricReportDefinition)
1550 .methods(boost::beast::http::verb::get)(
1551 std::bind_front(handleMetricReportGet, std::ref(app)));
1552
1553 BMCWEB_ROUTE(app,
1554 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1555 .privileges(redfish::privileges::deleteMetricReportDefinition)
1556 .methods(boost::beast::http::verb::delete_)(
1557 std::bind_front(handleMetricReportDelete, std::ref(app)));
1558
1559 BMCWEB_ROUTE(app,
1560 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1561 .privileges(redfish::privileges::patchMetricReportDefinition)
1562 .methods(boost::beast::http::verb::patch)(
1563 std::bind_front(telemetry::handleReportPatch, std::ref(app)));
1564 }
1565 } // namespace redfish
1566