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/chassis_utils.hpp" 22 #include "utils/collection.hpp" 23 #include "utils/dbus_utils.hpp" 24 #include "utils/json_utils.hpp" 25 26 #include <asm-generic/errno.h> 27 28 #include <boost/beast/http/field.hpp> 29 #include <boost/beast/http/verb.hpp> 30 #include <boost/system/error_code.hpp> 31 #include <boost/url/format.hpp> 32 #include <boost/url/url.hpp> 33 #include <nlohmann/json.hpp> 34 #include <sdbusplus/message/native_types.hpp> 35 #include <sdbusplus/unpack_properties.hpp> 36 37 #include <algorithm> 38 #include <array> 39 #include <format> 40 #include <functional> 41 #include <memory> 42 #include <optional> 43 #include <ranges> 44 #include <string> 45 #include <string_view> 46 #include <utility> 47 #include <vector> 48 49 namespace redfish 50 { 51 52 inline chassis::ChassisType translateChassisTypeToRedfish( 53 const std::string_view& chassisType) 54 { 55 if (chassisType == 56 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Blade") 57 { 58 return chassis::ChassisType::Blade; 59 } 60 if (chassisType == 61 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Component") 62 { 63 return chassis::ChassisType::Component; 64 } 65 if (chassisType == 66 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Enclosure") 67 { 68 return chassis::ChassisType::Enclosure; 69 } 70 if (chassisType == 71 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Module") 72 { 73 return chassis::ChassisType::Module; 74 } 75 if (chassisType == 76 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.RackMount") 77 { 78 return chassis::ChassisType::RackMount; 79 } 80 if (chassisType == 81 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.StandAlone") 82 { 83 return chassis::ChassisType::StandAlone; 84 } 85 if (chassisType == 86 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.StorageEnclosure") 87 { 88 return chassis::ChassisType::StorageEnclosure; 89 } 90 if (chassisType == 91 "xyz.openbmc_project.Inventory.Item.Chassis.ChassisType.Zone") 92 { 93 return chassis::ChassisType::Zone; 94 } 95 return chassis::ChassisType::Invalid; 96 } 97 98 /** 99 * @brief Retrieves resources over dbus to link to the chassis 100 * 101 * @param[in] asyncResp - Shared pointer for completing asynchronous 102 * calls 103 * @param[in] path - Chassis dbus path to look for the storage. 104 * 105 * Calls the Association endpoints on the path + "/storage" and add the link of 106 * json["Links"]["Storage@odata.count"] = 107 * {"@odata.id", "/redfish/v1/Storage/" + resourceId} 108 * 109 * @return None. 110 */ 111 inline void getStorageLink(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 112 const sdbusplus::message::object_path& path) 113 { 114 dbus::utility::getProperty<std::vector<std::string>>( 115 "xyz.openbmc_project.ObjectMapper", (path / "storage").str, 116 "xyz.openbmc_project.Association", "endpoints", 117 [asyncResp](const boost::system::error_code& ec, 118 const std::vector<std::string>& storageList) { 119 if (ec) 120 { 121 BMCWEB_LOG_DEBUG("getStorageLink got DBUS response error"); 122 return; 123 } 124 125 nlohmann::json::array_t storages; 126 for (const std::string& storagePath : storageList) 127 { 128 std::string id = 129 sdbusplus::message::object_path(storagePath).filename(); 130 if (id.empty()) 131 { 132 continue; 133 } 134 135 nlohmann::json::object_t storage; 136 storage["@odata.id"] = 137 boost::urls::format("/redfish/v1/Systems/{}/Storage/{}", 138 BMCWEB_REDFISH_SYSTEM_URI_NAME, id); 139 storages.emplace_back(std::move(storage)); 140 } 141 asyncResp->res.jsonValue["Links"]["Storage@odata.count"] = 142 storages.size(); 143 asyncResp->res.jsonValue["Links"]["Storage"] = std::move(storages); 144 }); 145 } 146 147 /** 148 * @brief Retrieves chassis state properties over dbus 149 * 150 * @param[in] asyncResp - Shared pointer for completing asynchronous calls. 151 * 152 * @return None. 153 */ 154 inline void getChassisState(std::shared_ptr<bmcweb::AsyncResp> asyncResp) 155 { 156 // crow::connections::systemBus->async_method_call( 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 const std::string* partNumber = nullptr; 423 const std::string* serialNumber = nullptr; 424 const std::string* manufacturer = nullptr; 425 const std::string* model = nullptr; 426 const std::string* sparePartNumber = nullptr; 427 428 const bool success = sdbusplus::unpackPropertiesNoThrow( 429 dbus_utils::UnpackErrorPrinter(), propertiesList, "PartNumber", 430 partNumber, "SerialNumber", serialNumber, "Manufacturer", manufacturer, 431 "Model", model, "SparePartNumber", sparePartNumber); 432 433 if (!success) 434 { 435 messages::internalError(asyncResp->res); 436 return; 437 } 438 439 if (partNumber != nullptr) 440 { 441 asyncResp->res.jsonValue["PartNumber"] = *partNumber; 442 } 443 444 if (serialNumber != nullptr) 445 { 446 asyncResp->res.jsonValue["SerialNumber"] = *serialNumber; 447 } 448 449 if (manufacturer != nullptr) 450 { 451 asyncResp->res.jsonValue["Manufacturer"] = *manufacturer; 452 } 453 454 if (model != nullptr) 455 { 456 asyncResp->res.jsonValue["Model"] = *model; 457 } 458 459 // SparePartNumber is optional on D-Bus 460 // so skip if it is empty 461 if (sparePartNumber != nullptr && !sparePartNumber->empty()) 462 { 463 asyncResp->res.jsonValue["SparePartNumber"] = *sparePartNumber; 464 } 465 466 asyncResp->res.jsonValue["Name"] = chassisId; 467 asyncResp->res.jsonValue["Id"] = chassisId; 468 469 if constexpr (BMCWEB_REDFISH_ALLOW_DEPRECATED_POWER_THERMAL) 470 { 471 asyncResp->res.jsonValue["Thermal"]["@odata.id"] = 472 boost::urls::format("/redfish/v1/Chassis/{}/Thermal", chassisId); 473 // Power object 474 asyncResp->res.jsonValue["Power"]["@odata.id"] = 475 boost::urls::format("/redfish/v1/Chassis/{}/Power", chassisId); 476 } 477 478 if constexpr (BMCWEB_REDFISH_NEW_POWERSUBSYSTEM_THERMALSUBSYSTEM) 479 { 480 asyncResp->res.jsonValue["ThermalSubsystem"]["@odata.id"] = 481 boost::urls::format("/redfish/v1/Chassis/{}/ThermalSubsystem", 482 chassisId); 483 asyncResp->res.jsonValue["PowerSubsystem"]["@odata.id"] = 484 boost::urls::format("/redfish/v1/Chassis/{}/PowerSubsystem", 485 chassisId); 486 asyncResp->res.jsonValue["EnvironmentMetrics"]["@odata.id"] = 487 boost::urls::format("/redfish/v1/Chassis/{}/EnvironmentMetrics", 488 chassisId); 489 } 490 // SensorCollection 491 asyncResp->res.jsonValue["Sensors"]["@odata.id"] = 492 boost::urls::format("/redfish/v1/Chassis/{}/Sensors", chassisId); 493 asyncResp->res.jsonValue["Status"]["State"] = resource::State::Enabled; 494 495 nlohmann::json::array_t computerSystems; 496 nlohmann::json::object_t system; 497 system["@odata.id"] = 498 std::format("/redfish/v1/Systems/{}", BMCWEB_REDFISH_SYSTEM_URI_NAME); 499 computerSystems.emplace_back(std::move(system)); 500 asyncResp->res.jsonValue["Links"]["ComputerSystems"] = 501 std::move(computerSystems); 502 503 nlohmann::json::array_t managedBy; 504 nlohmann::json::object_t manager; 505 manager["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}", 506 BMCWEB_REDFISH_MANAGER_URI_NAME); 507 managedBy.emplace_back(std::move(manager)); 508 asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); 509 getChassisState(asyncResp); 510 getStorageLink(asyncResp, path); 511 } 512 513 inline void handleChassisProperties( 514 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 515 const dbus::utility::DBusPropertiesMap& propertiesList) 516 { 517 const std::string* type = nullptr; 518 519 const bool success = sdbusplus::unpackPropertiesNoThrow( 520 dbus_utils::UnpackErrorPrinter(), propertiesList, "Type", type); 521 522 if (!success) 523 { 524 messages::internalError(asyncResp->res); 525 return; 526 } 527 528 // Chassis Type is a required property in Redfish 529 // If there is an error or some enum we don't support just sit it to Rack 530 // Mount 531 asyncResp->res.jsonValue["ChassisType"] = chassis::ChassisType::RackMount; 532 533 if (type != nullptr) 534 { 535 auto chassisType = translateChassisTypeToRedfish(*type); 536 if (chassisType != chassis::ChassisType::Invalid) 537 { 538 asyncResp->res.jsonValue["ChassisType"] = chassisType; 539 } 540 } 541 } 542 543 inline void handleChassisGetSubTree( 544 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 545 const std::string& chassisId, const boost::system::error_code& ec, 546 const dbus::utility::MapperGetSubTreeResponse& subtree) 547 { 548 if (ec) 549 { 550 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 551 messages::internalError(asyncResp->res); 552 return; 553 } 554 // Iterate over all retrieved ObjectPaths. 555 for (const std::pair< 556 std::string, 557 std::vector<std::pair<std::string, std::vector<std::string>>>>& 558 object : subtree) 559 { 560 const std::string& path = object.first; 561 const std::vector<std::pair<std::string, std::vector<std::string>>>& 562 connectionNames = object.second; 563 564 sdbusplus::message::object_path objPath(path); 565 if (objPath.filename() != chassisId) 566 { 567 continue; 568 } 569 570 getChassisConnectivity(asyncResp, chassisId, path); 571 572 if (connectionNames.empty()) 573 { 574 BMCWEB_LOG_ERROR("Got 0 Connection names"); 575 continue; 576 } 577 578 asyncResp->res.jsonValue["@odata.type"] = "#Chassis.v1_22_0.Chassis"; 579 asyncResp->res.jsonValue["@odata.id"] = 580 boost::urls::format("/redfish/v1/Chassis/{}", chassisId); 581 asyncResp->res.jsonValue["Name"] = "Chassis Collection"; 582 asyncResp->res.jsonValue["Actions"]["#Chassis.Reset"]["target"] = 583 boost::urls::format("/redfish/v1/Chassis/{}/Actions/Chassis.Reset", 584 chassisId); 585 asyncResp->res 586 .jsonValue["Actions"]["#Chassis.Reset"]["@Redfish.ActionInfo"] = 587 boost::urls::format("/redfish/v1/Chassis/{}/ResetActionInfo", 588 chassisId); 589 dbus::utility::getAssociationEndPoints( 590 path + "/drive", 591 [asyncResp, chassisId](const boost::system::error_code& ec3, 592 const dbus::utility::MapperEndPoints& resp) { 593 if (ec3 || resp.empty()) 594 { 595 return; // no drives = no failures 596 } 597 598 nlohmann::json reference; 599 reference["@odata.id"] = boost::urls::format( 600 "/redfish/v1/Chassis/{}/Drives", chassisId); 601 asyncResp->res.jsonValue["Drives"] = std::move(reference); 602 }); 603 604 const std::string& connectionName = connectionNames[0].first; 605 606 const std::vector<std::string>& interfaces2 = connectionNames[0].second; 607 const std::array<const char*, 3> hasIndicatorLed = { 608 "xyz.openbmc_project.Inventory.Item.Chassis", 609 "xyz.openbmc_project.Inventory.Item.Panel", 610 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; 611 612 const std::string assetTagInterface = 613 "xyz.openbmc_project.Inventory.Decorator.AssetTag"; 614 const std::string replaceableInterface = 615 "xyz.openbmc_project.Inventory.Decorator.Replaceable"; 616 const std::string revisionInterface = 617 "xyz.openbmc_project.Inventory.Decorator.Revision"; 618 for (const auto& interface : interfaces2) 619 { 620 if (interface == assetTagInterface) 621 { 622 dbus::utility::getProperty<std::string>( 623 connectionName, path, assetTagInterface, "AssetTag", 624 [asyncResp, chassisId](const boost::system::error_code& ec2, 625 const std::string& property) { 626 if (ec2) 627 { 628 BMCWEB_LOG_ERROR( 629 "DBus response error for AssetTag: {}", ec2); 630 messages::internalError(asyncResp->res); 631 return; 632 } 633 asyncResp->res.jsonValue["AssetTag"] = property; 634 }); 635 } 636 else if (interface == replaceableInterface) 637 { 638 dbus::utility::getProperty<bool>( 639 connectionName, path, replaceableInterface, "HotPluggable", 640 [asyncResp, chassisId](const boost::system::error_code& ec2, 641 const bool property) { 642 if (ec2) 643 { 644 BMCWEB_LOG_ERROR( 645 "DBus response error for HotPluggable: {}", 646 ec2); 647 messages::internalError(asyncResp->res); 648 return; 649 } 650 asyncResp->res.jsonValue["HotPluggable"] = property; 651 }); 652 } 653 else if (interface == revisionInterface) 654 { 655 dbus::utility::getProperty<std::string>( 656 connectionName, path, revisionInterface, "Version", 657 [asyncResp, chassisId](const boost::system::error_code& ec2, 658 const std::string& property) { 659 if (ec2) 660 { 661 BMCWEB_LOG_ERROR( 662 "DBus response error for Version: {}", ec2); 663 messages::internalError(asyncResp->res); 664 return; 665 } 666 asyncResp->res.jsonValue["Version"] = property; 667 }); 668 } 669 } 670 671 for (const char* interface : hasIndicatorLed) 672 { 673 if (std::ranges::find(interfaces2, interface) != interfaces2.end()) 674 { 675 getIndicatorLedState(asyncResp); 676 getSystemLocationIndicatorActive(asyncResp); 677 break; 678 } 679 } 680 681 dbus::utility::getAllProperties( 682 *crow::connections::systemBus, connectionName, path, 683 "xyz.openbmc_project.Inventory.Decorator.Asset", 684 [asyncResp, chassisId, 685 path](const boost::system::error_code&, 686 const dbus::utility::DBusPropertiesMap& propertiesList) { 687 handleDecoratorAssetProperties(asyncResp, chassisId, path, 688 propertiesList); 689 }); 690 691 dbus::utility::getAllProperties( 692 *crow::connections::systemBus, connectionName, path, 693 "xyz.openbmc_project.Inventory.Item.Chassis", 694 [asyncResp]( 695 const boost::system::error_code&, 696 const dbus::utility::DBusPropertiesMap& propertiesList) { 697 handleChassisProperties(asyncResp, propertiesList); 698 }); 699 700 for (const auto& interface : interfaces2) 701 { 702 if (interface == "xyz.openbmc_project.Common.UUID") 703 { 704 getChassisUUID(asyncResp, connectionName, path); 705 } 706 else if (interface == 707 "xyz.openbmc_project.Inventory.Decorator.LocationCode") 708 { 709 getChassisLocationCode(asyncResp, connectionName, path); 710 } 711 } 712 713 return; 714 } 715 716 // Couldn't find an object with that name. return an error 717 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 718 } 719 720 inline void handleChassisGet( 721 App& app, const crow::Request& req, 722 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 723 const std::string& chassisId) 724 { 725 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 726 { 727 return; 728 } 729 730 dbus::utility::getSubTree( 731 "/xyz/openbmc_project/inventory", 0, chassisInterfaces, 732 std::bind_front(handleChassisGetSubTree, asyncResp, chassisId)); 733 734 constexpr std::array<std::string_view, 1> interfaces2 = { 735 "xyz.openbmc_project.Chassis.Intrusion"}; 736 737 dbus::utility::getSubTree( 738 "/xyz/openbmc_project", 0, interfaces2, 739 std::bind_front(handlePhysicalSecurityGetSubTree, asyncResp)); 740 } 741 742 inline void handleChassisPatch( 743 App& app, const crow::Request& req, 744 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 745 const std::string& param) 746 { 747 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 748 { 749 return; 750 } 751 std::optional<bool> locationIndicatorActive; 752 std::optional<std::string> indicatorLed; 753 754 if (param.empty()) 755 { 756 return; 757 } 758 759 if (!json_util::readJsonPatch( // 760 req, asyncResp->res, // 761 "IndicatorLED", indicatorLed, // 762 "LocationIndicatorActive", locationIndicatorActive // 763 )) 764 { 765 return; 766 } 767 768 // TODO (Gunnar): Remove IndicatorLED after enough time has passed 769 if (!locationIndicatorActive && !indicatorLed) 770 { 771 return; // delete this when we support more patch properties 772 } 773 if (indicatorLed) 774 { 775 asyncResp->res.addHeader( 776 boost::beast::http::field::warning, 777 "299 - \"IndicatorLED is deprecated. Use LocationIndicatorActive instead.\""); 778 } 779 780 const std::string& chassisId = param; 781 782 dbus::utility::getSubTree( 783 "/xyz/openbmc_project/inventory", 0, chassisInterfaces, 784 [asyncResp, chassisId, locationIndicatorActive, 785 indicatorLed](const boost::system::error_code& ec, 786 const dbus::utility::MapperGetSubTreeResponse& subtree) { 787 if (ec) 788 { 789 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 790 messages::internalError(asyncResp->res); 791 return; 792 } 793 794 // Iterate over all retrieved ObjectPaths. 795 for (const std::pair<std::string, 796 std::vector<std::pair< 797 std::string, std::vector<std::string>>>>& 798 object : subtree) 799 { 800 const std::string& path = object.first; 801 const std::vector< 802 std::pair<std::string, std::vector<std::string>>>& 803 connectionNames = object.second; 804 805 sdbusplus::message::object_path objPath(path); 806 if (objPath.filename() != chassisId) 807 { 808 continue; 809 } 810 811 if (connectionNames.empty()) 812 { 813 BMCWEB_LOG_ERROR("Got 0 Connection names"); 814 continue; 815 } 816 817 const std::vector<std::string>& interfaces3 = 818 connectionNames[0].second; 819 820 const std::array<const char*, 3> hasIndicatorLed = { 821 "xyz.openbmc_project.Inventory.Item.Chassis", 822 "xyz.openbmc_project.Inventory.Item.Panel", 823 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; 824 bool indicatorChassis = false; 825 for (const char* interface : hasIndicatorLed) 826 { 827 if (std::ranges::find(interfaces3, interface) != 828 interfaces3.end()) 829 { 830 indicatorChassis = true; 831 break; 832 } 833 } 834 if (locationIndicatorActive) 835 { 836 if (indicatorChassis) 837 { 838 setSystemLocationIndicatorActive( 839 asyncResp, *locationIndicatorActive); 840 } 841 else 842 { 843 messages::propertyUnknown(asyncResp->res, 844 "LocationIndicatorActive"); 845 } 846 } 847 if (indicatorLed) 848 { 849 if (indicatorChassis) 850 { 851 setIndicatorLedState(asyncResp, *indicatorLed); 852 } 853 else 854 { 855 messages::propertyUnknown(asyncResp->res, 856 "IndicatorLED"); 857 } 858 } 859 return; 860 } 861 862 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 863 }); 864 } 865 866 /** 867 * Chassis override class for delivering Chassis Schema 868 * Functions triggers appropriate requests on DBus 869 */ 870 inline void requestRoutesChassis(App& app) 871 { 872 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/") 873 .privileges(redfish::privileges::getChassis) 874 .methods(boost::beast::http::verb::get)( 875 std::bind_front(handleChassisGet, std::ref(app))); 876 877 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/") 878 .privileges(redfish::privileges::patchChassis) 879 .methods(boost::beast::http::verb::patch)( 880 std::bind_front(handleChassisPatch, std::ref(app))); 881 } 882 883 inline void doChassisPowerCycle( 884 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 885 { 886 constexpr std::array<std::string_view, 1> interfaces = { 887 "xyz.openbmc_project.State.Chassis"}; 888 889 // Use mapper to get subtree paths. 890 dbus::utility::getSubTreePaths( 891 "/", 0, interfaces, 892 [asyncResp]( 893 const boost::system::error_code& ec, 894 const dbus::utility::MapperGetSubTreePathsResponse& chassisList) { 895 if (ec) 896 { 897 BMCWEB_LOG_ERROR("[mapper] Bad D-Bus request error: {}", ec); 898 messages::internalError(asyncResp->res); 899 return; 900 } 901 902 const char* processName = "xyz.openbmc_project.State.Chassis"; 903 const char* interfaceName = "xyz.openbmc_project.State.Chassis"; 904 const char* destProperty = "RequestedPowerTransition"; 905 const std::string propertyValue = 906 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle"; 907 std::string objectPath = 908 "/xyz/openbmc_project/state/chassis_system0"; 909 910 /* Look for system reset chassis path */ 911 if ((std::ranges::find(chassisList, objectPath)) == 912 chassisList.end()) 913 { 914 /* We prefer to reset the full chassis_system, but if it doesn't 915 * exist on some platforms, fall back to a host-only power reset 916 */ 917 objectPath = "/xyz/openbmc_project/state/chassis0"; 918 } 919 920 setDbusProperty(asyncResp, "ResetType", processName, objectPath, 921 interfaceName, destProperty, propertyValue); 922 }); 923 } 924 925 inline void handleChassisResetActionInfoPost( 926 App& app, const crow::Request& req, 927 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 928 const std::string& /*chassisId*/) 929 { 930 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 931 { 932 return; 933 } 934 BMCWEB_LOG_DEBUG("Post Chassis Reset."); 935 936 std::string resetType; 937 938 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType)) 939 { 940 return; 941 } 942 943 if (resetType != "PowerCycle") 944 { 945 BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", resetType); 946 messages::actionParameterNotSupported(asyncResp->res, resetType, 947 "ResetType"); 948 949 return; 950 } 951 doChassisPowerCycle(asyncResp); 952 } 953 954 /** 955 * ChassisResetAction class supports the POST method for the Reset 956 * action. 957 * Function handles POST method request. 958 * Analyzes POST body before sending Reset request data to D-Bus. 959 */ 960 961 inline void requestRoutesChassisResetAction(App& app) 962 { 963 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Actions/Chassis.Reset/") 964 .privileges(redfish::privileges::postChassis) 965 .methods(boost::beast::http::verb::post)( 966 std::bind_front(handleChassisResetActionInfoPost, std::ref(app))); 967 } 968 969 inline void handleChassisResetActionInfoGet( 970 App& app, const crow::Request& req, 971 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 972 const std::string& chassisId) 973 { 974 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 975 { 976 return; 977 } 978 asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo"; 979 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 980 "/redfish/v1/Chassis/{}/ResetActionInfo", chassisId); 981 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 982 983 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 984 nlohmann::json::array_t parameters; 985 nlohmann::json::object_t parameter; 986 parameter["Name"] = "ResetType"; 987 parameter["Required"] = true; 988 parameter["DataType"] = action_info::ParameterTypes::String; 989 nlohmann::json::array_t allowed; 990 allowed.emplace_back("PowerCycle"); 991 parameter["AllowableValues"] = std::move(allowed); 992 parameters.emplace_back(std::move(parameter)); 993 994 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 995 } 996 997 /** 998 * ChassisResetActionInfo derived class for delivering Chassis 999 * ResetType AllowableValues using ResetInfo schema. 1000 */ 1001 inline void requestRoutesChassisResetActionInfo(App& app) 1002 { 1003 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ResetActionInfo/") 1004 .privileges(redfish::privileges::getActionInfo) 1005 .methods(boost::beast::http::verb::get)( 1006 std::bind_front(handleChassisResetActionInfoGet, std::ref(app))); 1007 } 1008 1009 } // namespace redfish 1010