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