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 450 asyncResp->res.jsonValue["Assembly"]["@odata.id"] = 451 boost::urls::format("/redfish/v1/Chassis/{}/Assembly", chassisId); 452 453 // SensorCollection 454 asyncResp->res.jsonValue["Sensors"]["@odata.id"] = 455 boost::urls::format("/redfish/v1/Chassis/{}/Sensors", chassisId); 456 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 457 458 nlohmann::json::array_t computerSystems; 459 nlohmann::json::object_t system; 460 system["@odata.id"] = 461 std::format("/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); 462 computerSystems.emplace_back(std::move(system)); 463 asyncResp->res.jsonValue["Links"]["ComputerSystems"] = 464 std::move(computerSystems); 465 466 nlohmann::json::array_t managedBy; 467 nlohmann::json::object_t manager; 468 manager["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}", 469 BMCWEB_REDFISH_MANAGER_URI_NAME); 470 managedBy.emplace_back(std::move(manager)); 471 asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); 472 getChassisState(asyncResp); 473 getStorageLink(asyncResp, path); 474 } 475 476 inline void handleChassisProperties( 477 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 478 const dbus::utility::DBusPropertiesMap& propertiesList) 479 { 480 const std::string* type = nullptr; 481 482 const bool success = sdbusplus::unpackPropertiesNoThrow( 483 dbus_utils::UnpackErrorPrinter(), propertiesList, "Type", type); 484 485 if (!success) 486 { 487 messages::internalError(asyncResp->res); 488 return; 489 } 490 491 // Chassis Type is a required property in Redfish 492 // If there is an error or some enum we don't support just sit it to Rack 493 // Mount 494 asyncResp->res.jsonValue["ChassisType"] = chassis::ChassisType::RackMount; 495 496 if (type != nullptr) 497 { 498 auto chassisType = translateChassisTypeToRedfish(*type); 499 if (chassisType != chassis::ChassisType::Invalid) 500 { 501 asyncResp->res.jsonValue["ChassisType"] = chassisType; 502 } 503 } 504 } 505 506 inline void handleChassisGetSubTree( 507 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 508 const std::string& chassisId, const boost::system::error_code& ec, 509 const dbus::utility::MapperGetSubTreeResponse& subtree) 510 { 511 if (ec) 512 { 513 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 514 messages::internalError(asyncResp->res); 515 return; 516 } 517 // Iterate over all retrieved ObjectPaths. 518 for (const std::pair< 519 std::string, 520 std::vector<std::pair<std::string, std::vector<std::string>>>>& 521 object : subtree) 522 { 523 const std::string& path = object.first; 524 const std::vector<std::pair<std::string, std::vector<std::string>>>& 525 connectionNames = object.second; 526 527 sdbusplus::message::object_path objPath(path); 528 if (objPath.filename() != chassisId) 529 { 530 continue; 531 } 532 533 getChassisConnectivity(asyncResp, chassisId, path); 534 535 if (connectionNames.empty()) 536 { 537 BMCWEB_LOG_ERROR("Got 0 Connection names"); 538 continue; 539 } 540 541 asyncResp->res.jsonValue["@odata.type"] = "#Chassis.v1_22_0.Chassis"; 542 asyncResp->res.jsonValue["@odata.id"] = 543 boost::urls::format("/redfish/v1/Chassis/{}", chassisId); 544 asyncResp->res.jsonValue["Name"] = "Chassis Collection"; 545 asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]["target"] = 546 boost::urls::format("/redfish/v1/Chassis/{}/Actions/Chassis.Reset", 547 chassisId); 548 asyncResp->res 549 .jsonValue["Actions"]["#Chassis.Reset"]["@Redfish.ActionInfo"] = 550 boost::urls::format("/redfish/v1/Chassis/{}/ResetActionInfo", 551 chassisId); 552 dbus::utility::getAssociationEndPoints( 553 path + "/drive", 554 [asyncResp, chassisId](const boost::system::error_code& ec3, 555 const dbus::utility::MapperEndPoints& resp) { 556 if (ec3 || resp.empty()) 557 { 558 return; // no drives = no failures 559 } 560 561 nlohmann::json reference; 562 reference["@odata.id"] = boost::urls::format( 563 "/redfish/v1/Chassis/{}/Drives", chassisId); 564 asyncResp->res.jsonValue["Drives"] = std::move(reference); 565 }); 566 567 const std::string& connectionName = connectionNames[0].first; 568 569 const std::vector<std::string>& interfaces2 = connectionNames[0].second; 570 const std::array<const char*, 3> hasIndicatorLed = { 571 "xyz.openbmc_project.Inventory.Item.Chassis", 572 "xyz.openbmc_project.Inventory.Item.Panel", 573 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; 574 575 const std::string assetTagInterface = 576 "xyz.openbmc_project.Inventory.Decorator.AssetTag"; 577 const std::string replaceableInterface = 578 "xyz.openbmc_project.Inventory.Decorator.Replaceable"; 579 const std::string revisionInterface = 580 "xyz.openbmc_project.Inventory.Decorator.Revision"; 581 for (const auto& interface : interfaces2) 582 { 583 if (interface == assetTagInterface) 584 { 585 dbus::utility::getProperty<std::string>( 586 connectionName, path, assetTagInterface, "AssetTag", 587 [asyncResp, chassisId](const boost::system::error_code& ec2, 588 const std::string& property) { 589 if (ec2) 590 { 591 BMCWEB_LOG_ERROR( 592 "DBus response error for AssetTag: {}", ec2); 593 messages::internalError(asyncResp->res); 594 return; 595 } 596 asyncResp->res.jsonValue["AssetTag"] = property; 597 }); 598 } 599 else if (interface == replaceableInterface) 600 { 601 dbus::utility::getProperty<bool>( 602 connectionName, path, replaceableInterface, "HotPluggable", 603 [asyncResp, chassisId](const boost::system::error_code& ec2, 604 const bool property) { 605 if (ec2) 606 { 607 BMCWEB_LOG_ERROR( 608 "DBus response error for HotPluggable: {}", 609 ec2); 610 messages::internalError(asyncResp->res); 611 return; 612 } 613 asyncResp->res.jsonValue["HotPluggable"] = property; 614 }); 615 } 616 else if (interface == revisionInterface) 617 { 618 dbus::utility::getProperty<std::string>( 619 connectionName, path, revisionInterface, "Version", 620 [asyncResp, chassisId](const boost::system::error_code& ec2, 621 const std::string& property) { 622 if (ec2) 623 { 624 BMCWEB_LOG_ERROR( 625 "DBus response error for Version: {}", ec2); 626 messages::internalError(asyncResp->res); 627 return; 628 } 629 asyncResp->res.jsonValue["Version"] = property; 630 }); 631 } 632 } 633 634 for (const char* interface : hasIndicatorLed) 635 { 636 if (std::ranges::find(interfaces2, interface) != interfaces2.end()) 637 { 638 if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) 639 { 640 getIndicatorLedState(asyncResp); 641 } 642 getLocationIndicatorActive(asyncResp, objPath); 643 break; 644 } 645 } 646 647 dbus::utility::getAllProperties( 648 *crow::connections::systemBus, connectionName, path, 649 "xyz.openbmc_project.Inventory.Decorator.Asset", 650 [asyncResp, chassisId, 651 path](const boost::system::error_code&, 652 const dbus::utility::DBusPropertiesMap& propertiesList) { 653 handleDecoratorAssetProperties(asyncResp, chassisId, path, 654 propertiesList); 655 }); 656 657 dbus::utility::getAllProperties( 658 *crow::connections::systemBus, connectionName, path, 659 "xyz.openbmc_project.Inventory.Item.Chassis", 660 [asyncResp]( 661 const boost::system::error_code&, 662 const dbus::utility::DBusPropertiesMap& propertiesList) { 663 handleChassisProperties(asyncResp, propertiesList); 664 }); 665 666 for (const auto& interface : interfaces2) 667 { 668 if (interface == "xyz.openbmc_project.Common.UUID") 669 { 670 getChassisUUID(asyncResp, connectionName, path); 671 } 672 else if (interface == 673 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 674 { 675 getChassisLocationCode(asyncResp, connectionName, path); 676 } 677 } 678 679 return; 680 } 681 682 // Couldn't find an object with that name. return an error 683 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 684 } 685 686 inline void handleChassisGet( 687 App& app, const crow::Request& req, 688 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 689 const std::string& chassisId) 690 { 691 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 692 { 693 return; 694 } 695 696 dbus::utility::getSubTree( 697 "/xyz/openbmc_project/inventory", 0, chassisInterfaces, 698 std::bind_front(handleChassisGetSubTree, asyncResp, chassisId)); 699 700 constexpr std::array<std::string_view, 1> interfaces2 = { 701 "xyz.openbmc_project.Chassis.Intrusion"}; 702 703 dbus::utility::getSubTree( 704 "/xyz/openbmc_project", 0, interfaces2, 705 std::bind_front(handlePhysicalSecurityGetSubTree, asyncResp)); 706 } 707 708 inline void handleChassisPatch( 709 App& app, const crow::Request& req, 710 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 711 const std::string& param) 712 { 713 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 714 { 715 return; 716 } 717 std::optional<bool> locationIndicatorActive; 718 std::optional<std::string> indicatorLed; 719 720 if (param.empty()) 721 { 722 return; 723 } 724 725 if (!json_util::readJsonPatch( // 726 req, asyncResp->res, // 727 "IndicatorLED", indicatorLed, // 728 "LocationIndicatorActive", locationIndicatorActive // 729 )) 730 { 731 return; 732 } 733 734 if (!locationIndicatorActive && !indicatorLed) 735 { 736 return; // delete this when we support more patch properties 737 } 738 if (indicatorLed) 739 { 740 if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) 741 { 742 asyncResp->res.addHeader( 743 boost::beast::http::field::warning, 744 "299 - \"IndicatorLED is deprecated. Use LocationIndicatorActive instead.\""); 745 } 746 else 747 { 748 messages::propertyUnknown(asyncResp->res, "IndicatorLED"); 749 return; 750 } 751 } 752 753 const std::string& chassisId = param; 754 755 dbus::utility::getSubTree( 756 "/xyz/openbmc_project/inventory", 0, chassisInterfaces, 757 [asyncResp, chassisId, locationIndicatorActive, 758 indicatorLed](const boost::system::error_code& ec, 759 const dbus::utility::MapperGetSubTreeResponse& subtree) { 760 if (ec) 761 { 762 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 763 messages::internalError(asyncResp->res); 764 return; 765 } 766 767 // Iterate over all retrieved ObjectPaths. 768 for (const std::pair<std::string, 769 std::vector<std::pair< 770 std::string, std::vector<std::string>>>>& 771 object : subtree) 772 { 773 const std::string& path = object.first; 774 const std::vector< 775 std::pair<std::string, std::vector<std::string>>>& 776 connectionNames = object.second; 777 778 sdbusplus::message::object_path objPath(path); 779 if (objPath.filename() != chassisId) 780 { 781 continue; 782 } 783 784 if (connectionNames.empty()) 785 { 786 BMCWEB_LOG_ERROR("Got 0 Connection names"); 787 continue; 788 } 789 790 const std::vector<std::string>& interfaces3 = 791 connectionNames[0].second; 792 793 const std::array<const char*, 3> hasIndicatorLed = { 794 "xyz.openbmc_project.Inventory.Item.Chassis", 795 "xyz.openbmc_project.Inventory.Item.Panel", 796 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; 797 bool indicatorChassis = false; 798 for (const char* interface : hasIndicatorLed) 799 { 800 if (std::ranges::find(interfaces3, interface) != 801 interfaces3.end()) 802 { 803 indicatorChassis = true; 804 break; 805 } 806 } 807 if (locationIndicatorActive) 808 { 809 if (indicatorChassis) 810 { 811 setLocationIndicatorActive(asyncResp, path, 812 *locationIndicatorActive); 813 } 814 else 815 { 816 messages::propertyUnknown(asyncResp->res, 817 "LocationIndicatorActive"); 818 } 819 } 820 if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_INDICATORLED) 821 { 822 if (indicatorLed) 823 { 824 if (indicatorChassis) 825 { 826 setIndicatorLedState(asyncResp, *indicatorLed); 827 } 828 else 829 { 830 messages::propertyUnknown(asyncResp->res, 831 "IndicatorLED"); 832 } 833 } 834 } 835 return; 836 } 837 838 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 839 }); 840 } 841 842 /** 843 * Chassis override class for delivering Chassis Schema 844 * Functions triggers appropriate requests on DBus 845 */ 846 inline void requestRoutesChassis(App& app) 847 { 848 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/") 849 .privileges(redfish::privileges::getChassis) 850 .methods(boost::beast::http::verb::get)( 851 std::bind_front(handleChassisGet, std::ref(app))); 852 853 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/") 854 .privileges(redfish::privileges::patchChassis) 855 .methods(boost::beast::http::verb::patch)( 856 std::bind_front(handleChassisPatch, std::ref(app))); 857 } 858 859 inline void doChassisPowerCycle( 860 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 861 { 862 constexpr std::array<std::string_view, 1> interfaces = { 863 "xyz.openbmc_project.State.Chassis"}; 864 865 // Use mapper to get subtree paths. 866 dbus::utility::getSubTreePaths( 867 "/", 0, interfaces, 868 [asyncResp]( 869 const boost::system::error_code& ec, 870 const dbus::utility::MapperGetSubTreePathsResponse& chassisList) { 871 if (ec) 872 { 873 BMCWEB_LOG_ERROR("[mapper] Bad D-Bus request error: {}", ec); 874 messages::internalError(asyncResp->res); 875 return; 876 } 877 878 const char* processName = "xyz.openbmc_project.State.Chassis"; 879 const char* interfaceName = "xyz.openbmc_project.State.Chassis"; 880 const char* destProperty = "RequestedPowerTransition"; 881 const std::string propertyValue = 882 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle"; 883 std::string objectPath = 884 "/xyz/openbmc_project/state/chassis_system0"; 885 886 /* Look for system reset chassis path */ 887 if ((std::ranges::find(chassisList, objectPath)) == 888 chassisList.end()) 889 { 890 /* We prefer to reset the full chassis_system, but if it doesn't 891 * exist on some platforms, fall back to a host-only power reset 892 */ 893 objectPath = "/xyz/openbmc_project/state/chassis0"; 894 } 895 896 setDbusProperty(asyncResp, "ResetType", processName, objectPath, 897 interfaceName, destProperty, propertyValue); 898 }); 899 } 900 901 inline void handleChassisResetActionInfoPost( 902 App& app, const crow::Request& req, 903 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 904 const std::string& /*chassisId*/) 905 { 906 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 907 { 908 return; 909 } 910 BMCWEB_LOG_DEBUG("Post Chassis Reset."); 911 912 std::string resetType; 913 914 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType)) 915 { 916 return; 917 } 918 919 if (resetType != "PowerCycle") 920 { 921 BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", resetType); 922 messages::actionParameterNotSupported(asyncResp->res, resetType, 923 "ResetType"); 924 925 return; 926 } 927 doChassisPowerCycle(asyncResp); 928 } 929 930 /** 931 * ChassisResetAction class supports the POST method for the Reset 932 * action. 933 * Function handles POST method request. 934 * Analyzes POST body before sending Reset request data to D-Bus. 935 */ 936 937 inline void requestRoutesChassisResetAction(App& app) 938 { 939 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Actions/Chassis.Reset/") 940 .privileges(redfish::privileges::postChassis) 941 .methods(boost::beast::http::verb::post)( 942 std::bind_front(handleChassisResetActionInfoPost, std::ref(app))); 943 } 944 945 inline void handleChassisResetActionInfoGet( 946 App& app, const crow::Request& req, 947 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 948 const std::string& chassisId) 949 { 950 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 951 { 952 return; 953 } 954 asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo"; 955 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 956 "/redfish/v1/Chassis/{}/ResetActionInfo", chassisId); 957 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 958 959 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 960 nlohmann::json::array_t parameters; 961 nlohmann::json::object_t parameter; 962 parameter["Name"] = "ResetType"; 963 parameter["Required"] = true; 964 parameter["DataType"] = action_info::ParameterTypes::String; 965 nlohmann::json::array_t allowed; 966 allowed.emplace_back("PowerCycle"); 967 parameter["AllowableValues"] = std::move(allowed); 968 parameters.emplace_back(std::move(parameter)); 969 970 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 971 } 972 973 /** 974 * ChassisResetActionInfo derived class for delivering Chassis 975 * ResetType AllowableValues using ResetInfo schema. 976 */ 977 inline void requestRoutesChassisResetActionInfo(App& app) 978 { 979 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ResetActionInfo/") 980 .privileges(redfish::privileges::getActionInfo) 981 .methods(boost::beast::http::verb::get)( 982 std::bind_front(handleChassisResetActionInfoGet, std::ref(app))); 983 } 984 985 } // namespace redfish 986