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