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