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