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