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<std::variant<nlohmann::json::object_t, std::nullptr_t>>&& 1194 metrics) 1195 { 1196 dbus::utility::getAllProperties( 1197 telemetry::service, telemetry::getDbusReportPath(id), 1198 telemetry::reportInterface, 1199 [asyncResp, id = std::string(id), redfishMetrics = std::move(metrics)]( 1200 boost::system::error_code ec, 1201 const dbus::utility::DBusPropertiesMap& properties) mutable { 1202 if (!verifyCommonErrors(asyncResp->res, id, ec)) 1203 { 1204 return; 1205 } 1206 1207 ReadingParameters readingParams; 1208 1209 const bool success = sdbusplus::unpackPropertiesNoThrow( 1210 dbus_utils::UnpackErrorPrinter(), properties, 1211 "ReadingParameters", readingParams); 1212 1213 if (!success) 1214 { 1215 messages::internalError(asyncResp->res); 1216 return; 1217 } 1218 1219 auto updateMetricsReq = 1220 std::make_shared<UpdateMetrics>(id, asyncResp); 1221 1222 boost::container::flat_set<std::pair<std::string, std::string>> 1223 chassisSensors; 1224 1225 size_t index = 0; 1226 for (std::variant<nlohmann::json::object_t, std::nullptr_t>& 1227 metricVariant : redfishMetrics) 1228 { 1229 nlohmann::json::object_t* metric = 1230 std::get_if<nlohmann::json::object_t>(&metricVariant); 1231 if (metric == nullptr) 1232 { 1233 index++; 1234 continue; 1235 } 1236 1237 AddReportArgs::MetricArgs metricArgs; 1238 std::vector< 1239 std::tuple<sdbusplus::message::object_path, std::string>> 1240 pathAndUri; 1241 1242 if (index < readingParams.size()) 1243 { 1244 const ReadingParameters::value_type& existing = 1245 readingParams[index]; 1246 1247 pathAndUri = std::get<0>(existing); 1248 metricArgs.collectionFunction = std::get<1>(existing); 1249 metricArgs.collectionTimeScope = std::get<2>(existing); 1250 metricArgs.collectionDuration = std::get<3>(existing); 1251 } 1252 1253 if (!getUserMetric(asyncResp->res, *metric, metricArgs)) 1254 { 1255 return; 1256 } 1257 1258 std::optional<IncorrectMetricUri> error = 1259 getChassisSensorNode(metricArgs.uris, chassisSensors); 1260 1261 if (error) 1262 { 1263 messages::propertyValueIncorrect( 1264 asyncResp->res, error->uri, 1265 "MetricProperties/" + std::to_string(error->index)); 1266 return; 1267 } 1268 1269 updateMetricsReq->emplace(pathAndUri, metricArgs); 1270 index++; 1271 } 1272 1273 for (const auto& [chassis, sensorType] : chassisSensors) 1274 { 1275 retrieveUriToDbusMap( 1276 chassis, sensorType, 1277 [asyncResp, updateMetricsReq]( 1278 const boost::beast::http::status status, 1279 const std::map<std::string, std::string>& uriToDbus) { 1280 if (status != boost::beast::http::status::ok) 1281 { 1282 BMCWEB_LOG_ERROR( 1283 "Failed to retrieve URI to dbus sensors map with err {}", 1284 static_cast<unsigned>(status)); 1285 return; 1286 } 1287 updateMetricsReq->insert(uriToDbus); 1288 }); 1289 } 1290 }); 1291 } 1292 1293 inline void handleMetricReportDefinitionCollectionHead( 1294 App& app, const crow::Request& req, 1295 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1296 { 1297 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1298 { 1299 return; 1300 } 1301 asyncResp->res.addHeader( 1302 boost::beast::http::field::link, 1303 "</redfish/v1/JsonSchemas/MetricReportDefinitionCollection/MetricReportDefinitionCollection.json>; rel=describedby"); 1304 } 1305 1306 inline void handleMetricReportDefinitionCollectionGet( 1307 App& app, const crow::Request& req, 1308 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1309 { 1310 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1311 { 1312 return; 1313 } 1314 asyncResp->res.addHeader( 1315 boost::beast::http::field::link, 1316 "</redfish/v1/JsonSchemas/MetricReportDefinition/MetricReportDefinition.json>; rel=describedby"); 1317 1318 asyncResp->res.jsonValue["@odata.type"] = 1319 "#MetricReportDefinitionCollection." 1320 "MetricReportDefinitionCollection"; 1321 asyncResp->res.jsonValue["@odata.id"] = 1322 "/redfish/v1/TelemetryService/MetricReportDefinitions"; 1323 asyncResp->res.jsonValue["Name"] = "Metric Definition Collection"; 1324 constexpr std::array<std::string_view, 1> interfaces{ 1325 telemetry::reportInterface}; 1326 collection_util::getCollectionMembers( 1327 asyncResp, 1328 boost::urls::url( 1329 "/redfish/v1/TelemetryService/MetricReportDefinitions"), 1330 interfaces, "/xyz/openbmc_project/Telemetry/Reports/TelemetryService"); 1331 } 1332 1333 inline void handleReportPatch( 1334 App& app, const crow::Request& req, 1335 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id) 1336 { 1337 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1338 { 1339 return; 1340 } 1341 1342 std::optional<std::string> reportingTypeStr; 1343 std::optional<std::string> reportUpdatesStr; 1344 std::optional<bool> metricReportDefinitionEnabled; 1345 std::optional< 1346 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>> 1347 metrics; 1348 std::optional<std::vector<std::string>> reportActionsStr; 1349 std::optional<std::string> scheduleDurationStr; 1350 1351 if (!json_util::readJsonPatch( // 1352 req, asyncResp->res, // 1353 "MetricReportDefinitionEnabled", metricReportDefinitionEnabled, // 1354 "MetricReportDefinitionType", reportingTypeStr, // 1355 "Metrics", metrics, // 1356 "ReportActions", reportActionsStr, // 1357 "ReportUpdates", reportUpdatesStr, // 1358 "Schedule/RecurrenceInterval", scheduleDurationStr // 1359 )) 1360 { 1361 return; 1362 } 1363 1364 if (metricReportDefinitionEnabled) 1365 { 1366 setReportEnabled(asyncResp, id, *metricReportDefinitionEnabled); 1367 } 1368 1369 if (reportUpdatesStr) 1370 { 1371 setReportUpdates(asyncResp, id, *reportUpdatesStr); 1372 } 1373 1374 if (reportActionsStr) 1375 { 1376 setReportActions(asyncResp, id, *reportActionsStr); 1377 } 1378 1379 if (reportingTypeStr || scheduleDurationStr) 1380 { 1381 setReportTypeAndInterval(asyncResp, id, reportingTypeStr, 1382 scheduleDurationStr); 1383 } 1384 1385 if (metrics) 1386 { 1387 setReportMetrics(asyncResp, id, std::move(*metrics)); 1388 } 1389 } 1390 1391 inline void handleReportDelete( 1392 App& app, const crow::Request& req, 1393 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id) 1394 { 1395 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1396 { 1397 return; 1398 } 1399 1400 const std::string reportPath = getDbusReportPath(id); 1401 1402 crow::connections::systemBus->async_method_call( 1403 [asyncResp, 1404 reportId = std::string(id)](const boost::system::error_code& ec) { 1405 if (!verifyCommonErrors(asyncResp->res, reportId, ec)) 1406 { 1407 return; 1408 } 1409 asyncResp->res.result(boost::beast::http::status::no_content); 1410 }, 1411 service, reportPath, "xyz.openbmc_project.Object.Delete", "Delete"); 1412 } 1413 } // namespace telemetry 1414 1415 inline void afterRetrieveUriToDbusMap( 1416 const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/, 1417 const std::shared_ptr<telemetry::AddReport>& addReportReq, 1418 const boost::beast::http::status status, 1419 const std::map<std::string, std::string>& uriToDbus) 1420 { 1421 if (status != boost::beast::http::status::ok) 1422 { 1423 BMCWEB_LOG_ERROR( 1424 "Failed to retrieve URI to dbus sensors map with err {}", 1425 static_cast<unsigned>(status)); 1426 return; 1427 } 1428 addReportReq->insert(uriToDbus); 1429 } 1430 1431 inline void handleMetricReportDefinitionsPost( 1432 App& app, const crow::Request& req, 1433 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1434 { 1435 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1436 { 1437 return; 1438 } 1439 1440 telemetry::AddReportArgs args; 1441 if (!telemetry::getUserParameters(asyncResp->res, req, args)) 1442 { 1443 return; 1444 } 1445 1446 boost::container::flat_set<std::pair<std::string, std::string>> 1447 chassisSensors; 1448 if (!telemetry::getChassisSensorNodeFromMetrics(asyncResp, args.metrics, 1449 chassisSensors)) 1450 { 1451 return; 1452 } 1453 1454 auto addReportReq = 1455 std::make_shared<telemetry::AddReport>(std::move(args), asyncResp); 1456 for (const auto& [chassis, sensorType] : chassisSensors) 1457 { 1458 retrieveUriToDbusMap(chassis, sensorType, 1459 std::bind_front(afterRetrieveUriToDbusMap, 1460 asyncResp, addReportReq)); 1461 } 1462 } 1463 1464 inline void 1465 handleMetricReportHead(App& app, const crow::Request& req, 1466 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1467 const std::string& /*id*/) 1468 { 1469 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1470 { 1471 return; 1472 } 1473 asyncResp->res.addHeader( 1474 boost::beast::http::field::link, 1475 "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby"); 1476 } 1477 1478 inline void handleMetricReportGet( 1479 App& app, const crow::Request& req, 1480 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 1481 { 1482 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1483 { 1484 return; 1485 } 1486 asyncResp->res.addHeader( 1487 boost::beast::http::field::link, 1488 "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby"); 1489 1490 dbus::utility::getAllProperties( 1491 telemetry::service, telemetry::getDbusReportPath(id), 1492 telemetry::reportInterface, 1493 [asyncResp, id](const boost::system::error_code& ec, 1494 const dbus::utility::DBusPropertiesMap& properties) { 1495 if (!redfish::telemetry::verifyCommonErrors(asyncResp->res, id, ec)) 1496 { 1497 return; 1498 } 1499 1500 telemetry::fillReportDefinition(asyncResp, id, properties); 1501 }); 1502 } 1503 1504 inline void handleMetricReportDelete( 1505 App& app, const crow::Request& req, 1506 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 1507 1508 { 1509 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1510 { 1511 return; 1512 } 1513 1514 const std::string reportPath = telemetry::getDbusReportPath(id); 1515 1516 crow::connections::systemBus->async_method_call( 1517 [asyncResp, id](const boost::system::error_code& ec) { 1518 /* 1519 * boost::system::errc and std::errc are missing value 1520 * for EBADR error that is defined in Linux. 1521 */ 1522 if (ec.value() == EBADR) 1523 { 1524 messages::resourceNotFound(asyncResp->res, 1525 "MetricReportDefinition", id); 1526 return; 1527 } 1528 1529 if (ec) 1530 { 1531 BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); 1532 messages::internalError(asyncResp->res); 1533 return; 1534 } 1535 1536 asyncResp->res.result(boost::beast::http::status::no_content); 1537 }, 1538 telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete", 1539 "Delete"); 1540 } 1541 1542 inline void requestRoutesMetricReportDefinitionCollection(App& app) 1543 { 1544 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") 1545 .privileges(redfish::privileges::headMetricReportDefinitionCollection) 1546 .methods(boost::beast::http::verb::head)(std::bind_front( 1547 telemetry::handleMetricReportDefinitionCollectionHead, 1548 std::ref(app))); 1549 1550 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") 1551 .privileges(redfish::privileges::getMetricReportDefinitionCollection) 1552 .methods(boost::beast::http::verb::get)(std::bind_front( 1553 telemetry::handleMetricReportDefinitionCollectionGet, 1554 std::ref(app))); 1555 1556 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") 1557 .privileges(redfish::privileges::postMetricReportDefinitionCollection) 1558 .methods(boost::beast::http::verb::post)( 1559 std::bind_front(handleMetricReportDefinitionsPost, std::ref(app))); 1560 } 1561 1562 inline void requestRoutesMetricReportDefinition(App& app) 1563 { 1564 BMCWEB_ROUTE(app, 1565 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 1566 .privileges(redfish::privileges::getMetricReportDefinition) 1567 .methods(boost::beast::http::verb::head)( 1568 std::bind_front(handleMetricReportHead, std::ref(app))); 1569 1570 BMCWEB_ROUTE(app, 1571 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 1572 .privileges(redfish::privileges::getMetricReportDefinition) 1573 .methods(boost::beast::http::verb::get)( 1574 std::bind_front(handleMetricReportGet, std::ref(app))); 1575 1576 BMCWEB_ROUTE(app, 1577 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 1578 .privileges(redfish::privileges::deleteMetricReportDefinition) 1579 .methods(boost::beast::http::verb::delete_)( 1580 std::bind_front(handleMetricReportDelete, std::ref(app))); 1581 1582 BMCWEB_ROUTE(app, 1583 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 1584 .privileges(redfish::privileges::patchMetricReportDefinition) 1585 .methods(boost::beast::http::verb::patch)( 1586 std::bind_front(telemetry::handleReportPatch, std::ref(app))); 1587 } 1588 } // namespace redfish 1589