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