1 // SPDX-License-Identifier: Apache-2.0 2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors 3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation 4 #pragma once 5 6 #include "bmcweb_config.h" 7 8 #include "app.hpp" 9 #include "async_resp.hpp" 10 #include "dbus_singleton.hpp" 11 #include "dbus_utility.hpp" 12 #include "error_messages.hpp" 13 #include "generated/enums/action_info.hpp" 14 #include "generated/enums/chassis.hpp" 15 #include "generated/enums/resource.hpp" 16 #include "http_request.hpp" 17 #include "led.hpp" 18 #include "logging.hpp" 19 #include "query.hpp" 20 #include "registries/privilege_registry.hpp" 21 #include "utils/asset_utils.hpp" 22 #include "utils/chassis_utils.hpp" 23 #include "utils/collection.hpp" 24 #include "utils/dbus_utils.hpp" 25 #include "utils/json_utils.hpp" 26 27 #include <asm-generic/errno.h> 28 29 #include <boost/beast/http/field.hpp> 30 #include <boost/beast/http/verb.hpp> 31 #include <boost/system/error_code.hpp> 32 #include <boost/url/format.hpp> 33 #include <boost/url/url.hpp> 34 #include <nlohmann/json.hpp> 35 #include <sdbusplus/message/native_types.hpp> 36 #include <sdbusplus/unpack_properties.hpp> 37 38 #include <algorithm> 39 #include <array> 40 #include <format> 41 #include <functional> 42 #include <memory> 43 #include <optional> 44 #include <ranges> 45 #include <string> 46 #include <string_view> 47 #include <utility> 48 #include <vector> 49 50 namespace redfish 51 { 52 53 inline chassis::ChassisType translateChassisTypeToRedfish( 54 const std::string_view& chassisType) 55 { 56 if (chassisType == 57 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Blade") 58 { 59 return chassis::ChassisType::Blade; 60 } 61 if (chassisType == 62 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Component") 63 { 64 return chassis::ChassisType::Component; 65 } 66 if (chassisType == 67 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Enclosure") 68 { 69 return chassis::ChassisType::Enclosure; 70 } 71 if (chassisType == 72 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Module") 73 { 74 return chassis::ChassisType::Module; 75 } 76 if (chassisType == 77 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.RackMount") 78 { 79 return chassis::ChassisType::RackMount; 80 } 81 if (chassisType == 82 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.StandAlone") 83 { 84 return chassis::ChassisType::StandAlone; 85 } 86 if (chassisType == 87 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.StorageEnclosure") 88 { 89 return chassis::ChassisType::StorageEnclosure; 90 } 91 if (chassisType == 92 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Zone") 93 { 94 return chassis::ChassisType::Zone; 95 } 96 return chassis::ChassisType::Invalid; 97 } 98 99 /** 100 * @brief Retrieves resources over dbus to link to the chassis 101 * 102 * @param[in] asyncResp - Shared pointer for completing asynchronous 103 * calls 104 * @param[in] path - Chassis dbus path to look for the storage. 105 * 106 * Calls the Association endpoints on the path + "/storage" and add the link of 107 * json["Links"]["Storage@odata.count"] = 108 * {"@odata.id", "/redfish/v1/Storage/" + resourceId} 109 * 110 * @return None. 111 */ 112 inline void getStorageLink(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 113 const sdbusplus::message::object_path& path) 114 { 115 dbus::utility::getProperty<std::vector<std::string>>( 116 "xyz.openbmc_project.ObjectMapper", (path / "storage").str, 117 "xyz.openbmc_project.Association", "endpoints", 118 [asyncResp](const boost::system::error_code& ec, 119 const std::vector<std::string>& storageList) { 120 if (ec) 121 { 122 BMCWEB_LOG_DEBUG("getStorageLink got DBUS response error"); 123 return; 124 } 125 126 nlohmann::json::array_t storages; 127 for (const std::string& storagePath : storageList) 128 { 129 std::string id = 130 sdbusplus::message::object_path(storagePath).filename(); 131 if (id.empty()) 132 { 133 continue; 134 } 135 136 nlohmann::json::object_t storage; 137 storage["@odata.id"] = 138 boost::urls::format("/redfish/v1/Systems/{}/Storage/{}", 139 BMCWEB_REDFISH_SYSTEM_URI_NAME, id); 140 storages.emplace_back(std::move(storage)); 141 } 142 asyncResp->res.jsonValue["Links"]["Storage@odata.count"] = 143 storages.size(); 144 asyncResp->res.jsonValue["Links"]["Storage"] = std::move(storages); 145 }); 146 } 147 148 /** 149 * @brief Retrieves chassis state properties over dbus 150 * 151 * @param[in] asyncResp - Shared pointer for completing asynchronous calls. 152 * 153 * @return None. 154 */ 155 inline void getChassisState(std::shared_ptr<bmcweb::AsyncResp> asyncResp) 156 { 157 dbus::utility::getProperty<std::string>( 158 "xyz.openbmc_project.State.Chassis", 159 "/xyz/openbmc_project/state/chassis0", 160 "xyz.openbmc_project.State.Chassis", "CurrentPowerState", 161 [asyncResp{std::move(asyncResp)}](const boost::system::error_code& ec, 162 const std::string& chassisState) { 163 if (ec) 164 { 165 if (ec == boost::system::errc::host_unreachable) 166 { 167 // Service not available, no error, just don't return 168 // chassis state info 169 BMCWEB_LOG_DEBUG("Service not available {}", ec); 170 return; 171 } 172 BMCWEB_LOG_DEBUG("DBUS response error {}", ec); 173 messages::internalError(asyncResp->res); 174 return; 175 } 176 177 BMCWEB_LOG_DEBUG("Chassis state: {}", chassisState); 178 // Verify Chassis State 179 if (chassisState == 180 "xyz.openbmc_project.State.Chassis.PowerState.On") 181 { 182 asyncResp->res.jsonValue["PowerState"] = 183 resource::PowerState::On; 184 asyncResp->res.jsonValue["Status"]["State"] = 185 resource::State::Enabled; 186 } 187 else if (chassisState == 188 "xyz.openbmc_project.State.Chassis.PowerState.Off") 189 { 190 asyncResp->res.jsonValue["PowerState"] = 191 resource::PowerState::Off; 192 asyncResp->res.jsonValue["Status"]["State"] = 193 resource::State::StandbyOffline; 194 } 195 }); 196 } 197 198 /** 199 * Retrieves physical security properties over dbus 200 */ 201 inline void handlePhysicalSecurityGetSubTree( 202 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 203 const boost::system::error_code& ec, 204 const dbus::utility::MapperGetSubTreeResponse& subtree) 205 { 206 if (ec) 207 { 208 // do not add err msg in redfish response, because this is not 209 // mandatory property 210 BMCWEB_LOG_INFO("DBUS error: no matched iface {}", ec); 211 return; 212 } 213 // Iterate over all retrieved ObjectPaths. 214 for (const auto& object : subtree) 215 { 216 if (!object.second.empty()) 217 { 218 const auto& service = object.second.front(); 219 220 BMCWEB_LOG_DEBUG("Get intrusion status by service "); 221 222 dbus::utility::getProperty<std::string>( 223 service.first, object.first, 224 "xyz.openbmc_project.Chassis.Intrusion", "Status", 225 [asyncResp](const boost::system::error_code& ec1, 226 const std::string& value) { 227 if (ec1) 228 { 229 // do not add err msg in redfish response, because this 230 // is not 231 // mandatory property 232 BMCWEB_LOG_ERROR("DBUS response error {}", ec1); 233 return; 234 } 235 asyncResp->res.jsonValue["PhysicalSecurity"] 236 ["IntrusionSensorNumber"] = 1; 237 asyncResp->res 238 .jsonValue["PhysicalSecurity"]["IntrusionSensor"] = 239 value; 240 }); 241 242 return; 243 } 244 } 245 } 246 247 inline void handleChassisCollectionGet( 248 App& app, const crow::Request& req, 249 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 250 { 251 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 252 { 253 return; 254 } 255 asyncResp->res.jsonValue["@odata.type"] = 256 "#ChassisCollection.ChassisCollection"; 257 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Chassis"; 258 asyncResp->res.jsonValue["Name"] = "Chassis Collection"; 259 260 collection_util::getCollectionMembers( 261 asyncResp, boost::urls::url("/redfish/v1/Chassis"), chassisInterfaces, 262 "/xyz/openbmc_project/inventory"); 263 } 264 265 inline void getChassisContainedBy( 266 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 267 const std::string& chassisId, const boost::system::error_code& ec, 268 const dbus::utility::MapperGetSubTreePathsResponse& upstreamChassisPaths) 269 { 270 if (ec) 271 { 272 if (ec.value() != EBADR) 273 { 274 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 275 messages::internalError(asyncResp->res); 276 } 277 return; 278 } 279 if (upstreamChassisPaths.empty()) 280 { 281 return; 282 } 283 if (upstreamChassisPaths.size() > 1) 284 { 285 BMCWEB_LOG_ERROR("{} is contained by multiple chassis", chassisId); 286 messages::internalError(asyncResp->res); 287 return; 288 } 289 290 sdbusplus::message::object_path upstreamChassisPath( 291 upstreamChassisPaths[0]); 292 std::string upstreamChassis = upstreamChassisPath.filename(); 293 if (upstreamChassis.empty()) 294 { 295 BMCWEB_LOG_WARNING("Malformed upstream Chassis path {} on {}", 296 upstreamChassisPath.str, chassisId); 297 return; 298 } 299 300 asyncResp->res.jsonValue["Links"]["ContainedBy"]["@odata.id"] = 301 boost::urls::format("/redfish/v1/Chassis/{}", upstreamChassis); 302 } 303 304 inline void getChassisContains( 305 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 306 const std::string& chassisId, const boost::system::error_code& ec, 307 const dbus::utility::MapperGetSubTreePathsResponse& downstreamChassisPaths) 308 { 309 if (ec) 310 { 311 if (ec.value() != EBADR) 312 { 313 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 314 messages::internalError(asyncResp->res); 315 } 316 return; 317 } 318 if (downstreamChassisPaths.empty()) 319 { 320 return; 321 } 322 nlohmann::json& jValue = asyncResp->res.jsonValue["Links"]["Contains"]; 323 if (!jValue.is_array()) 324 { 325 // Create the array if it was empty 326 jValue = nlohmann::json::array(); 327 } 328 for (const auto& p : downstreamChassisPaths) 329 { 330 sdbusplus::message::object_path downstreamChassisPath(p); 331 std::string downstreamChassis = downstreamChassisPath.filename(); 332 if (downstreamChassis.empty()) 333 { 334 BMCWEB_LOG_WARNING("Malformed downstream Chassis path {} on {}", 335 downstreamChassisPath.str, chassisId); 336 continue; 337 } 338 nlohmann::json link; 339 link["@odata.id"] = 340 boost::urls::format("/redfish/v1/Chassis/{}", downstreamChassis); 341 jValue.push_back(std::move(link)); 342 } 343 asyncResp->res.jsonValue["Links"]["Contains@odata.count"] = jValue.size(); 344 } 345 346 inline void getChassisConnectivity( 347 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 348 const std::string& chassisId, const std::string& chassisPath) 349 { 350 BMCWEB_LOG_DEBUG("Get chassis connectivity"); 351 352 dbus::utility::getAssociatedSubTreePaths( 353 chassisPath + "/contained_by", 354 sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, 355 chassisInterfaces, 356 std::bind_front(getChassisContainedBy, asyncResp, chassisId)); 357 358 dbus::utility::getAssociatedSubTreePaths( 359 chassisPath + "/containing", 360 sdbusplus::message::object_path("/xyz/openbmc_project/inventory"), 0, 361 chassisInterfaces, 362 std::bind_front(getChassisContains, asyncResp, chassisId)); 363 } 364 365 /** 366 * ChassisCollection derived class for delivering Chassis Collection Schema 367 * Functions triggers appropriate requests on DBus 368 */ 369 inline void requestRoutesChassisCollection(App& app) 370 { 371 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/") 372 .privileges(redfish::privileges::getChassisCollection) 373 .methods(boost::beast::http::verb::get)( 374 std::bind_front(handleChassisCollectionGet, std::ref(app))); 375 } 376 377 inline void getChassisLocationCode( 378 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 379 const std::string& connectionName, const std::string& path) 380 { 381 dbus::utility::getProperty<std::string>( 382 connectionName, path, 383 "xyz.openbmc_project.Inventory.Decorator.LocationCode", "LocationCode", 384 [asyncResp](const boost::system::error_code& ec, 385 const std::string& property) { 386 if (ec) 387 { 388 BMCWEB_LOG_ERROR("DBUS response error for Location"); 389 messages::internalError(asyncResp->res); 390 return; 391 } 392 393 asyncResp->res 394 .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = 395 property; 396 }); 397 } 398 399 inline void getChassisUUID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 400 const std::string& connectionName, 401 const std::string& path) 402 { 403 dbus::utility::getProperty<std::string>( 404 connectionName, path, "xyz.openbmc_project.Common.UUID", "UUID", 405 [asyncResp](const boost::system::error_code& ec, 406 const std::string& chassisUUID) { 407 if (ec) 408 { 409 BMCWEB_LOG_ERROR("DBUS response error for UUID"); 410 messages::internalError(asyncResp->res); 411 return; 412 } 413 asyncResp->res.jsonValue["UUID"] = chassisUUID; 414 }); 415 } 416 417 inline void handleDecoratorAssetProperties( 418 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 419 const std::string& chassisId, const std::string& path, 420 const dbus::utility::DBusPropertiesMap& propertiesList) 421 { 422 asset_utils::extractAssetInfo(asyncResp, ""_json_pointer, propertiesList, 423 true); 424 425 asyncResp->res.jsonValue["Name"] = chassisId; 426 asyncResp->res.jsonValue["Id"] = chassisId; 427 428 if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_POWER_THERMAL) 429 { 430 asyncResp->res.jsonValue["Thermal"]["@odata.id"] = 431 boost::urls::format("/redfish/v1/Chassis/{}/Thermal", chassisId); 432 // Power object 433 asyncResp->res.jsonValue["Power"]["@odata.id"] = 434 boost::urls::format("/redfish/v1/Chassis/{}/Power", chassisId); 435 } 436 437 if constexpr (BMCWEB_REDFISH_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM) 438 { 439 asyncResp->res.jsonValue["ThermalSubsystem"]["@odata.id"] = 440 boost::urls::format("/redfish/v1/Chassis/{}/ThermalSubsystem", 441 chassisId); 442 asyncResp->res.jsonValue["PowerSubsystem"]["@odata.id"] = 443 boost::urls::format("/redfish/v1/Chassis/{}/PowerSubsystem", 444 chassisId); 445 asyncResp->res.jsonValue["EnvironmentMetrics"]["@odata.id"] = 446 boost::urls::format("/redfish/v1/Chassis/{}/EnvironmentMetrics", 447 chassisId); 448 } 449 // SensorCollection 450 asyncResp->res.jsonValue["Sensors"]["@odata.id"] = 451 boost::urls::format("/redfish/v1/Chassis/{}/Sensors", chassisId); 452 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 453 454 nlohmann::json::array_t computerSystems; 455 nlohmann::json::object_t system; 456 system["@odata.id"] = 457 std::format("/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); 458 computerSystems.emplace_back(std::move(system)); 459 asyncResp->res.jsonValue["Links"]["ComputerSystems"] = 460 std::move(computerSystems); 461 462 nlohmann::json::array_t managedBy; 463 nlohmann::json::object_t manager; 464 manager["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}", 465 BMCWEB_REDFISH_MANAGER_URI_NAME); 466 managedBy.emplace_back(std::move(manager)); 467 asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); 468 getChassisState(asyncResp); 469 getStorageLink(asyncResp, path); 470 } 471 472 inline void handleChassisProperties( 473 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 474 const dbus::utility::DBusPropertiesMap& propertiesList) 475 { 476 const std::string* type = nullptr; 477 478 const bool success = sdbusplus::unpackPropertiesNoThrow( 479 dbus_utils::UnpackErrorPrinter(), propertiesList, "Type", type); 480 481 if (!success) 482 { 483 messages::internalError(asyncResp->res); 484 return; 485 } 486 487 // Chassis Type is a required property in Redfish 488 // If there is an error or some enum we don't support just sit it to Rack 489 // Mount 490 asyncResp->res.jsonValue["ChassisType"] = chassis::ChassisType::RackMount; 491 492 if (type != nullptr) 493 { 494 auto chassisType = translateChassisTypeToRedfish(*type); 495 if (chassisType != chassis::ChassisType::Invalid) 496 { 497 asyncResp->res.jsonValue["ChassisType"] = chassisType; 498 } 499 } 500 } 501 502 inline void handleChassisGetSubTree( 503 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 504 const std::string& chassisId, const boost::system::error_code& ec, 505 const dbus::utility::MapperGetSubTreeResponse& subtree) 506 { 507 if (ec) 508 { 509 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 510 messages::internalError(asyncResp->res); 511 return; 512 } 513 // Iterate over all retrieved ObjectPaths. 514 for (const std::pair< 515 std::string, 516 std::vector<std::pair<std::string, std::vector<std::string>>>>& 517 object : subtree) 518 { 519 const std::string& path = object.first; 520 const std::vector<std::pair<std::string, std::vector<std::string>>>& 521 connectionNames = object.second; 522 523 sdbusplus::message::object_path objPath(path); 524 if (objPath.filename() != chassisId) 525 { 526 continue; 527 } 528 529 getChassisConnectivity(asyncResp, chassisId, path); 530 531 if (connectionNames.empty()) 532 { 533 BMCWEB_LOG_ERROR("Got 0 Connection names"); 534 continue; 535 } 536 537 asyncResp->res.jsonValue["@odata.type"] = "#Chassis.v1_22_0.Chassis"; 538 asyncResp->res.jsonValue["@odata.id"] = 539 boost::urls::format("/redfish/v1/Chassis/{}", chassisId); 540 asyncResp->res.jsonValue["Name"] = "Chassis Collection"; 541 asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]["target"] = 542 boost::urls::format("/redfish/v1/Chassis/{}/Actions/Chassis.Reset", 543 chassisId); 544 asyncResp->res 545 .jsonValue["Actions"]["#Chassis.Reset"]["@Redfish.ActionInfo"] = 546 boost::urls::format("/redfish/v1/Chassis/{}/ResetActionInfo", 547 chassisId); 548 dbus::utility::getAssociationEndPoints( 549 path + "/drive", 550 [asyncResp, chassisId](const boost::system::error_code& ec3, 551 const dbus::utility::MapperEndPoints& resp) { 552 if (ec3 || resp.empty()) 553 { 554 return; // no drives = no failures 555 } 556 557 nlohmann::json reference; 558 reference["@odata.id"] = boost::urls::format( 559 "/redfish/v1/Chassis/{}/Drives", chassisId); 560 asyncResp->res.jsonValue["Drives"] = std::move(reference); 561 }); 562 563 const std::string& connectionName = connectionNames[0].first; 564 565 const std::vector<std::string>& interfaces2 = connectionNames[0].second; 566 const std::array<const char*, 3> hasIndicatorLed = { 567 "xyz.openbmc_project.Inventory.Item.Chassis", 568 "xyz.openbmc_project.Inventory.Item.Panel", 569 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; 570 571 const std::string assetTagInterface = 572 "xyz.openbmc_project.Inventory.Decorator.AssetTag"; 573 const std::string replaceableInterface = 574 "xyz.openbmc_project.Inventory.Decorator.Replaceable"; 575 const std::string revisionInterface = 576 "xyz.openbmc_project.Inventory.Decorator.Revision"; 577 for (const auto& interface : interfaces2) 578 { 579 if (interface == assetTagInterface) 580 { 581 dbus::utility::getProperty<std::string>( 582 connectionName, path, assetTagInterface, "AssetTag", 583 [asyncResp, chassisId](const boost::system::error_code& ec2, 584 const std::string& property) { 585 if (ec2) 586 { 587 BMCWEB_LOG_ERROR( 588 "DBus response error for AssetTag: {}", ec2); 589 messages::internalError(asyncResp->res); 590 return; 591 } 592 asyncResp->res.jsonValue["AssetTag"] = property; 593 }); 594 } 595 else if (interface == replaceableInterface) 596 { 597 dbus::utility::getProperty<bool>( 598 connectionName, path, replaceableInterface, "HotPluggable", 599 [asyncResp, chassisId](const boost::system::error_code& ec2, 600 const bool property) { 601 if (ec2) 602 { 603 BMCWEB_LOG_ERROR( 604 "DBus response error for HotPluggable: {}", 605 ec2); 606 messages::internalError(asyncResp->res); 607 return; 608 } 609 asyncResp->res.jsonValue["HotPluggable"] = property; 610 }); 611 } 612 else if (interface == revisionInterface) 613 { 614 dbus::utility::getProperty<std::string>( 615 connectionName, path, revisionInterface, "Version", 616 [asyncResp, chassisId](const boost::system::error_code& ec2, 617 const std::string& property) { 618 if (ec2) 619 { 620 BMCWEB_LOG_ERROR( 621 "DBus response error for Version: {}", ec2); 622 messages::internalError(asyncResp->res); 623 return; 624 } 625 asyncResp->res.jsonValue["Version"] = property; 626 }); 627 } 628 } 629 630 for (const char* interface : hasIndicatorLed) 631 { 632 if (std::ranges::find(interfaces2, interface) != interfaces2.end()) 633 { 634 if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) 635 { 636 getIndicatorLedState(asyncResp); 637 } 638 getLocationIndicatorActive(asyncResp, objPath); 639 break; 640 } 641 } 642 643 dbus::utility::getAllProperties( 644 *crow::connections::systemBus, connectionName, path, 645 "xyz.openbmc_project.Inventory.Decorator.Asset", 646 [asyncResp, chassisId, 647 path](const boost::system::error_code&, 648 const dbus::utility::DBusPropertiesMap& propertiesList) { 649 handleDecoratorAssetProperties(asyncResp, chassisId, path, 650 propertiesList); 651 }); 652 653 dbus::utility::getAllProperties( 654 *crow::connections::systemBus, connectionName, path, 655 "xyz.openbmc_project.Inventory.Item.Chassis", 656 [asyncResp]( 657 const boost::system::error_code&, 658 const dbus::utility::DBusPropertiesMap& propertiesList) { 659 handleChassisProperties(asyncResp, propertiesList); 660 }); 661 662 for (const auto& interface : interfaces2) 663 { 664 if (interface == "xyz.openbmc_project.Common.UUID") 665 { 666 getChassisUUID(asyncResp, connectionName, path); 667 } 668 else if (interface == 669 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 670 { 671 getChassisLocationCode(asyncResp, connectionName, path); 672 } 673 } 674 675 return; 676 } 677 678 // Couldn't find an object with that name. return an error 679 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 680 } 681 682 inline void handleChassisGet( 683 App& app, const crow::Request& req, 684 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 685 const std::string& chassisId) 686 { 687 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 688 { 689 return; 690 } 691 692 dbus::utility::getSubTree( 693 "/xyz/openbmc_project/inventory", 0, chassisInterfaces, 694 std::bind_front(handleChassisGetSubTree, asyncResp, chassisId)); 695 696 constexpr std::array<std::string_view, 1> interfaces2 = { 697 "xyz.openbmc_project.Chassis.Intrusion"}; 698 699 dbus::utility::getSubTree( 700 "/xyz/openbmc_project", 0, interfaces2, 701 std::bind_front(handlePhysicalSecurityGetSubTree, asyncResp)); 702 } 703 704 inline void handleChassisPatch( 705 App& app, const crow::Request& req, 706 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 707 const std::string& param) 708 { 709 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 710 { 711 return; 712 } 713 std::optional<bool> locationIndicatorActive; 714 std::optional<std::string> indicatorLed; 715 716 if (param.empty()) 717 { 718 return; 719 } 720 721 if (!json_util::readJsonPatch( // 722 req, asyncResp->res, // 723 "IndicatorLED", indicatorLed, // 724 "LocationIndicatorActive", locationIndicatorActive // 725 )) 726 { 727 return; 728 } 729 730 if (!locationIndicatorActive && !indicatorLed) 731 { 732 return; // delete this when we support more patch properties 733 } 734 if (indicatorLed) 735 { 736 if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) 737 { 738 asyncResp->res.addHeader( 739 boost::beast::http::field::warning, 740 "299 - \"IndicatorLED is deprecated. Use LocationIndicatorActive instead.\""); 741 } 742 else 743 { 744 messages::propertyUnknown(asyncResp->res, "IndicatorLED"); 745 return; 746 } 747 } 748 749 const std::string& chassisId = param; 750 751 dbus::utility::getSubTree( 752 "/xyz/openbmc_project/inventory", 0, chassisInterfaces, 753 [asyncResp, chassisId, locationIndicatorActive, 754 indicatorLed](const boost::system::error_code& ec, 755 const dbus::utility::MapperGetSubTreeResponse& subtree) { 756 if (ec) 757 { 758 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 759 messages::internalError(asyncResp->res); 760 return; 761 } 762 763 // Iterate over all retrieved ObjectPaths. 764 for (const std::pair<std::string, 765 std::vector<std::pair< 766 std::string, std::vector<std::string>>>>& 767 object : subtree) 768 { 769 const std::string& path = object.first; 770 const std::vector< 771 std::pair<std::string, std::vector<std::string>>>& 772 connectionNames = object.second; 773 774 sdbusplus::message::object_path objPath(path); 775 if (objPath.filename() != chassisId) 776 { 777 continue; 778 } 779 780 if (connectionNames.empty()) 781 { 782 BMCWEB_LOG_ERROR("Got 0 Connection names"); 783 continue; 784 } 785 786 const std::vector<std::string>& interfaces3 = 787 connectionNames[0].second; 788 789 const std::array<const char*, 3> hasIndicatorLed = { 790 "xyz.openbmc_project.Inventory.Item.Chassis", 791 "xyz.openbmc_project.Inventory.Item.Panel", 792 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; 793 bool indicatorChassis = false; 794 for (const char* interface : hasIndicatorLed) 795 { 796 if (std::ranges::find(interfaces3, interface) != 797 interfaces3.end()) 798 { 799 indicatorChassis = true; 800 break; 801 } 802 } 803 if (locationIndicatorActive) 804 { 805 if (indicatorChassis) 806 { 807 setLocationIndicatorActive(asyncResp, path, 808 *locationIndicatorActive); 809 } 810 else 811 { 812 messages::propertyUnknown(asyncResp->res, 813 "LocationIndicatorActive"); 814 } 815 } 816 if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) 817 { 818 if (indicatorLed) 819 { 820 if (indicatorChassis) 821 { 822 setIndicatorLedState(asyncResp, *indicatorLed); 823 } 824 else 825 { 826 messages::propertyUnknown(asyncResp->res, 827 "IndicatorLED"); 828 } 829 } 830 } 831 return; 832 } 833 834 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 835 }); 836 } 837 838 /** 839 * Chassis override class for delivering Chassis Schema 840 * Functions triggers appropriate requests on DBus 841 */ 842 inline void requestRoutesChassis(App& app) 843 { 844 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/") 845 .privileges(redfish::privileges::getChassis) 846 .methods(boost::beast::http::verb::get)( 847 std::bind_front(handleChassisGet, std::ref(app))); 848 849 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/") 850 .privileges(redfish::privileges::patchChassis) 851 .methods(boost::beast::http::verb::patch)( 852 std::bind_front(handleChassisPatch, std::ref(app))); 853 } 854 855 inline void doChassisPowerCycle( 856 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 857 { 858 constexpr std::array<std::string_view, 1> interfaces = { 859 "xyz.openbmc_project.State.Chassis"}; 860 861 // Use mapper to get subtree paths. 862 dbus::utility::getSubTreePaths( 863 "/", 0, interfaces, 864 [asyncResp]( 865 const boost::system::error_code& ec, 866 const dbus::utility::MapperGetSubTreePathsResponse& chassisList) { 867 if (ec) 868 { 869 BMCWEB_LOG_ERROR("[mapper] Bad D-Bus request error: {}", ec); 870 messages::internalError(asyncResp->res); 871 return; 872 } 873 874 const char* processName = "xyz.openbmc_project.State.Chassis"; 875 const char* interfaceName = "xyz.openbmc_project.State.Chassis"; 876 const char* destProperty = "RequestedPowerTransition"; 877 const std::string propertyValue = 878 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle"; 879 std::string objectPath = 880 "/xyz/openbmc_project/state/chassis_system0"; 881 882 /* Look for system reset chassis path */ 883 if ((std::ranges::find(chassisList, objectPath)) == 884 chassisList.end()) 885 { 886 /* We prefer to reset the full chassis_system, but if it doesn't 887 * exist on some platforms, fall back to a host-only power reset 888 */ 889 objectPath = "/xyz/openbmc_project/state/chassis0"; 890 } 891 892 setDbusProperty(asyncResp, "ResetType", processName, objectPath, 893 interfaceName, destProperty, propertyValue); 894 }); 895 } 896 897 inline void handleChassisResetActionInfoPost( 898 App& app, const crow::Request& req, 899 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 900 const std::string& /*chassisId*/) 901 { 902 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 903 { 904 return; 905 } 906 BMCWEB_LOG_DEBUG("Post Chassis Reset."); 907 908 std::string resetType; 909 910 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType)) 911 { 912 return; 913 } 914 915 if (resetType != "PowerCycle") 916 { 917 BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", resetType); 918 messages::actionParameterNotSupported(asyncResp->res, resetType, 919 "ResetType"); 920 921 return; 922 } 923 doChassisPowerCycle(asyncResp); 924 } 925 926 /** 927 * ChassisResetAction class supports the POST method for the Reset 928 * action. 929 * Function handles POST method request. 930 * Analyzes POST body before sending Reset request data to D-Bus. 931 */ 932 933 inline void requestRoutesChassisResetAction(App& app) 934 { 935 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Actions/Chassis.Reset/") 936 .privileges(redfish::privileges::postChassis) 937 .methods(boost::beast::http::verb::post)( 938 std::bind_front(handleChassisResetActionInfoPost, std::ref(app))); 939 } 940 941 inline void handleChassisResetActionInfoGet( 942 App& app, const crow::Request& req, 943 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 944 const std::string& chassisId) 945 { 946 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 947 { 948 return; 949 } 950 asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo"; 951 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 952 "/redfish/v1/Chassis/{}/ResetActionInfo", chassisId); 953 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 954 955 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 956 nlohmann::json::array_t parameters; 957 nlohmann::json::object_t parameter; 958 parameter["Name"] = "ResetType"; 959 parameter["Required"] = true; 960 parameter["DataType"] = action_info::ParameterTypes::String; 961 nlohmann::json::array_t allowed; 962 allowed.emplace_back("PowerCycle"); 963 parameter["AllowableValues"] = std::move(allowed); 964 parameters.emplace_back(std::move(parameter)); 965 966 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 967 } 968 969 /** 970 * ChassisResetActionInfo derived class for delivering Chassis 971 * ResetType AllowableValues using ResetInfo schema. 972 */ 973 inline void requestRoutesChassisResetActionInfo(App& app) 974 { 975 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ResetActionInfo/") 976 .privileges(redfish::privileges::getActionInfo) 977 .methods(boost::beast::http::verb::get)( 978 std::bind_front(handleChassisResetActionInfoGet, std::ref(app))); 979 } 980 981 } // namespace redfish 982