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;
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)
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 dbus::utility::async_method_call(
844 asyncResp,
845 [asyncResp, args](const boost::system::error_code& ec,
846 const sdbusplus::message_t& msg,
847 const std::string& /*arg1*/) {
848 afterAddReport(asyncResp, args, ec, msg);
849 },
850 telemetry::service, "/xyz/openbmc_project/Telemetry/Reports",
851 "xyz.openbmc_project.Telemetry.ReportManager", "AddReport",
852 "TelemetryService/" + args.id, args.name, args.reportingType,
853 args.reportUpdates, args.appendLimit, args.reportActions,
854 args.interval, readingParams, args.metricReportDefinitionEnabled);
855 }
856
857 AddReport(const AddReport&) = delete;
858 AddReport(AddReport&&) = delete;
859 AddReport& operator=(const AddReport&) = delete;
860 AddReport& operator=(AddReport&&) = delete;
861
insert(const std::map<std::string,std::string> & el)862 void insert(const std::map<std::string, std::string>& el)
863 {
864 uriToDbus.insert(el.begin(), el.end());
865 }
866
867 private:
868 const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
869 AddReportArgs args;
870 boost::container::flat_map<std::string, std::string> uriToDbus;
871 };
872
873 inline std::optional<
874 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)875 sensorPathToUri(
876 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
877 std::span<const std::string> uris,
878 const std::map<std::string, std::string>& metricPropertyToDbusPaths)
879 {
880 std::vector<std::tuple<sdbusplus::message::object_path, std::string>>
881 result;
882
883 for (const std::string& uri : uris)
884 {
885 auto it = metricPropertyToDbusPaths.find(uri);
886 if (it == metricPropertyToDbusPaths.end())
887 {
888 messages::propertyValueNotInList(asyncResp->res, uri,
889 "MetricProperties");
890 return {};
891 }
892 result.emplace_back(it->second, uri);
893 }
894
895 return result;
896 }
897
afterSetReadingParams(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & reportId,const boost::system::error_code & ec,const sdbusplus::message_t & msg)898 inline void afterSetReadingParams(
899 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
900 const std::string& reportId, const boost::system::error_code& ec,
901 const sdbusplus::message_t& msg)
902 {
903 if (!ec)
904 {
905 messages::success(asyncResp->res);
906 return;
907 }
908 if (ec == boost::system::errc::invalid_argument)
909 {
910 const sd_bus_error* errorMessage = msg.get_error();
911 if (errorMessage != nullptr)
912 {
913 for (const auto& arg : {"Id", "ReadingParameters"})
914 {
915 if (!handleParamError(asyncResp->res, errorMessage->message,
916 arg))
917 {
918 return;
919 }
920 }
921 }
922 }
923 if (!verifyCommonErrors(asyncResp->res, reportId, ec))
924 {
925 return;
926 }
927 messages::internalError(asyncResp->res);
928 }
929
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)930 inline void setReadingParams(
931 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
932 const std::string& reportId, ReadingParameters readingParams,
933 const std::vector<std::vector<std::string>>& readingParamsUris,
934 const std::map<std::string, std::string>& metricPropertyToDbusPaths)
935 {
936 if (asyncResp->res.result() != boost::beast::http::status::ok)
937 {
938 return;
939 }
940
941 for (size_t index = 0; index < readingParamsUris.size(); ++index)
942 {
943 std::span<const std::string> newUris = readingParamsUris[index];
944
945 const std::optional<std::vector<
946 std::tuple<sdbusplus::message::object_path, std::string>>>
947 readingParam =
948 sensorPathToUri(asyncResp, newUris, metricPropertyToDbusPaths);
949
950 if (!readingParam)
951 {
952 return;
953 }
954
955 for (const std::tuple<sdbusplus::message::object_path, std::string>&
956 value : *readingParam)
957 {
958 std::get<0>(readingParams[index]).emplace_back(value);
959 }
960 }
961
962 dbus::utility::async_method_call(
963 asyncResp,
964 [asyncResp, reportId](const boost::system::error_code& ec,
965 const sdbusplus::message_t& msg) {
966 afterSetReadingParams(asyncResp, reportId, ec, msg);
967 },
968 "xyz.openbmc_project.Telemetry", getDbusReportPath(reportId),
969 "org.freedesktop.DBus.Properties", "Set",
970 "xyz.openbmc_project.Telemetry.Report", "ReadingParameters",
971 dbus::utility::DbusVariantType{readingParams});
972 }
973
974 class UpdateMetrics
975 {
976 public:
UpdateMetrics(std::string_view idIn,const std::shared_ptr<bmcweb::AsyncResp> & asyncRespIn)977 UpdateMetrics(std::string_view idIn,
978 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) :
979 id(idIn), asyncResp(asyncRespIn)
980 {}
981
~UpdateMetrics()982 ~UpdateMetrics()
983 {
984 boost::asio::post(
985 crow::connections::systemBus->get_io_context(),
986 std::bind_front(&setReadingParams, asyncResp, id,
987 std::move(readingParams), readingParamsUris,
988 metricPropertyToDbusPaths));
989 }
990
991 UpdateMetrics(const UpdateMetrics&) = delete;
992 UpdateMetrics(UpdateMetrics&&) = delete;
993 UpdateMetrics& operator=(const UpdateMetrics&) = delete;
994 UpdateMetrics& operator=(UpdateMetrics&&) = delete;
995
996 std::string id;
997 std::map<std::string, std::string> metricPropertyToDbusPaths;
998
insert(const std::map<std::string,std::string> & additionalMetricPropertyToDbusPaths)999 void insert(const std::map<std::string, std::string>&
1000 additionalMetricPropertyToDbusPaths)
1001 {
1002 metricPropertyToDbusPaths.insert(
1003 additionalMetricPropertyToDbusPaths.begin(),
1004 additionalMetricPropertyToDbusPaths.end());
1005 }
1006
emplace(std::span<const std::tuple<sdbusplus::message::object_path,std::string>> pathAndUri,const AddReportArgs::MetricArgs & metricArgs)1007 void emplace(
1008 std::span<
1009 const std::tuple<sdbusplus::message::object_path, std::string>>
1010 pathAndUri,
1011 const AddReportArgs::MetricArgs& metricArgs)
1012 {
1013 readingParamsUris.emplace_back(metricArgs.uris);
1014 readingParams.emplace_back(
1015 std::vector(pathAndUri.begin(), pathAndUri.end()),
1016 metricArgs.collectionFunction, metricArgs.collectionTimeScope,
1017 metricArgs.collectionDuration);
1018 }
1019
1020 private:
1021 const std::shared_ptr<bmcweb::AsyncResp> asyncResp;
1022 std::vector<std::vector<std::string>> readingParamsUris;
1023 ReadingParameters readingParams;
1024 };
1025
setReportEnabled(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,bool enabled)1026 inline void setReportEnabled(
1027 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
1028 bool enabled)
1029 {
1030 dbus::utility::async_method_call(
1031 asyncResp,
1032 [asyncResp, id = std::string(id)](const boost::system::error_code& ec) {
1033 if (!verifyCommonErrors(asyncResp->res, id, ec))
1034 {
1035 return;
1036 }
1037 },
1038 "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1039 "org.freedesktop.DBus.Properties", "Set",
1040 "xyz.openbmc_project.Telemetry.Report", "Enabled",
1041 dbus::utility::DbusVariantType{enabled});
1042 }
1043
afterSetReportingProperties(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id,const boost::system::error_code & ec,const sdbusplus::message_t & msg)1044 inline void afterSetReportingProperties(
1045 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
1046 const boost::system::error_code& ec, const sdbusplus::message_t& msg)
1047 {
1048 if (!ec)
1049 {
1050 asyncResp->res.result(boost::beast::http::status::no_content);
1051 return;
1052 }
1053
1054 if (ec == boost::system::errc::invalid_argument)
1055 {
1056 const sd_bus_error* errorMessage = msg.get_error();
1057 if (errorMessage != nullptr)
1058 {
1059 for (const auto& arg : {"Id", "ReportingType", "Interval"})
1060 {
1061 if (!handleParamError(asyncResp->res, errorMessage->message,
1062 arg))
1063 {
1064 return;
1065 }
1066 }
1067 }
1068 }
1069 if (!verifyCommonErrors(asyncResp->res, id, ec))
1070 {
1071 return;
1072 }
1073 messages::internalError(asyncResp->res);
1074 }
1075
setReportTypeAndInterval(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,const std::optional<std::string> & reportingType,const std::optional<std::string> & recurrenceIntervalStr)1076 inline void setReportTypeAndInterval(
1077 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
1078 const std::optional<std::string>& reportingType,
1079 const std::optional<std::string>& recurrenceIntervalStr)
1080 {
1081 std::string dbusReportingType;
1082 if (reportingType)
1083 {
1084 dbusReportingType = toDbusReportingType(*reportingType);
1085 if (dbusReportingType.empty())
1086 {
1087 messages::propertyValueNotInList(asyncResp->res, *reportingType,
1088 "MetricReportDefinitionType");
1089 return;
1090 }
1091 }
1092
1093 uint64_t recurrenceInterval = std::numeric_limits<uint64_t>::max();
1094 if (recurrenceIntervalStr)
1095 {
1096 std::optional<std::chrono::milliseconds> durationNum =
1097 time_utils::fromDurationString(*recurrenceIntervalStr);
1098 if (!durationNum || durationNum->count() < 0)
1099 {
1100 messages::propertyValueIncorrect(
1101 asyncResp->res, "RecurrenceInterval", *recurrenceIntervalStr);
1102 return;
1103 }
1104
1105 recurrenceInterval = static_cast<uint64_t>(durationNum->count());
1106 }
1107
1108 dbus::utility::async_method_call(
1109 asyncResp,
1110 [asyncResp, id = std::string(id)](const boost::system::error_code& ec,
1111 const sdbusplus::message_t& msg) {
1112 afterSetReportingProperties(asyncResp, id, ec, msg);
1113 },
1114 "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1115 "xyz.openbmc_project.Telemetry.Report", "SetReportingProperties",
1116 dbusReportingType, recurrenceInterval);
1117 }
1118
afterSetReportUpdates(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id,const boost::system::error_code & ec,const sdbusplus::message_t & msg)1119 inline void afterSetReportUpdates(
1120 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
1121 const boost::system::error_code& ec, const sdbusplus::message_t& msg)
1122 {
1123 if (!ec)
1124 {
1125 asyncResp->res.result(boost::beast::http::status::no_content);
1126 return;
1127 }
1128 if (ec == boost::system::errc::invalid_argument)
1129 {
1130 const sd_bus_error* errorMessage = msg.get_error();
1131 if (errorMessage != nullptr)
1132 {
1133 for (const auto& arg : {"Id", "ReportUpdates"})
1134 {
1135 if (!handleParamError(asyncResp->res, errorMessage->message,
1136 arg))
1137 {
1138 return;
1139 }
1140 }
1141 }
1142 }
1143 if (!verifyCommonErrors(asyncResp->res, id, ec))
1144 {
1145 return;
1146 }
1147 }
1148
setReportUpdates(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,const std::string & reportUpdates)1149 inline void setReportUpdates(
1150 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
1151 const std::string& reportUpdates)
1152 {
1153 std::string dbusReportUpdates = toDbusReportUpdates(reportUpdates);
1154 if (dbusReportUpdates.empty())
1155 {
1156 messages::propertyValueNotInList(asyncResp->res, reportUpdates,
1157 "ReportUpdates");
1158 return;
1159 }
1160 dbus::utility::async_method_call(
1161 asyncResp,
1162 [asyncResp, id = std::string(id)](const boost::system::error_code& ec,
1163 const sdbusplus::message_t& msg) {
1164 afterSetReportUpdates(asyncResp, id, ec, msg);
1165 },
1166 "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1167 "org.freedesktop.DBus.Properties", "Set",
1168 "xyz.openbmc_project.Telemetry.Report", "ReportUpdates",
1169 dbus::utility::DbusVariantType{dbusReportUpdates});
1170 }
1171
afterSetReportActions(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id,const boost::system::error_code & ec,const sdbusplus::message_t & msg)1172 inline void afterSetReportActions(
1173 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id,
1174 const boost::system::error_code& ec, const sdbusplus::message_t& msg)
1175 {
1176 if (ec == boost::system::errc::invalid_argument)
1177 {
1178 const sd_bus_error* errorMessage = msg.get_error();
1179 if (errorMessage != nullptr)
1180 {
1181 for (const auto& arg : {"Id", "ReportActions"})
1182 {
1183 if (!handleParamError(asyncResp->res, errorMessage->message,
1184 arg))
1185 {
1186 return;
1187 }
1188 }
1189 }
1190 }
1191
1192 if (!verifyCommonErrors(asyncResp->res, id, ec))
1193 {
1194 return;
1195 }
1196
1197 messages::internalError(asyncResp->res);
1198 }
1199
setReportActions(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,const std::vector<std::string> & reportActions)1200 inline void setReportActions(
1201 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
1202 const std::vector<std::string>& reportActions)
1203 {
1204 std::vector<std::string> dbusReportActions;
1205 if (!toDbusReportActions(asyncResp->res, reportActions, dbusReportActions))
1206 {
1207 return;
1208 }
1209
1210 dbus::utility::async_method_call(
1211 asyncResp,
1212 [asyncResp, id = std::string(id)](const boost::system::error_code& ec,
1213 const sdbusplus::message_t& msg) {
1214 afterSetReportActions(asyncResp, id, ec, msg);
1215 },
1216 "xyz.openbmc_project.Telemetry", getDbusReportPath(id),
1217 "org.freedesktop.DBus.Properties", "Set",
1218 "xyz.openbmc_project.Telemetry.Report", "ReportActions",
1219 dbus::utility::DbusVariantType{dbusReportActions});
1220 }
1221
setReportMetrics(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id,std::vector<std::variant<nlohmann::json::object_t,std::nullptr_t>> && metrics)1222 inline void setReportMetrics(
1223 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id,
1224 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>&&
1225 metrics)
1226 {
1227 dbus::utility::getAllProperties(
1228 telemetry::service, telemetry::getDbusReportPath(id),
1229 telemetry::reportInterface,
1230 [asyncResp, id = std::string(id), redfishMetrics = std::move(metrics)](
1231 boost::system::error_code ec,
1232 const dbus::utility::DBusPropertiesMap& properties) mutable {
1233 if (!verifyCommonErrors(asyncResp->res, id, ec))
1234 {
1235 return;
1236 }
1237
1238 ReadingParameters readingParams;
1239
1240 const bool success = sdbusplus::unpackPropertiesNoThrow(
1241 dbus_utils::UnpackErrorPrinter(), properties,
1242 "ReadingParameters", readingParams);
1243
1244 if (!success)
1245 {
1246 messages::internalError(asyncResp->res);
1247 return;
1248 }
1249
1250 auto updateMetricsReq =
1251 std::make_shared<UpdateMetrics>(id, asyncResp);
1252
1253 boost::container::flat_set<std::pair<std::string, std::string>>
1254 chassisSensors;
1255
1256 size_t index = 0;
1257 for (std::variant<nlohmann::json::object_t, std::nullptr_t>&
1258 metricVariant : redfishMetrics)
1259 {
1260 nlohmann::json::object_t* metric =
1261 std::get_if<nlohmann::json::object_t>(&metricVariant);
1262 if (metric == nullptr)
1263 {
1264 index++;
1265 continue;
1266 }
1267
1268 AddReportArgs::MetricArgs metricArgs;
1269 std::vector<
1270 std::tuple<sdbusplus::message::object_path, std::string>>
1271 pathAndUri;
1272
1273 if (index < readingParams.size())
1274 {
1275 const ReadingParameters::value_type& existing =
1276 readingParams[index];
1277
1278 if (metric->empty())
1279 {
1280 pathAndUri = std::get<0>(existing);
1281 }
1282 metricArgs.collectionFunction = std::get<1>(existing);
1283 metricArgs.collectionTimeScope = std::get<2>(existing);
1284 metricArgs.collectionDuration = std::get<3>(existing);
1285 }
1286
1287 if (!getUserMetric(asyncResp->res, *metric, metricArgs))
1288 {
1289 return;
1290 }
1291
1292 std::optional<IncorrectMetricUri> error =
1293 getChassisSensorNode(metricArgs.uris, chassisSensors);
1294
1295 if (error)
1296 {
1297 messages::propertyValueIncorrect(
1298 asyncResp->res, error->uri,
1299 "MetricProperties/" + std::to_string(error->index));
1300 return;
1301 }
1302
1303 updateMetricsReq->emplace(pathAndUri, metricArgs);
1304 index++;
1305 }
1306
1307 for (const auto& [chassis, sensorType] : chassisSensors)
1308 {
1309 retrieveUriToDbusMap(
1310 chassis, sensorType,
1311 [asyncResp, updateMetricsReq](
1312 const boost::beast::http::status status,
1313 const std::map<std::string, std::string>& uriToDbus) {
1314 if (status != boost::beast::http::status::ok)
1315 {
1316 BMCWEB_LOG_ERROR(
1317 "Failed to retrieve URI to dbus sensors map with err {}",
1318 static_cast<unsigned>(status));
1319 return;
1320 }
1321 updateMetricsReq->insert(uriToDbus);
1322 });
1323 }
1324 });
1325 }
1326
handleMetricReportDefinitionCollectionHead(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1327 inline void handleMetricReportDefinitionCollectionHead(
1328 App& app, const crow::Request& req,
1329 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1330 {
1331 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1332 {
1333 return;
1334 }
1335 asyncResp->res.addHeader(
1336 boost::beast::http::field::link,
1337 "</redfish/v1/JsonSchemas/MetricReportDefinitionCollection/MetricReportDefinitionCollection.json>; rel=describedby");
1338 }
1339
handleMetricReportDefinitionCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1340 inline void handleMetricReportDefinitionCollectionGet(
1341 App& app, const crow::Request& req,
1342 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1343 {
1344 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1345 {
1346 return;
1347 }
1348 asyncResp->res.addHeader(
1349 boost::beast::http::field::link,
1350 "</redfish/v1/JsonSchemas/MetricReportDefinition/MetricReportDefinition.json>; rel=describedby");
1351
1352 asyncResp->res.jsonValue["@odata.type"] =
1353 "#MetricReportDefinitionCollection."
1354 "MetricReportDefinitionCollection";
1355 asyncResp->res.jsonValue["@odata.id"] =
1356 "/redfish/v1/TelemetryService/MetricReportDefinitions";
1357 asyncResp->res.jsonValue["Name"] = "Metric Definition Collection";
1358 constexpr std::array<std::string_view, 1> interfaces{
1359 telemetry::reportInterface};
1360 collection_util::getCollectionMembers(
1361 asyncResp,
1362 boost::urls::url(
1363 "/redfish/v1/TelemetryService/MetricReportDefinitions"),
1364 interfaces, "/xyz/openbmc_project/Telemetry/Reports/TelemetryService");
1365 }
1366
handleReportPatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id)1367 inline void handleReportPatch(
1368 App& app, const crow::Request& req,
1369 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id)
1370 {
1371 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1372 {
1373 return;
1374 }
1375
1376 std::optional<std::string> reportingTypeStr;
1377 std::optional<std::string> reportUpdatesStr;
1378 std::optional<bool> metricReportDefinitionEnabled;
1379 std::optional<
1380 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>
1381 metrics;
1382 std::optional<std::vector<std::string>> reportActionsStr;
1383 std::optional<std::string> scheduleDurationStr;
1384
1385 if (!json_util::readJsonPatch( //
1386 req, asyncResp->res, //
1387 "MetricReportDefinitionEnabled", metricReportDefinitionEnabled, //
1388 "MetricReportDefinitionType", reportingTypeStr, //
1389 "Metrics", metrics, //
1390 "ReportActions", reportActionsStr, //
1391 "ReportUpdates", reportUpdatesStr, //
1392 "Schedule/RecurrenceInterval", scheduleDurationStr //
1393 ))
1394 {
1395 return;
1396 }
1397
1398 if (metricReportDefinitionEnabled)
1399 {
1400 setReportEnabled(asyncResp, id, *metricReportDefinitionEnabled);
1401 }
1402
1403 if (reportUpdatesStr)
1404 {
1405 setReportUpdates(asyncResp, id, *reportUpdatesStr);
1406 }
1407
1408 if (reportActionsStr)
1409 {
1410 setReportActions(asyncResp, id, *reportActionsStr);
1411 }
1412
1413 if (reportingTypeStr || scheduleDurationStr)
1414 {
1415 setReportTypeAndInterval(asyncResp, id, reportingTypeStr,
1416 scheduleDurationStr);
1417 }
1418
1419 if (metrics)
1420 {
1421 setReportMetrics(asyncResp, id, std::move(*metrics));
1422 }
1423 }
1424
handleReportDelete(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string_view id)1425 inline void handleReportDelete(
1426 App& app, const crow::Request& req,
1427 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id)
1428 {
1429 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1430 {
1431 return;
1432 }
1433
1434 const std::string reportPath = getDbusReportPath(id);
1435
1436 dbus::utility::async_method_call(
1437 asyncResp,
1438 [asyncResp,
1439 reportId = std::string(id)](const boost::system::error_code& ec) {
1440 if (!verifyCommonErrors(asyncResp->res, reportId, ec))
1441 {
1442 return;
1443 }
1444 asyncResp->res.result(boost::beast::http::status::no_content);
1445 },
1446 service, reportPath, "xyz.openbmc_project.Object.Delete", "Delete");
1447 }
1448 } // namespace telemetry
1449
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)1450 inline void afterRetrieveUriToDbusMap(
1451 const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/,
1452 const std::shared_ptr<telemetry::AddReport>& addReportReq,
1453 const boost::beast::http::status status,
1454 const std::map<std::string, std::string>& uriToDbus)
1455 {
1456 if (status != boost::beast::http::status::ok)
1457 {
1458 BMCWEB_LOG_ERROR(
1459 "Failed to retrieve URI to dbus sensors map with err {}",
1460 static_cast<unsigned>(status));
1461 return;
1462 }
1463 addReportReq->insert(uriToDbus);
1464 }
1465
handleMetricReportDefinitionsPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1466 inline void handleMetricReportDefinitionsPost(
1467 App& app, const crow::Request& req,
1468 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1469 {
1470 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1471 {
1472 return;
1473 }
1474
1475 telemetry::AddReportArgs args;
1476 if (!telemetry::getUserParameters(asyncResp->res, req, args))
1477 {
1478 return;
1479 }
1480
1481 boost::container::flat_set<std::pair<std::string, std::string>>
1482 chassisSensors;
1483 if (!telemetry::getChassisSensorNodeFromMetrics(asyncResp, args.metrics,
1484 chassisSensors))
1485 {
1486 return;
1487 }
1488
1489 auto addReportReq =
1490 std::make_shared<telemetry::AddReport>(std::move(args), asyncResp);
1491 for (const auto& [chassis, sensorType] : chassisSensors)
1492 {
1493 retrieveUriToDbusMap(chassis, sensorType,
1494 std::bind_front(afterRetrieveUriToDbusMap,
1495 asyncResp, addReportReq));
1496 }
1497 }
1498
handleMetricReportHead(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string &)1499 inline void handleMetricReportHead(
1500 App& app, const crow::Request& req,
1501 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1502 const std::string& /*id*/)
1503 {
1504 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1505 {
1506 return;
1507 }
1508 asyncResp->res.addHeader(
1509 boost::beast::http::field::link,
1510 "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby");
1511 }
1512
handleMetricReportGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id)1513 inline void handleMetricReportGet(
1514 App& app, const crow::Request& req,
1515 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1516 {
1517 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1518 {
1519 return;
1520 }
1521 asyncResp->res.addHeader(
1522 boost::beast::http::field::link,
1523 "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby");
1524
1525 dbus::utility::getAllProperties(
1526 telemetry::service, telemetry::getDbusReportPath(id),
1527 telemetry::reportInterface,
1528 [asyncResp, id](const boost::system::error_code& ec,
1529 const dbus::utility::DBusPropertiesMap& properties) {
1530 if (!redfish::telemetry::verifyCommonErrors(asyncResp->res, id, ec))
1531 {
1532 return;
1533 }
1534
1535 telemetry::fillReportDefinition(asyncResp, id, properties);
1536 });
1537 }
1538
handleMetricReportDelete(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id)1539 inline void handleMetricReportDelete(
1540 App& app, const crow::Request& req,
1541 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
1542
1543 {
1544 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1545 {
1546 return;
1547 }
1548
1549 const std::string reportPath = telemetry::getDbusReportPath(id);
1550
1551 dbus::utility::async_method_call(
1552 asyncResp,
1553 [asyncResp, id](const boost::system::error_code& ec) {
1554 /*
1555 * boost::system::errc and std::errc are missing value
1556 * for EBADR error that is defined in Linux.
1557 */
1558 if (ec.value() == EBADR)
1559 {
1560 messages::resourceNotFound(asyncResp->res,
1561 "MetricReportDefinition", id);
1562 return;
1563 }
1564
1565 if (ec)
1566 {
1567 BMCWEB_LOG_ERROR("respHandler DBus error {}", ec);
1568 messages::internalError(asyncResp->res);
1569 return;
1570 }
1571
1572 asyncResp->res.result(boost::beast::http::status::no_content);
1573 },
1574 telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete",
1575 "Delete");
1576 }
1577
requestRoutesMetricReportDefinitionCollection(App & app)1578 inline void requestRoutesMetricReportDefinitionCollection(App& app)
1579 {
1580 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1581 .privileges(redfish::privileges::headMetricReportDefinitionCollection)
1582 .methods(boost::beast::http::verb::head)(std::bind_front(
1583 telemetry::handleMetricReportDefinitionCollectionHead,
1584 std::ref(app)));
1585
1586 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1587 .privileges(redfish::privileges::getMetricReportDefinitionCollection)
1588 .methods(boost::beast::http::verb::get)(std::bind_front(
1589 telemetry::handleMetricReportDefinitionCollectionGet,
1590 std::ref(app)));
1591
1592 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/")
1593 .privileges(redfish::privileges::postMetricReportDefinitionCollection)
1594 .methods(boost::beast::http::verb::post)(
1595 std::bind_front(handleMetricReportDefinitionsPost, std::ref(app)));
1596 }
1597
requestRoutesMetricReportDefinition(App & app)1598 inline void requestRoutesMetricReportDefinition(App& app)
1599 {
1600 BMCWEB_ROUTE(app,
1601 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1602 .privileges(redfish::privileges::getMetricReportDefinition)
1603 .methods(boost::beast::http::verb::head)(
1604 std::bind_front(handleMetricReportHead, std::ref(app)));
1605
1606 BMCWEB_ROUTE(app,
1607 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1608 .privileges(redfish::privileges::getMetricReportDefinition)
1609 .methods(boost::beast::http::verb::get)(
1610 std::bind_front(handleMetricReportGet, std::ref(app)));
1611
1612 BMCWEB_ROUTE(app,
1613 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1614 .privileges(redfish::privileges::deleteMetricReportDefinition)
1615 .methods(boost::beast::http::verb::delete_)(
1616 std::bind_front(handleMetricReportDelete, std::ref(app)));
1617
1618 BMCWEB_ROUTE(app,
1619 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/")
1620 .privileges(redfish::privileges::patchMetricReportDefinition)
1621 .methods(boost::beast::http::verb::patch)(
1622 std::bind_front(telemetry::handleReportPatch, std::ref(app)));
1623 }
1624 } // namespace redfish
1625