1 #pragma once 2 3 #include "app.hpp" 4 #include "dbus_utility.hpp" 5 #include "generated/enums/metric_report_definition.hpp" 6 #include "query.hpp" 7 #include "registries/privilege_registry.hpp" 8 #include "sensors.hpp" 9 #include "utils/collection.hpp" 10 #include "utils/dbus_utils.hpp" 11 #include "utils/telemetry_utils.hpp" 12 #include "utils/time_utils.hpp" 13 14 #include <boost/container/flat_map.hpp> 15 #include <boost/url/format.hpp> 16 #include <sdbusplus/asio/property.hpp> 17 #include <sdbusplus/unpack_properties.hpp> 18 19 #include <array> 20 #include <map> 21 #include <optional> 22 #include <span> 23 #include <string> 24 #include <string_view> 25 #include <tuple> 26 #include <utility> 27 #include <variant> 28 #include <vector> 29 30 namespace redfish 31 { 32 33 namespace telemetry 34 { 35 36 using ReadingParameters = std::vector<std::tuple< 37 std::vector<std::tuple<sdbusplus::message::object_path, std::string>>, 38 std::string, std::string, uint64_t>>; 39 40 inline metric_report_definition::ReportActionsEnum 41 toRedfishReportAction(std::string_view dbusValue) 42 { 43 if (dbusValue == 44 "xyz.openbmc_project.Telemetry.Report.ReportActions.EmitsReadingsUpdate") 45 { 46 return metric_report_definition::ReportActionsEnum::RedfishEvent; 47 } 48 if (dbusValue == 49 "xyz.openbmc_project.Telemetry.Report.ReportActions.LogToMetricReportsCollection") 50 { 51 return metric_report_definition::ReportActionsEnum:: 52 LogToMetricReportsCollection; 53 } 54 return metric_report_definition::ReportActionsEnum::Invalid; 55 } 56 57 inline std::string toDbusReportAction(std::string_view redfishValue) 58 { 59 if (redfishValue == "RedfishEvent") 60 { 61 return "xyz.openbmc_project.Telemetry.Report.ReportActions.EmitsReadingsUpdate"; 62 } 63 if (redfishValue == "LogToMetricReportsCollection") 64 { 65 return "xyz.openbmc_project.Telemetry.Report.ReportActions.LogToMetricReportsCollection"; 66 } 67 return ""; 68 } 69 70 inline metric_report_definition::MetricReportDefinitionType 71 toRedfishReportingType(std::string_view dbusValue) 72 { 73 if (dbusValue == 74 "xyz.openbmc_project.Telemetry.Report.ReportingType.OnChange") 75 { 76 return metric_report_definition::MetricReportDefinitionType::OnChange; 77 } 78 if (dbusValue == 79 "xyz.openbmc_project.Telemetry.Report.ReportingType.OnRequest") 80 { 81 return metric_report_definition::MetricReportDefinitionType::OnRequest; 82 } 83 if (dbusValue == 84 "xyz.openbmc_project.Telemetry.Report.ReportingType.Periodic") 85 { 86 return metric_report_definition::MetricReportDefinitionType::Periodic; 87 } 88 return metric_report_definition::MetricReportDefinitionType::Invalid; 89 } 90 91 inline std::string toDbusReportingType(std::string_view redfishValue) 92 { 93 if (redfishValue == "OnChange") 94 { 95 return "xyz.openbmc_project.Telemetry.Report.ReportingType.OnChange"; 96 } 97 if (redfishValue == "OnRequest") 98 { 99 return "xyz.openbmc_project.Telemetry.Report.ReportingType.OnRequest"; 100 } 101 if (redfishValue == "Periodic") 102 { 103 return "xyz.openbmc_project.Telemetry.Report.ReportingType.Periodic"; 104 } 105 return ""; 106 } 107 108 inline metric_report_definition::CollectionTimeScope 109 toRedfishCollectionTimeScope(std::string_view dbusValue) 110 { 111 if (dbusValue == 112 "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Point") 113 { 114 return metric_report_definition::CollectionTimeScope::Point; 115 } 116 if (dbusValue == 117 "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Interval") 118 { 119 return metric_report_definition::CollectionTimeScope::Interval; 120 } 121 if (dbusValue == 122 "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.StartupInterval") 123 { 124 return metric_report_definition::CollectionTimeScope::StartupInterval; 125 } 126 return metric_report_definition::CollectionTimeScope::Invalid; 127 } 128 129 inline std::string toDbusCollectionTimeScope(std::string_view redfishValue) 130 { 131 if (redfishValue == "Point") 132 { 133 return "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Point"; 134 } 135 if (redfishValue == "Interval") 136 { 137 return "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.Interval"; 138 } 139 if (redfishValue == "StartupInterval") 140 { 141 return "xyz.openbmc_project.Telemetry.Report.CollectionTimescope.StartupInterval"; 142 } 143 return ""; 144 } 145 146 inline metric_report_definition::ReportUpdatesEnum 147 toRedfishReportUpdates(std::string_view dbusValue) 148 { 149 if (dbusValue == 150 "xyz.openbmc_project.Telemetry.Report.ReportUpdates.Overwrite") 151 { 152 return metric_report_definition::ReportUpdatesEnum::Overwrite; 153 } 154 if (dbusValue == 155 "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendWrapsWhenFull") 156 { 157 return metric_report_definition::ReportUpdatesEnum::AppendWrapsWhenFull; 158 } 159 if (dbusValue == 160 "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendStopsWhenFull") 161 { 162 return metric_report_definition::ReportUpdatesEnum::AppendStopsWhenFull; 163 } 164 return metric_report_definition::ReportUpdatesEnum::Invalid; 165 } 166 167 inline std::string toDbusReportUpdates(std::string_view redfishValue) 168 { 169 if (redfishValue == "Overwrite") 170 { 171 return "xyz.openbmc_project.Telemetry.Report.ReportUpdates.Overwrite"; 172 } 173 if (redfishValue == "AppendWrapsWhenFull") 174 { 175 return "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendWrapsWhenFull"; 176 } 177 if (redfishValue == "AppendStopsWhenFull") 178 { 179 return "xyz.openbmc_project.Telemetry.Report.ReportUpdates.AppendStopsWhenFull"; 180 } 181 return ""; 182 } 183 184 inline std::optional<nlohmann::json::array_t> getLinkedTriggers( 185 std::span<const sdbusplus::message::object_path> triggerPaths) 186 { 187 nlohmann::json::array_t triggers; 188 189 for (const sdbusplus::message::object_path& path : triggerPaths) 190 { 191 if (path.parent_path() != 192 "/xyz/openbmc_project/Telemetry/Triggers/TelemetryService") 193 { 194 BMCWEB_LOG_ERROR << "Property Triggers contains invalid value: " 195 << path.str; 196 return std::nullopt; 197 } 198 199 std::string id = path.filename(); 200 if (id.empty()) 201 { 202 BMCWEB_LOG_ERROR << "Property Triggers contains invalid value: " 203 << path.str; 204 return std::nullopt; 205 } 206 nlohmann::json::object_t trigger; 207 trigger["@odata.id"] = 208 boost::urls::format("/redfish/v1/TelemetryService/Triggers/{}", id); 209 triggers.emplace_back(std::move(trigger)); 210 } 211 212 return triggers; 213 } 214 215 inline void 216 fillReportDefinition(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 217 const std::string& id, 218 const dbus::utility::DBusPropertiesMap& properties) 219 { 220 std::vector<std::string> reportActions; 221 ReadingParameters readingParams; 222 std::string reportingType; 223 std::string reportUpdates; 224 std::string name; 225 uint64_t appendLimit = 0; 226 uint64_t interval = 0; 227 bool enabled = false; 228 std::vector<sdbusplus::message::object_path> triggers; 229 230 const bool success = sdbusplus::unpackPropertiesNoThrow( 231 dbus_utils::UnpackErrorPrinter(), properties, "ReportingType", 232 reportingType, "Interval", interval, "ReportActions", reportActions, 233 "ReportUpdates", reportUpdates, "AppendLimit", appendLimit, 234 "ReadingParameters", readingParams, "Name", name, "Enabled", enabled, 235 "Triggers", triggers); 236 237 if (!success) 238 { 239 messages::internalError(asyncResp->res); 240 return; 241 } 242 243 metric_report_definition::MetricReportDefinitionType redfishReportingType = 244 toRedfishReportingType(reportingType); 245 if (redfishReportingType == 246 metric_report_definition::MetricReportDefinitionType::Invalid) 247 { 248 messages::internalError(asyncResp->res); 249 return; 250 } 251 252 asyncResp->res.jsonValue["MetricReportDefinitionType"] = 253 redfishReportingType; 254 255 std::optional<nlohmann::json::array_t> linkedTriggers = 256 getLinkedTriggers(triggers); 257 if (!linkedTriggers) 258 { 259 messages::internalError(asyncResp->res); 260 return; 261 } 262 263 asyncResp->res.jsonValue["Links"]["Triggers"] = std::move(*linkedTriggers); 264 265 nlohmann::json::array_t redfishReportActions; 266 for (const std::string& action : reportActions) 267 { 268 metric_report_definition::ReportActionsEnum redfishAction = 269 toRedfishReportAction(action); 270 if (redfishAction == 271 metric_report_definition::ReportActionsEnum::Invalid) 272 { 273 messages::internalError(asyncResp->res); 274 return; 275 } 276 277 redfishReportActions.emplace_back(redfishAction); 278 } 279 280 asyncResp->res.jsonValue["ReportActions"] = std::move(redfishReportActions); 281 282 nlohmann::json::array_t metrics = nlohmann::json::array(); 283 for (const auto& [sensorData, collectionFunction, collectionTimeScope, 284 collectionDuration] : readingParams) 285 { 286 nlohmann::json::array_t metricProperties; 287 288 for (const auto& [sensorPath, sensorMetadata] : sensorData) 289 { 290 metricProperties.emplace_back(sensorMetadata); 291 } 292 293 nlohmann::json::object_t metric; 294 295 metric_report_definition::CalculationAlgorithmEnum 296 redfishCollectionFunction = 297 telemetry::toRedfishCollectionFunction(collectionFunction); 298 if (redfishCollectionFunction == 299 metric_report_definition::CalculationAlgorithmEnum::Invalid) 300 { 301 messages::internalError(asyncResp->res); 302 return; 303 } 304 metric["CollectionFunction"] = redfishCollectionFunction; 305 306 metric_report_definition::CollectionTimeScope 307 redfishCollectionTimeScope = 308 toRedfishCollectionTimeScope(collectionTimeScope); 309 if (redfishCollectionTimeScope == 310 metric_report_definition::CollectionTimeScope::Invalid) 311 { 312 messages::internalError(asyncResp->res); 313 return; 314 } 315 metric["CollectionTimeScope"] = redfishCollectionTimeScope; 316 317 metric["MetricProperties"] = std::move(metricProperties); 318 metric["CollectionDuration"] = time_utils::toDurationString( 319 std::chrono::milliseconds(collectionDuration)); 320 metrics.emplace_back(std::move(metric)); 321 } 322 asyncResp->res.jsonValue["Metrics"] = std::move(metrics); 323 324 if (enabled) 325 { 326 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 327 } 328 else 329 { 330 asyncResp->res.jsonValue["Status"]["State"] = "Disabled"; 331 } 332 333 metric_report_definition::ReportUpdatesEnum redfishReportUpdates = 334 toRedfishReportUpdates(reportUpdates); 335 if (redfishReportUpdates == 336 metric_report_definition::ReportUpdatesEnum::Invalid) 337 { 338 messages::internalError(asyncResp->res); 339 return; 340 } 341 asyncResp->res.jsonValue["ReportUpdates"] = redfishReportUpdates; 342 343 asyncResp->res.jsonValue["MetricReportDefinitionEnabled"] = enabled; 344 asyncResp->res.jsonValue["AppendLimit"] = appendLimit; 345 asyncResp->res.jsonValue["Name"] = name; 346 asyncResp->res.jsonValue["Schedule"]["RecurrenceInterval"] = 347 time_utils::toDurationString(std::chrono::milliseconds(interval)); 348 asyncResp->res.jsonValue["@odata.type"] = 349 "#MetricReportDefinition.v1_3_0.MetricReportDefinition"; 350 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 351 "/redfish/v1/TelemetryService/MetricReportDefinitions/{}", id); 352 asyncResp->res.jsonValue["Id"] = id; 353 asyncResp->res.jsonValue["MetricReport"]["@odata.id"] = boost::urls::format( 354 "/redfish/v1/TelemetryService/MetricReports/{}", id); 355 } 356 357 struct AddReportArgs 358 { 359 struct MetricArgs 360 { 361 std::vector<std::string> uris; 362 std::string collectionFunction; 363 std::string collectionTimeScope; 364 uint64_t collectionDuration = 0; 365 }; 366 367 std::string id; 368 std::string name; 369 std::string reportingType; 370 std::string reportUpdates; 371 uint64_t appendLimit = std::numeric_limits<uint64_t>::max(); 372 std::vector<std::string> reportActions; 373 uint64_t interval = std::numeric_limits<uint64_t>::max(); 374 std::vector<MetricArgs> metrics; 375 bool metricReportDefinitionEnabled = true; 376 }; 377 378 inline bool toDbusReportActions(crow::Response& res, 379 const std::vector<std::string>& actions, 380 AddReportArgs& args) 381 { 382 size_t index = 0; 383 for (const std::string& action : actions) 384 { 385 std::string dbusReportAction = toDbusReportAction(action); 386 if (dbusReportAction.empty()) 387 { 388 messages::propertyValueNotInList(res, nlohmann::json(action).dump(), 389 "ReportActions/" + 390 std::to_string(index)); 391 return false; 392 } 393 394 args.reportActions.emplace_back(std::move(dbusReportAction)); 395 index++; 396 } 397 return true; 398 } 399 400 inline bool getUserMetric(crow::Response& res, nlohmann::json& metric, 401 AddReportArgs::MetricArgs& metricArgs) 402 { 403 std::optional<std::vector<std::string>> uris; 404 std::optional<std::string> collectionDurationStr; 405 std::optional<std::string> collectionFunction; 406 std::optional<std::string> collectionTimeScopeStr; 407 408 if (!json_util::readJson(metric, res, "MetricProperties", uris, 409 "CollectionFunction", collectionFunction, 410 "CollectionTimeScope", collectionTimeScopeStr, 411 "CollectionDuration", collectionDurationStr)) 412 { 413 return false; 414 } 415 416 if (uris) 417 { 418 metricArgs.uris = std::move(*uris); 419 } 420 421 if (collectionFunction) 422 { 423 std::string dbusCollectionFunction = 424 telemetry::toDbusCollectionFunction(*collectionFunction); 425 if (dbusCollectionFunction.empty()) 426 { 427 messages::propertyValueIncorrect(res, "CollectionFunction", 428 *collectionFunction); 429 return false; 430 } 431 metricArgs.collectionFunction = std::move(dbusCollectionFunction); 432 } 433 434 if (collectionTimeScopeStr) 435 { 436 std::string dbusCollectionTimeScope = 437 toDbusCollectionTimeScope(*collectionTimeScopeStr); 438 if (dbusCollectionTimeScope.empty()) 439 { 440 messages::propertyValueIncorrect(res, "CollectionTimeScope", 441 *collectionTimeScopeStr); 442 return false; 443 } 444 metricArgs.collectionTimeScope = std::move(dbusCollectionTimeScope); 445 } 446 447 if (collectionDurationStr) 448 { 449 std::optional<std::chrono::milliseconds> duration = 450 time_utils::fromDurationString(*collectionDurationStr); 451 452 if (!duration || duration->count() < 0) 453 { 454 messages::propertyValueIncorrect(res, "CollectionDuration", 455 *collectionDurationStr); 456 return false; 457 } 458 459 metricArgs.collectionDuration = 460 static_cast<uint64_t>(duration->count()); 461 } 462 463 return true; 464 } 465 466 inline bool getUserMetrics(crow::Response& res, 467 std::span<nlohmann::json> metrics, 468 std::vector<AddReportArgs::MetricArgs>& result) 469 { 470 result.reserve(metrics.size()); 471 472 for (nlohmann::json& m : metrics) 473 { 474 AddReportArgs::MetricArgs metricArgs; 475 476 if (!getUserMetric(res, m, metricArgs)) 477 { 478 return false; 479 } 480 481 result.emplace_back(std::move(metricArgs)); 482 } 483 484 return true; 485 } 486 487 inline bool getUserParameters(crow::Response& res, const crow::Request& req, 488 AddReportArgs& args) 489 { 490 std::optional<std::string> id; 491 std::optional<std::string> name; 492 std::optional<std::string> reportingTypeStr; 493 std::optional<std::string> reportUpdatesStr; 494 std::optional<uint64_t> appendLimit; 495 std::optional<bool> metricReportDefinitionEnabled; 496 std::optional<std::vector<nlohmann::json>> metrics; 497 std::optional<std::vector<std::string>> reportActionsStr; 498 std::optional<nlohmann::json> schedule; 499 500 if (!json_util::readJsonPatch( 501 req, res, "Id", id, "Name", name, "Metrics", metrics, 502 "MetricReportDefinitionType", reportingTypeStr, "ReportUpdates", 503 reportUpdatesStr, "AppendLimit", appendLimit, "ReportActions", 504 reportActionsStr, "Schedule", schedule, 505 "MetricReportDefinitionEnabled", metricReportDefinitionEnabled)) 506 { 507 return false; 508 } 509 510 if (id) 511 { 512 constexpr const char* allowedCharactersInId = 513 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_"; 514 if (id->empty() || 515 id->find_first_not_of(allowedCharactersInId) != std::string::npos) 516 { 517 messages::propertyValueIncorrect(res, "Id", *id); 518 return false; 519 } 520 args.id = *id; 521 } 522 523 if (name) 524 { 525 args.name = *name; 526 } 527 528 if (reportingTypeStr) 529 { 530 std::string dbusReportingType = toDbusReportingType(*reportingTypeStr); 531 if (dbusReportingType.empty()) 532 { 533 messages::propertyValueNotInList(res, *reportingTypeStr, 534 "MetricReportDefinitionType"); 535 return false; 536 } 537 args.reportingType = dbusReportingType; 538 } 539 540 if (reportUpdatesStr) 541 { 542 std::string dbusReportUpdates = toDbusReportUpdates(*reportUpdatesStr); 543 if (dbusReportUpdates.empty()) 544 { 545 messages::propertyValueNotInList(res, *reportUpdatesStr, 546 "ReportUpdates"); 547 return false; 548 } 549 args.reportUpdates = dbusReportUpdates; 550 } 551 552 if (appendLimit) 553 { 554 args.appendLimit = *appendLimit; 555 } 556 557 if (metricReportDefinitionEnabled) 558 { 559 args.metricReportDefinitionEnabled = *metricReportDefinitionEnabled; 560 } 561 562 if (reportActionsStr) 563 { 564 if (!toDbusReportActions(res, *reportActionsStr, args)) 565 { 566 return false; 567 } 568 } 569 570 if (reportingTypeStr == "Periodic") 571 { 572 if (!schedule) 573 { 574 messages::createFailedMissingReqProperties(res, "Schedule"); 575 return false; 576 } 577 578 std::string durationStr; 579 if (!json_util::readJson(*schedule, res, "RecurrenceInterval", 580 durationStr)) 581 { 582 return false; 583 } 584 585 std::optional<std::chrono::milliseconds> durationNum = 586 time_utils::fromDurationString(durationStr); 587 if (!durationNum || durationNum->count() < 0) 588 { 589 messages::propertyValueIncorrect(res, "RecurrenceInterval", 590 durationStr); 591 return false; 592 } 593 args.interval = static_cast<uint64_t>(durationNum->count()); 594 } 595 596 if (metrics) 597 { 598 if (!getUserMetrics(res, *metrics, args.metrics)) 599 { 600 return false; 601 } 602 } 603 604 return true; 605 } 606 607 inline bool getChassisSensorNodeFromMetrics( 608 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 609 std::span<const AddReportArgs::MetricArgs> metrics, 610 boost::container::flat_set<std::pair<std::string, std::string>>& matched) 611 { 612 for (const auto& metric : metrics) 613 { 614 std::optional<IncorrectMetricUri> error = 615 getChassisSensorNode(metric.uris, matched); 616 if (error) 617 { 618 messages::propertyValueIncorrect(asyncResp->res, error->uri, 619 "MetricProperties/" + 620 std::to_string(error->index)); 621 return false; 622 } 623 } 624 return true; 625 } 626 627 class AddReport 628 { 629 public: 630 AddReport(AddReportArgs argsIn, 631 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : 632 asyncResp(asyncRespIn), 633 args{std::move(argsIn)} 634 {} 635 636 ~AddReport() 637 { 638 boost::asio::post(crow::connections::systemBus->get_io_context(), 639 std::bind_front(&performAddReport, asyncResp, args, 640 std::move(uriToDbus))); 641 } 642 643 static void performAddReport( 644 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 645 const AddReportArgs& args, 646 const boost::container::flat_map<std::string, std::string>& uriToDbus) 647 { 648 if (asyncResp->res.result() != boost::beast::http::status::ok) 649 { 650 return; 651 } 652 653 telemetry::ReadingParameters readingParams; 654 readingParams.reserve(args.metrics.size()); 655 656 for (const auto& metric : args.metrics) 657 { 658 std::vector< 659 std::tuple<sdbusplus::message::object_path, std::string>> 660 sensorParams; 661 sensorParams.reserve(metric.uris.size()); 662 663 for (size_t i = 0; i < metric.uris.size(); i++) 664 { 665 const std::string& uri = metric.uris[i]; 666 auto el = uriToDbus.find(uri); 667 if (el == uriToDbus.end()) 668 { 669 BMCWEB_LOG_ERROR 670 << "Failed to find DBus sensor corresponding to URI " 671 << uri; 672 messages::propertyValueNotInList(asyncResp->res, uri, 673 "MetricProperties/" + 674 std::to_string(i)); 675 return; 676 } 677 678 const std::string& dbusPath = el->second; 679 sensorParams.emplace_back(dbusPath, uri); 680 } 681 682 readingParams.emplace_back( 683 std::move(sensorParams), metric.collectionFunction, 684 metric.collectionTimeScope, metric.collectionDuration); 685 } 686 687 crow::connections::systemBus->async_method_call( 688 [asyncResp, id = args.id, uriToDbus]( 689 const boost::system::error_code& ec, const std::string&) { 690 if (ec == boost::system::errc::file_exists) 691 { 692 messages::resourceAlreadyExists( 693 asyncResp->res, "MetricReportDefinition", "Id", id); 694 return; 695 } 696 if (ec == boost::system::errc::too_many_files_open) 697 { 698 messages::createLimitReachedForResource(asyncResp->res); 699 return; 700 } 701 if (ec == boost::system::errc::argument_list_too_long) 702 { 703 nlohmann::json metricProperties = nlohmann::json::array(); 704 for (const auto& [uri, _] : uriToDbus) 705 { 706 metricProperties.emplace_back(uri); 707 } 708 messages::propertyValueIncorrect(asyncResp->res, 709 metricProperties.dump(), 710 "MetricProperties"); 711 return; 712 } 713 if (ec) 714 { 715 messages::internalError(asyncResp->res); 716 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; 717 return; 718 } 719 720 messages::created(asyncResp->res); 721 }, 722 telemetry::service, "/xyz/openbmc_project/Telemetry/Reports", 723 "xyz.openbmc_project.Telemetry.ReportManager", "AddReport", 724 "TelemetryService/" + args.id, args.name, args.reportingType, 725 args.reportUpdates, args.appendLimit, args.reportActions, 726 args.interval, readingParams, args.metricReportDefinitionEnabled); 727 } 728 729 AddReport(const AddReport&) = delete; 730 AddReport(AddReport&&) = delete; 731 AddReport& operator=(const AddReport&) = delete; 732 AddReport& operator=(AddReport&&) = delete; 733 734 void insert(const std::map<std::string, std::string>& el) 735 { 736 uriToDbus.insert(el.begin(), el.end()); 737 } 738 739 private: 740 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 741 AddReportArgs args; 742 boost::container::flat_map<std::string, std::string> uriToDbus{}; 743 }; 744 } // namespace telemetry 745 746 inline void requestRoutesMetricReportDefinitionCollection(App& app) 747 { 748 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") 749 .privileges(redfish::privileges::getMetricReportDefinitionCollection) 750 .methods(boost::beast::http::verb::get)( 751 [&app](const crow::Request& req, 752 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 753 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 754 { 755 return; 756 } 757 758 asyncResp->res.jsonValue["@odata.type"] = 759 "#MetricReportDefinitionCollection." 760 "MetricReportDefinitionCollection"; 761 asyncResp->res.jsonValue["@odata.id"] = 762 "/redfish/v1/TelemetryService/MetricReportDefinitions"; 763 asyncResp->res.jsonValue["Name"] = "Metric Definition Collection"; 764 constexpr std::array<std::string_view, 1> interfaces{ 765 telemetry::reportInterface}; 766 collection_util::getCollectionMembers( 767 asyncResp, 768 boost::urls::url( 769 "/redfish/v1/TelemetryService/MetricReportDefinitions"), 770 interfaces, 771 "/xyz/openbmc_project/Telemetry/Reports/TelemetryService"); 772 }); 773 774 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") 775 .privileges(redfish::privileges::postMetricReportDefinitionCollection) 776 .methods(boost::beast::http::verb::post)( 777 [&app](const crow::Request& req, 778 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 779 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 780 { 781 return; 782 } 783 784 telemetry::AddReportArgs args; 785 if (!telemetry::getUserParameters(asyncResp->res, req, args)) 786 { 787 return; 788 } 789 790 boost::container::flat_set<std::pair<std::string, std::string>> 791 chassisSensors; 792 if (!telemetry::getChassisSensorNodeFromMetrics(asyncResp, args.metrics, 793 chassisSensors)) 794 { 795 return; 796 } 797 798 auto addReportReq = 799 std::make_shared<telemetry::AddReport>(std::move(args), asyncResp); 800 for (const auto& [chassis, sensorType] : chassisSensors) 801 { 802 retrieveUriToDbusMap( 803 chassis, sensorType, 804 [asyncResp, addReportReq]( 805 const boost::beast::http::status status, 806 const std::map<std::string, std::string>& uriToDbus) { 807 if (status != boost::beast::http::status::ok) 808 { 809 BMCWEB_LOG_ERROR 810 << "Failed to retrieve URI to dbus sensors map with err " 811 << static_cast<unsigned>(status); 812 return; 813 } 814 addReportReq->insert(uriToDbus); 815 }); 816 } 817 }); 818 } 819 820 inline void requestRoutesMetricReportDefinition(App& app) 821 { 822 BMCWEB_ROUTE(app, 823 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 824 .privileges(redfish::privileges::getMetricReportDefinition) 825 .methods(boost::beast::http::verb::get)( 826 [&app](const crow::Request& req, 827 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 828 const std::string& id) { 829 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 830 { 831 return; 832 } 833 834 sdbusplus::asio::getAllProperties( 835 *crow::connections::systemBus, telemetry::service, 836 telemetry::getDbusReportPath(id), telemetry::reportInterface, 837 [asyncResp, 838 id](const boost::system::error_code& ec, 839 const dbus::utility::DBusPropertiesMap& properties) { 840 if (ec.value() == EBADR || 841 ec == boost::system::errc::host_unreachable) 842 { 843 messages::resourceNotFound(asyncResp->res, 844 "MetricReportDefinition", id); 845 return; 846 } 847 if (ec) 848 { 849 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; 850 messages::internalError(asyncResp->res); 851 return; 852 } 853 854 telemetry::fillReportDefinition(asyncResp, id, properties); 855 }); 856 }); 857 858 BMCWEB_ROUTE(app, 859 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 860 .privileges(redfish::privileges::deleteMetricReportDefinitionCollection) 861 .methods(boost::beast::http::verb::delete_)( 862 [&app](const crow::Request& req, 863 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 864 const std::string& id) 865 866 { 867 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 868 { 869 return; 870 } 871 872 const std::string reportPath = telemetry::getDbusReportPath(id); 873 874 crow::connections::systemBus->async_method_call( 875 [asyncResp, id](const boost::system::error_code& ec) { 876 /* 877 * boost::system::errc and std::errc are missing value 878 * for EBADR error that is defined in Linux. 879 */ 880 if (ec.value() == EBADR) 881 { 882 messages::resourceNotFound(asyncResp->res, 883 "MetricReportDefinition", id); 884 return; 885 } 886 887 if (ec) 888 { 889 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; 890 messages::internalError(asyncResp->res); 891 return; 892 } 893 894 asyncResp->res.result(boost::beast::http::status::no_content); 895 }, 896 telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete", 897 "Delete"); 898 }); 899 } 900 } // namespace redfish 901