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