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