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( 709 asyncResp->res, metricProperties, "MetricProperties"); 710 return; 711 } 712 if (ec) 713 { 714 messages::internalError(asyncResp->res); 715 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; 716 return; 717 } 718 719 messages::created(asyncResp->res); 720 }, 721 telemetry::service, "/xyz/openbmc_project/Telemetry/Reports", 722 "xyz.openbmc_project.Telemetry.ReportManager", "AddReport", 723 "TelemetryService/" + args.id, args.name, args.reportingType, 724 args.reportUpdates, args.appendLimit, args.reportActions, 725 args.interval, readingParams, args.metricReportDefinitionEnabled); 726 } 727 728 AddReport(const AddReport&) = delete; 729 AddReport(AddReport&&) = delete; 730 AddReport& operator=(const AddReport&) = delete; 731 AddReport& operator=(AddReport&&) = delete; 732 733 void insert(const std::map<std::string, std::string>& el) 734 { 735 uriToDbus.insert(el.begin(), el.end()); 736 } 737 738 private: 739 std::shared_ptr<bmcweb::AsyncResp> asyncResp; 740 AddReportArgs args; 741 boost::container::flat_map<std::string, std::string> uriToDbus{}; 742 }; 743 } // namespace telemetry 744 745 inline void handleMetricReportDefinitionCollectionHead( 746 App& app, const crow::Request& req, 747 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 748 { 749 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 750 { 751 return; 752 } 753 asyncResp->res.addHeader( 754 boost::beast::http::field::link, 755 "</redfish/v1/JsonSchemas/MetricReportDefinitionCollection/MetricReportDefinitionCollection.json>; rel=describedby"); 756 } 757 758 inline void handleMetricReportDefinitionCollectionGet( 759 App& app, const crow::Request& req, 760 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 761 { 762 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 763 { 764 return; 765 } 766 767 asyncResp->res.jsonValue["@odata.type"] = 768 "#MetricReportDefinitionCollection." 769 "MetricReportDefinitionCollection"; 770 asyncResp->res.jsonValue["@odata.id"] = 771 "/redfish/v1/TelemetryService/MetricReportDefinitions"; 772 asyncResp->res.jsonValue["Name"] = "Metric Definition Collection"; 773 constexpr std::array<std::string_view, 1> interfaces{ 774 telemetry::reportInterface}; 775 collection_util::getCollectionMembers( 776 asyncResp, 777 boost::urls::url( 778 "/redfish/v1/TelemetryService/MetricReportDefinitions"), 779 interfaces, "/xyz/openbmc_project/Telemetry/Reports/TelemetryService"); 780 } 781 782 inline void 783 handleMetricReportHead(App& app, const crow::Request& req, 784 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 785 const std::string& /*id*/) 786 { 787 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 788 { 789 return; 790 } 791 asyncResp->res.addHeader( 792 boost::beast::http::field::link, 793 "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby"); 794 } 795 796 inline void 797 handleMetricReportGet(App& app, const crow::Request& req, 798 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 799 const std::string& id) 800 { 801 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 802 { 803 return; 804 } 805 asyncResp->res.addHeader( 806 boost::beast::http::field::link, 807 "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby"); 808 809 sdbusplus::asio::getAllProperties( 810 *crow::connections::systemBus, telemetry::service, 811 telemetry::getDbusReportPath(id), telemetry::reportInterface, 812 [asyncResp, id](const boost::system::error_code& ec, 813 const dbus::utility::DBusPropertiesMap& properties) { 814 if (ec.value() == EBADR || ec == boost::system::errc::host_unreachable) 815 { 816 messages::resourceNotFound(asyncResp->res, "MetricReportDefinition", 817 id); 818 return; 819 } 820 if (ec) 821 { 822 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; 823 messages::internalError(asyncResp->res); 824 return; 825 } 826 827 telemetry::fillReportDefinition(asyncResp, id, properties); 828 }); 829 } 830 831 inline void handleMetricReportDelete( 832 App& app, const crow::Request& req, 833 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 834 835 { 836 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 837 { 838 return; 839 } 840 841 const std::string reportPath = telemetry::getDbusReportPath(id); 842 843 crow::connections::systemBus->async_method_call( 844 [asyncResp, id](const boost::system::error_code& ec) { 845 /* 846 * boost::system::errc and std::errc are missing value 847 * for EBADR error that is defined in Linux. 848 */ 849 if (ec.value() == EBADR) 850 { 851 messages::resourceNotFound(asyncResp->res, "MetricReportDefinition", 852 id); 853 return; 854 } 855 856 if (ec) 857 { 858 BMCWEB_LOG_ERROR << "respHandler DBus error " << ec; 859 messages::internalError(asyncResp->res); 860 return; 861 } 862 863 asyncResp->res.result(boost::beast::http::status::no_content); 864 }, 865 telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete", 866 "Delete"); 867 } 868 869 inline void requestRoutesMetricReportDefinitionCollection(App& app) 870 { 871 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") 872 .privileges(redfish::privileges::headMetricReportDefinitionCollection) 873 .methods(boost::beast::http::verb::head)(std::bind_front( 874 handleMetricReportDefinitionCollectionHead, std::ref(app))); 875 876 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") 877 .privileges(redfish::privileges::getMetricReportDefinitionCollection) 878 .methods(boost::beast::http::verb::get)(std::bind_front( 879 handleMetricReportDefinitionCollectionGet, std::ref(app))); 880 881 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") 882 .privileges(redfish::privileges::postMetricReportDefinitionCollection) 883 .methods(boost::beast::http::verb::post)( 884 [&app](const crow::Request& req, 885 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 886 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 887 { 888 return; 889 } 890 891 telemetry::AddReportArgs args; 892 if (!telemetry::getUserParameters(asyncResp->res, req, args)) 893 { 894 return; 895 } 896 897 boost::container::flat_set<std::pair<std::string, std::string>> 898 chassisSensors; 899 if (!telemetry::getChassisSensorNodeFromMetrics(asyncResp, args.metrics, 900 chassisSensors)) 901 { 902 return; 903 } 904 905 auto addReportReq = 906 std::make_shared<telemetry::AddReport>(std::move(args), asyncResp); 907 for (const auto& [chassis, sensorType] : chassisSensors) 908 { 909 retrieveUriToDbusMap( 910 chassis, sensorType, 911 [asyncResp, addReportReq]( 912 const boost::beast::http::status status, 913 const std::map<std::string, std::string>& uriToDbus) { 914 if (status != boost::beast::http::status::ok) 915 { 916 BMCWEB_LOG_ERROR 917 << "Failed to retrieve URI to dbus sensors map with err " 918 << static_cast<unsigned>(status); 919 return; 920 } 921 addReportReq->insert(uriToDbus); 922 }); 923 } 924 }); 925 } 926 927 inline void requestRoutesMetricReportDefinition(App& app) 928 { 929 BMCWEB_ROUTE(app, 930 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 931 .privileges(redfish::privileges::getMetricReportDefinition) 932 .methods(boost::beast::http::verb::head)( 933 std::bind_front(handleMetricReportHead, std::ref(app))); 934 935 BMCWEB_ROUTE(app, 936 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 937 .privileges(redfish::privileges::getMetricReportDefinition) 938 .methods(boost::beast::http::verb::get)( 939 std::bind_front(handleMetricReportGet, std::ref(app))); 940 941 BMCWEB_ROUTE(app, 942 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 943 .privileges(redfish::privileges::deleteMetricReportDefinitionCollection) 944 .methods(boost::beast::http::verb::delete_)( 945 std::bind_front(handleMetricReportDelete, std::ref(app))); 946 } 947 } // namespace redfish 948