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