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