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 verifyCommonErrors(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 false; 74 } 75 76 if (ec == boost::system::errc::file_exists) 77 { 78 messages::resourceAlreadyExists(res, "MetricReportDefinition", "Id", 79 id); 80 return false; 81 } 82 83 if (ec == boost::system::errc::too_many_files_open) 84 { 85 messages::createLimitReachedForResource(res); 86 return false; 87 } 88 89 if (ec) 90 { 91 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 92 messages::internalError(res); 93 return false; 94 } 95 96 return true; 97 } 98 99 inline metric_report_definition::ReportActionsEnum 100 toRedfishReportAction(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 206 toRedfishReportUpdates(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 = nlohmann::json::array(); 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 = 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 = 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) 756 { 757 messages::created(asyncResp->res); 758 return; 759 } 760 761 if (ec == boost::system::errc::invalid_argument) 762 { 763 const sd_bus_error* errorMessage = msg.get_error(); 764 if (errorMessage != nullptr) 765 { 766 for (const auto& arg : 767 {"Id", "Name", "ReportingType", "AppendLimit", "ReportActions", 768 "Interval", "ReportUpdates", "ReadingParameters"}) 769 { 770 if (!handleParamError(asyncResp->res, errorMessage->message, 771 arg)) 772 { 773 return; 774 } 775 } 776 } 777 } 778 if (!verifyCommonErrors(asyncResp->res, args.id, ec)) 779 { 780 return; 781 } 782 messages::internalError(asyncResp->res); 783 } 784 785 class AddReport 786 { 787 public: 788 AddReport(AddReportArgs&& argsIn, 789 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : 790 asyncResp(asyncRespIn), args(std::move(argsIn)) 791 {} 792 793 ~AddReport() 794 { 795 boost::asio::post(crow::connections::systemBus->get_io_context(), 796 std::bind_front(&performAddReport, asyncResp, args, 797 std::move(uriToDbus))); 798 } 799 800 static void performAddReport( 801 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 802 const AddReportArgs& args, 803 const boost::container::flat_map<std::string, std::string>& uriToDbus) 804 { 805 if (asyncResp->res.result() != boost::beast::http::status::ok) 806 { 807 return; 808 } 809 810 telemetry::ReadingParameters readingParams; 811 readingParams.reserve(args.metrics.size()); 812 813 for (const auto& metric : args.metrics) 814 { 815 std::vector< 816 std::tuple<sdbusplus::message::object_path, std::string>> 817 sensorParams; 818 sensorParams.reserve(metric.uris.size()); 819 820 for (size_t i = 0; i < metric.uris.size(); i++) 821 { 822 const std::string& uri = metric.uris[i]; 823 auto el = uriToDbus.find(uri); 824 if (el == uriToDbus.end()) 825 { 826 BMCWEB_LOG_ERROR( 827 "Failed to find DBus sensor corresponding to URI {}", 828 uri); 829 messages::propertyValueNotInList( 830 asyncResp->res, uri, 831 "MetricProperties/" + std::to_string(i)); 832 return; 833 } 834 835 const std::string& dbusPath = el->second; 836 sensorParams.emplace_back(dbusPath, uri); 837 } 838 839 readingParams.emplace_back( 840 std::move(sensorParams), metric.collectionFunction, 841 metric.collectionTimeScope, metric.collectionDuration); 842 } 843 crow::connections::systemBus->async_method_call( 844 [asyncResp, args](const boost::system::error_code& ec, 845 const sdbusplus::message_t& msg, 846 const std::string& /*arg1*/) { 847 afterAddReport(asyncResp, args, ec, msg); 848 }, 849 telemetry::service, "/xyz/openbmc_project/Telemetry/Reports", 850 "xyz.openbmc_project.Telemetry.ReportManager", "AddReport", 851 "TelemetryService/" + args.id, args.name, args.reportingType, 852 args.reportUpdates, args.appendLimit, args.reportActions, 853 args.interval, readingParams, args.metricReportDefinitionEnabled); 854 } 855 856 AddReport(const AddReport&) = delete; 857 AddReport(AddReport&&) = delete; 858 AddReport& operator=(const AddReport&) = delete; 859 AddReport& operator=(AddReport&&) = delete; 860 861 void insert(const std::map<std::string, std::string>& el) 862 { 863 uriToDbus.insert(el.begin(), el.end()); 864 } 865 866 private: 867 const std::shared_ptr<bmcweb::AsyncResp> asyncResp; 868 AddReportArgs args; 869 boost::container::flat_map<std::string, std::string> uriToDbus; 870 }; 871 872 inline std::optional< 873 std::vector<std::tuple<sdbusplus::message::object_path, std::string>>> 874 sensorPathToUri( 875 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 876 std::span<const std::string> uris, 877 const std::map<std::string, std::string>& metricPropertyToDbusPaths) 878 { 879 std::vector<std::tuple<sdbusplus::message::object_path, std::string>> 880 result; 881 882 for (const std::string& uri : uris) 883 { 884 auto it = metricPropertyToDbusPaths.find(uri); 885 if (it == metricPropertyToDbusPaths.end()) 886 { 887 messages::propertyValueNotInList(asyncResp->res, uri, 888 "MetricProperties"); 889 return {}; 890 } 891 result.emplace_back(it->second, uri); 892 } 893 894 return result; 895 } 896 897 inline void afterSetReadingParams( 898 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 899 const std::string& reportId, const boost::system::error_code& ec, 900 const sdbusplus::message_t& msg) 901 { 902 if (!ec) 903 { 904 messages::success(asyncResp->res); 905 return; 906 } 907 if (ec == boost::system::errc::invalid_argument) 908 { 909 const sd_bus_error* errorMessage = msg.get_error(); 910 if (errorMessage != nullptr) 911 { 912 for (const auto& arg : {"Id", "ReadingParameters"}) 913 { 914 if (!handleParamError(asyncResp->res, errorMessage->message, 915 arg)) 916 { 917 return; 918 } 919 } 920 } 921 } 922 if (!verifyCommonErrors(asyncResp->res, reportId, ec)) 923 { 924 return; 925 } 926 messages::internalError(asyncResp->res); 927 } 928 929 inline void setReadingParams( 930 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 931 const std::string& reportId, ReadingParameters readingParams, 932 const std::vector<std::vector<std::string>>& readingParamsUris, 933 const std::map<std::string, std::string>& metricPropertyToDbusPaths) 934 { 935 if (asyncResp->res.result() != boost::beast::http::status::ok) 936 { 937 return; 938 } 939 940 for (size_t index = 0; index < readingParamsUris.size(); ++index) 941 { 942 std::span<const std::string> newUris = readingParamsUris[index]; 943 944 const std::optional<std::vector< 945 std::tuple<sdbusplus::message::object_path, std::string>>> 946 readingParam = 947 sensorPathToUri(asyncResp, newUris, metricPropertyToDbusPaths); 948 949 if (!readingParam) 950 { 951 return; 952 } 953 954 for (const std::tuple<sdbusplus::message::object_path, std::string>& 955 value : *readingParam) 956 { 957 std::get<0>(readingParams[index]).emplace_back(value); 958 } 959 } 960 961 crow::connections::systemBus->async_method_call( 962 [asyncResp, reportId](const boost::system::error_code& ec, 963 const sdbusplus::message_t& msg) { 964 afterSetReadingParams(asyncResp, reportId, ec, msg); 965 }, 966 "xyz.openbmc_project.Telemetry", getDbusReportPath(reportId), 967 "org.freedesktop.DBus.Properties", "Set", 968 "xyz.openbmc_project.Telemetry.Report", "ReadingParameters", 969 dbus::utility::DbusVariantType{readingParams}); 970 } 971 972 class UpdateMetrics 973 { 974 public: 975 UpdateMetrics(std::string_view idIn, 976 const std::shared_ptr<bmcweb::AsyncResp>& asyncRespIn) : 977 id(idIn), asyncResp(asyncRespIn) 978 {} 979 980 ~UpdateMetrics() 981 { 982 boost::asio::post( 983 crow::connections::systemBus->get_io_context(), 984 std::bind_front(&setReadingParams, asyncResp, id, 985 std::move(readingParams), readingParamsUris, 986 metricPropertyToDbusPaths)); 987 } 988 989 UpdateMetrics(const UpdateMetrics&) = delete; 990 UpdateMetrics(UpdateMetrics&&) = delete; 991 UpdateMetrics& operator=(const UpdateMetrics&) = delete; 992 UpdateMetrics& operator=(UpdateMetrics&&) = delete; 993 994 std::string id; 995 std::map<std::string, std::string> metricPropertyToDbusPaths; 996 997 void insert(const std::map<std::string, std::string>& 998 additionalMetricPropertyToDbusPaths) 999 { 1000 metricPropertyToDbusPaths.insert( 1001 additionalMetricPropertyToDbusPaths.begin(), 1002 additionalMetricPropertyToDbusPaths.end()); 1003 } 1004 1005 void emplace( 1006 std::span< 1007 const std::tuple<sdbusplus::message::object_path, std::string>> 1008 pathAndUri, 1009 const AddReportArgs::MetricArgs& metricArgs) 1010 { 1011 readingParamsUris.emplace_back(metricArgs.uris); 1012 readingParams.emplace_back( 1013 std::vector(pathAndUri.begin(), pathAndUri.end()), 1014 metricArgs.collectionFunction, metricArgs.collectionTimeScope, 1015 metricArgs.collectionDuration); 1016 } 1017 1018 private: 1019 const std::shared_ptr<bmcweb::AsyncResp> asyncResp; 1020 std::vector<std::vector<std::string>> readingParamsUris; 1021 ReadingParameters readingParams; 1022 }; 1023 1024 inline void 1025 setReportEnabled(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1026 std::string_view id, bool enabled) 1027 { 1028 crow::connections::systemBus->async_method_call( 1029 [asyncResp, id = std::string(id)](const boost::system::error_code& ec) { 1030 if (!verifyCommonErrors(asyncResp->res, id, ec)) 1031 { 1032 return; 1033 } 1034 }, 1035 "xyz.openbmc_project.Telemetry", getDbusReportPath(id), 1036 "org.freedesktop.DBus.Properties", "Set", 1037 "xyz.openbmc_project.Telemetry.Report", "Enabled", 1038 dbus::utility::DbusVariantType{enabled}); 1039 } 1040 1041 inline void afterSetReportingProperties( 1042 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id, 1043 const boost::system::error_code& ec, const sdbusplus::message_t& msg) 1044 { 1045 if (!ec) 1046 { 1047 asyncResp->res.result(boost::beast::http::status::no_content); 1048 return; 1049 } 1050 1051 if (ec == boost::system::errc::invalid_argument) 1052 { 1053 const sd_bus_error* errorMessage = msg.get_error(); 1054 if (errorMessage != nullptr) 1055 { 1056 for (const auto& arg : {"Id", "ReportingType", "Interval"}) 1057 { 1058 if (!handleParamError(asyncResp->res, errorMessage->message, 1059 arg)) 1060 { 1061 return; 1062 } 1063 } 1064 } 1065 } 1066 if (!verifyCommonErrors(asyncResp->res, id, ec)) 1067 { 1068 return; 1069 } 1070 messages::internalError(asyncResp->res); 1071 } 1072 1073 inline void setReportTypeAndInterval( 1074 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id, 1075 const std::optional<std::string>& reportingType, 1076 const std::optional<std::string>& recurrenceIntervalStr) 1077 { 1078 std::string dbusReportingType; 1079 if (reportingType) 1080 { 1081 dbusReportingType = toDbusReportingType(*reportingType); 1082 if (dbusReportingType.empty()) 1083 { 1084 messages::propertyValueNotInList(asyncResp->res, *reportingType, 1085 "MetricReportDefinitionType"); 1086 return; 1087 } 1088 } 1089 1090 uint64_t recurrenceInterval = std::numeric_limits<uint64_t>::max(); 1091 if (recurrenceIntervalStr) 1092 { 1093 std::optional<std::chrono::milliseconds> durationNum = 1094 time_utils::fromDurationString(*recurrenceIntervalStr); 1095 if (!durationNum || durationNum->count() < 0) 1096 { 1097 messages::propertyValueIncorrect( 1098 asyncResp->res, "RecurrenceInterval", *recurrenceIntervalStr); 1099 return; 1100 } 1101 1102 recurrenceInterval = static_cast<uint64_t>(durationNum->count()); 1103 } 1104 1105 crow::connections::systemBus->async_method_call( 1106 [asyncResp, id = std::string(id)](const boost::system::error_code& ec, 1107 const sdbusplus::message_t& msg) { 1108 afterSetReportingProperties(asyncResp, id, ec, msg); 1109 }, 1110 "xyz.openbmc_project.Telemetry", getDbusReportPath(id), 1111 "xyz.openbmc_project.Telemetry.Report", "SetReportingProperties", 1112 dbusReportingType, recurrenceInterval); 1113 } 1114 1115 inline void afterSetReportUpdates( 1116 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id, 1117 const boost::system::error_code& ec, const sdbusplus::message_t& msg) 1118 { 1119 if (!ec) 1120 { 1121 asyncResp->res.result(boost::beast::http::status::no_content); 1122 return; 1123 } 1124 if (ec == boost::system::errc::invalid_argument) 1125 { 1126 const sd_bus_error* errorMessage = msg.get_error(); 1127 if (errorMessage != nullptr) 1128 { 1129 for (const auto& arg : {"Id", "ReportUpdates"}) 1130 { 1131 if (!handleParamError(asyncResp->res, errorMessage->message, 1132 arg)) 1133 { 1134 return; 1135 } 1136 } 1137 } 1138 } 1139 if (!verifyCommonErrors(asyncResp->res, id, ec)) 1140 { 1141 return; 1142 } 1143 } 1144 1145 inline void 1146 setReportUpdates(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1147 std::string_view id, const std::string& reportUpdates) 1148 { 1149 std::string dbusReportUpdates = toDbusReportUpdates(reportUpdates); 1150 if (dbusReportUpdates.empty()) 1151 { 1152 messages::propertyValueNotInList(asyncResp->res, reportUpdates, 1153 "ReportUpdates"); 1154 return; 1155 } 1156 crow::connections::systemBus->async_method_call( 1157 [asyncResp, id = std::string(id)](const boost::system::error_code& ec, 1158 const sdbusplus::message_t& msg) { 1159 afterSetReportUpdates(asyncResp, id, ec, msg); 1160 }, 1161 "xyz.openbmc_project.Telemetry", getDbusReportPath(id), 1162 "org.freedesktop.DBus.Properties", "Set", 1163 "xyz.openbmc_project.Telemetry.Report", "ReportUpdates", 1164 dbus::utility::DbusVariantType{dbusReportUpdates}); 1165 } 1166 1167 inline void afterSetReportActions( 1168 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id, 1169 const boost::system::error_code& ec, const sdbusplus::message_t& msg) 1170 { 1171 if (ec == boost::system::errc::invalid_argument) 1172 { 1173 const sd_bus_error* errorMessage = msg.get_error(); 1174 if (errorMessage != nullptr) 1175 { 1176 for (const auto& arg : {"Id", "ReportActions"}) 1177 { 1178 if (!handleParamError(asyncResp->res, errorMessage->message, 1179 arg)) 1180 { 1181 return; 1182 } 1183 } 1184 } 1185 } 1186 1187 if (!verifyCommonErrors(asyncResp->res, id, ec)) 1188 { 1189 return; 1190 } 1191 1192 messages::internalError(asyncResp->res); 1193 } 1194 1195 inline void setReportActions( 1196 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id, 1197 const std::vector<std::string>& reportActions) 1198 { 1199 std::vector<std::string> dbusReportActions; 1200 if (!toDbusReportActions(asyncResp->res, reportActions, dbusReportActions)) 1201 { 1202 return; 1203 } 1204 1205 crow::connections::systemBus->async_method_call( 1206 [asyncResp, id = std::string(id)](const boost::system::error_code& ec, 1207 const sdbusplus::message_t& msg) { 1208 afterSetReportActions(asyncResp, id, ec, msg); 1209 }, 1210 "xyz.openbmc_project.Telemetry", getDbusReportPath(id), 1211 "org.freedesktop.DBus.Properties", "Set", 1212 "xyz.openbmc_project.Telemetry.Report", "ReportActions", 1213 dbus::utility::DbusVariantType{dbusReportActions}); 1214 } 1215 1216 inline void setReportMetrics( 1217 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id, 1218 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>&& 1219 metrics) 1220 { 1221 dbus::utility::getAllProperties( 1222 telemetry::service, telemetry::getDbusReportPath(id), 1223 telemetry::reportInterface, 1224 [asyncResp, id = std::string(id), redfishMetrics = std::move(metrics)]( 1225 boost::system::error_code ec, 1226 const dbus::utility::DBusPropertiesMap& properties) mutable { 1227 if (!verifyCommonErrors(asyncResp->res, id, ec)) 1228 { 1229 return; 1230 } 1231 1232 ReadingParameters readingParams; 1233 1234 const bool success = sdbusplus::unpackPropertiesNoThrow( 1235 dbus_utils::UnpackErrorPrinter(), properties, 1236 "ReadingParameters", readingParams); 1237 1238 if (!success) 1239 { 1240 messages::internalError(asyncResp->res); 1241 return; 1242 } 1243 1244 auto updateMetricsReq = 1245 std::make_shared<UpdateMetrics>(id, asyncResp); 1246 1247 boost::container::flat_set<std::pair<std::string, std::string>> 1248 chassisSensors; 1249 1250 size_t index = 0; 1251 for (std::variant<nlohmann::json::object_t, std::nullptr_t>& 1252 metricVariant : redfishMetrics) 1253 { 1254 nlohmann::json::object_t* metric = 1255 std::get_if<nlohmann::json::object_t>(&metricVariant); 1256 if (metric == nullptr) 1257 { 1258 index++; 1259 continue; 1260 } 1261 1262 AddReportArgs::MetricArgs metricArgs; 1263 std::vector< 1264 std::tuple<sdbusplus::message::object_path, std::string>> 1265 pathAndUri; 1266 1267 if (index < readingParams.size()) 1268 { 1269 const ReadingParameters::value_type& existing = 1270 readingParams[index]; 1271 1272 if (metric->empty()) 1273 { 1274 pathAndUri = std::get<0>(existing); 1275 } 1276 metricArgs.collectionFunction = std::get<1>(existing); 1277 metricArgs.collectionTimeScope = std::get<2>(existing); 1278 metricArgs.collectionDuration = std::get<3>(existing); 1279 } 1280 1281 if (!getUserMetric(asyncResp->res, *metric, metricArgs)) 1282 { 1283 return; 1284 } 1285 1286 std::optional<IncorrectMetricUri> error = 1287 getChassisSensorNode(metricArgs.uris, chassisSensors); 1288 1289 if (error) 1290 { 1291 messages::propertyValueIncorrect( 1292 asyncResp->res, error->uri, 1293 "MetricProperties/" + std::to_string(error->index)); 1294 return; 1295 } 1296 1297 updateMetricsReq->emplace(pathAndUri, metricArgs); 1298 index++; 1299 } 1300 1301 for (const auto& [chassis, sensorType] : chassisSensors) 1302 { 1303 retrieveUriToDbusMap( 1304 chassis, sensorType, 1305 [asyncResp, updateMetricsReq]( 1306 const boost::beast::http::status status, 1307 const std::map<std::string, std::string>& uriToDbus) { 1308 if (status != boost::beast::http::status::ok) 1309 { 1310 BMCWEB_LOG_ERROR( 1311 "Failed to retrieve URI to dbus sensors map with err {}", 1312 static_cast<unsigned>(status)); 1313 return; 1314 } 1315 updateMetricsReq->insert(uriToDbus); 1316 }); 1317 } 1318 }); 1319 } 1320 1321 inline void handleMetricReportDefinitionCollectionHead( 1322 App& app, const crow::Request& req, 1323 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1324 { 1325 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1326 { 1327 return; 1328 } 1329 asyncResp->res.addHeader( 1330 boost::beast::http::field::link, 1331 "</redfish/v1/JsonSchemas/MetricReportDefinitionCollection/MetricReportDefinitionCollection.json>; rel=describedby"); 1332 } 1333 1334 inline void handleMetricReportDefinitionCollectionGet( 1335 App& app, const crow::Request& req, 1336 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1337 { 1338 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1339 { 1340 return; 1341 } 1342 asyncResp->res.addHeader( 1343 boost::beast::http::field::link, 1344 "</redfish/v1/JsonSchemas/MetricReportDefinition/MetricReportDefinition.json>; rel=describedby"); 1345 1346 asyncResp->res.jsonValue["@odata.type"] = 1347 "#MetricReportDefinitionCollection." 1348 "MetricReportDefinitionCollection"; 1349 asyncResp->res.jsonValue["@odata.id"] = 1350 "/redfish/v1/TelemetryService/MetricReportDefinitions"; 1351 asyncResp->res.jsonValue["Name"] = "Metric Definition Collection"; 1352 constexpr std::array<std::string_view, 1> interfaces{ 1353 telemetry::reportInterface}; 1354 collection_util::getCollectionMembers( 1355 asyncResp, 1356 boost::urls::url( 1357 "/redfish/v1/TelemetryService/MetricReportDefinitions"), 1358 interfaces, "/xyz/openbmc_project/Telemetry/Reports/TelemetryService"); 1359 } 1360 1361 inline void handleReportPatch( 1362 App& app, const crow::Request& req, 1363 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id) 1364 { 1365 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1366 { 1367 return; 1368 } 1369 1370 std::optional<std::string> reportingTypeStr; 1371 std::optional<std::string> reportUpdatesStr; 1372 std::optional<bool> metricReportDefinitionEnabled; 1373 std::optional< 1374 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>> 1375 metrics; 1376 std::optional<std::vector<std::string>> reportActionsStr; 1377 std::optional<std::string> scheduleDurationStr; 1378 1379 if (!json_util::readJsonPatch( // 1380 req, asyncResp->res, // 1381 "MetricReportDefinitionEnabled", metricReportDefinitionEnabled, // 1382 "MetricReportDefinitionType", reportingTypeStr, // 1383 "Metrics", metrics, // 1384 "ReportActions", reportActionsStr, // 1385 "ReportUpdates", reportUpdatesStr, // 1386 "Schedule/RecurrenceInterval", scheduleDurationStr // 1387 )) 1388 { 1389 return; 1390 } 1391 1392 if (metricReportDefinitionEnabled) 1393 { 1394 setReportEnabled(asyncResp, id, *metricReportDefinitionEnabled); 1395 } 1396 1397 if (reportUpdatesStr) 1398 { 1399 setReportUpdates(asyncResp, id, *reportUpdatesStr); 1400 } 1401 1402 if (reportActionsStr) 1403 { 1404 setReportActions(asyncResp, id, *reportActionsStr); 1405 } 1406 1407 if (reportingTypeStr || scheduleDurationStr) 1408 { 1409 setReportTypeAndInterval(asyncResp, id, reportingTypeStr, 1410 scheduleDurationStr); 1411 } 1412 1413 if (metrics) 1414 { 1415 setReportMetrics(asyncResp, id, std::move(*metrics)); 1416 } 1417 } 1418 1419 inline void handleReportDelete( 1420 App& app, const crow::Request& req, 1421 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string_view id) 1422 { 1423 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1424 { 1425 return; 1426 } 1427 1428 const std::string reportPath = getDbusReportPath(id); 1429 1430 crow::connections::systemBus->async_method_call( 1431 [asyncResp, 1432 reportId = std::string(id)](const boost::system::error_code& ec) { 1433 if (!verifyCommonErrors(asyncResp->res, reportId, ec)) 1434 { 1435 return; 1436 } 1437 asyncResp->res.result(boost::beast::http::status::no_content); 1438 }, 1439 service, reportPath, "xyz.openbmc_project.Object.Delete", "Delete"); 1440 } 1441 } // namespace telemetry 1442 1443 inline void afterRetrieveUriToDbusMap( 1444 const std::shared_ptr<bmcweb::AsyncResp>& /*asyncResp*/, 1445 const std::shared_ptr<telemetry::AddReport>& addReportReq, 1446 const boost::beast::http::status status, 1447 const std::map<std::string, std::string>& uriToDbus) 1448 { 1449 if (status != boost::beast::http::status::ok) 1450 { 1451 BMCWEB_LOG_ERROR( 1452 "Failed to retrieve URI to dbus sensors map with err {}", 1453 static_cast<unsigned>(status)); 1454 return; 1455 } 1456 addReportReq->insert(uriToDbus); 1457 } 1458 1459 inline void handleMetricReportDefinitionsPost( 1460 App& app, const crow::Request& req, 1461 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1462 { 1463 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1464 { 1465 return; 1466 } 1467 1468 telemetry::AddReportArgs args; 1469 if (!telemetry::getUserParameters(asyncResp->res, req, args)) 1470 { 1471 return; 1472 } 1473 1474 boost::container::flat_set<std::pair<std::string, std::string>> 1475 chassisSensors; 1476 if (!telemetry::getChassisSensorNodeFromMetrics(asyncResp, args.metrics, 1477 chassisSensors)) 1478 { 1479 return; 1480 } 1481 1482 auto addReportReq = 1483 std::make_shared<telemetry::AddReport>(std::move(args), asyncResp); 1484 for (const auto& [chassis, sensorType] : chassisSensors) 1485 { 1486 retrieveUriToDbusMap(chassis, sensorType, 1487 std::bind_front(afterRetrieveUriToDbusMap, 1488 asyncResp, addReportReq)); 1489 } 1490 } 1491 1492 inline void 1493 handleMetricReportHead(App& app, const crow::Request& req, 1494 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1495 const std::string& /*id*/) 1496 { 1497 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1498 { 1499 return; 1500 } 1501 asyncResp->res.addHeader( 1502 boost::beast::http::field::link, 1503 "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby"); 1504 } 1505 1506 inline void handleMetricReportGet( 1507 App& app, const crow::Request& req, 1508 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 1509 { 1510 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1511 { 1512 return; 1513 } 1514 asyncResp->res.addHeader( 1515 boost::beast::http::field::link, 1516 "</redfish/v1/JsonSchemas/MetricReport/MetricReport.json>; rel=describedby"); 1517 1518 dbus::utility::getAllProperties( 1519 telemetry::service, telemetry::getDbusReportPath(id), 1520 telemetry::reportInterface, 1521 [asyncResp, id](const boost::system::error_code& ec, 1522 const dbus::utility::DBusPropertiesMap& properties) { 1523 if (!redfish::telemetry::verifyCommonErrors(asyncResp->res, id, ec)) 1524 { 1525 return; 1526 } 1527 1528 telemetry::fillReportDefinition(asyncResp, id, properties); 1529 }); 1530 } 1531 1532 inline void handleMetricReportDelete( 1533 App& app, const crow::Request& req, 1534 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 1535 1536 { 1537 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1538 { 1539 return; 1540 } 1541 1542 const std::string reportPath = telemetry::getDbusReportPath(id); 1543 1544 crow::connections::systemBus->async_method_call( 1545 [asyncResp, id](const boost::system::error_code& ec) { 1546 /* 1547 * boost::system::errc and std::errc are missing value 1548 * for EBADR error that is defined in Linux. 1549 */ 1550 if (ec.value() == EBADR) 1551 { 1552 messages::resourceNotFound(asyncResp->res, 1553 "MetricReportDefinition", id); 1554 return; 1555 } 1556 1557 if (ec) 1558 { 1559 BMCWEB_LOG_ERROR("respHandler DBus error {}", ec); 1560 messages::internalError(asyncResp->res); 1561 return; 1562 } 1563 1564 asyncResp->res.result(boost::beast::http::status::no_content); 1565 }, 1566 telemetry::service, reportPath, "xyz.openbmc_project.Object.Delete", 1567 "Delete"); 1568 } 1569 1570 inline void requestRoutesMetricReportDefinitionCollection(App& app) 1571 { 1572 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") 1573 .privileges(redfish::privileges::headMetricReportDefinitionCollection) 1574 .methods(boost::beast::http::verb::head)(std::bind_front( 1575 telemetry::handleMetricReportDefinitionCollectionHead, 1576 std::ref(app))); 1577 1578 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") 1579 .privileges(redfish::privileges::getMetricReportDefinitionCollection) 1580 .methods(boost::beast::http::verb::get)(std::bind_front( 1581 telemetry::handleMetricReportDefinitionCollectionGet, 1582 std::ref(app))); 1583 1584 BMCWEB_ROUTE(app, "/redfish/v1/TelemetryService/MetricReportDefinitions/") 1585 .privileges(redfish::privileges::postMetricReportDefinitionCollection) 1586 .methods(boost::beast::http::verb::post)( 1587 std::bind_front(handleMetricReportDefinitionsPost, std::ref(app))); 1588 } 1589 1590 inline void requestRoutesMetricReportDefinition(App& app) 1591 { 1592 BMCWEB_ROUTE(app, 1593 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 1594 .privileges(redfish::privileges::getMetricReportDefinition) 1595 .methods(boost::beast::http::verb::head)( 1596 std::bind_front(handleMetricReportHead, std::ref(app))); 1597 1598 BMCWEB_ROUTE(app, 1599 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 1600 .privileges(redfish::privileges::getMetricReportDefinition) 1601 .methods(boost::beast::http::verb::get)( 1602 std::bind_front(handleMetricReportGet, std::ref(app))); 1603 1604 BMCWEB_ROUTE(app, 1605 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 1606 .privileges(redfish::privileges::deleteMetricReportDefinition) 1607 .methods(boost::beast::http::verb::delete_)( 1608 std::bind_front(handleMetricReportDelete, std::ref(app))); 1609 1610 BMCWEB_ROUTE(app, 1611 "/redfish/v1/TelemetryService/MetricReportDefinitions/<str>/") 1612 .privileges(redfish::privileges::patchMetricReportDefinition) 1613 .methods(boost::beast::http::verb::patch)( 1614 std::bind_front(telemetry::handleReportPatch, std::ref(app))); 1615 } 1616 } // namespace redfish 1617