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, "LocationIndicatorActive", 683 locationIndicatorActive, "IndicatorLED", indicatorLed)) 684 { 685 return; 686 } 687 688 // TODO (Gunnar): Remove IndicatorLED after enough time has passed 689 if (!locationIndicatorActive && !indicatorLed) 690 { 691 return; // delete this when we support more patch properties 692 } 693 if (indicatorLed) 694 { 695 asyncResp->res.addHeader( 696 boost::beast::http::field::warning, 697 "299 - \"IndicatorLED is deprecated. Use LocationIndicatorActive instead.\""); 698 } 699 700 constexpr std::array<std::string_view, 2> interfaces = { 701 "xyz.openbmc_project.Inventory.Item.Board", 702 "xyz.openbmc_project.Inventory.Item.Chassis"}; 703 704 const std::string& chassisId = param; 705 706 dbus::utility::getSubTree( 707 "/xyz/openbmc_project/inventory", 0, interfaces, 708 [asyncResp, chassisId, locationIndicatorActive, 709 indicatorLed](const boost::system::error_code& ec, 710 const dbus::utility::MapperGetSubTreeResponse& subtree) { 711 if (ec) 712 { 713 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 714 messages::internalError(asyncResp->res); 715 return; 716 } 717 718 // Iterate over all retrieved ObjectPaths. 719 for (const std::pair<std::string, 720 std::vector<std::pair< 721 std::string, std::vector<std::string>>>>& 722 object : subtree) 723 { 724 const std::string& path = object.first; 725 const std::vector< 726 std::pair<std::string, std::vector<std::string>>>& 727 connectionNames = object.second; 728 729 sdbusplus::message::object_path objPath(path); 730 if (objPath.filename() != chassisId) 731 { 732 continue; 733 } 734 735 if (connectionNames.empty()) 736 { 737 BMCWEB_LOG_ERROR("Got 0 Connection names"); 738 continue; 739 } 740 741 const std::vector<std::string>& interfaces3 = 742 connectionNames[0].second; 743 744 const std::array<const char*, 3> hasIndicatorLed = { 745 "xyz.openbmc_project.Inventory.Item.Chassis", 746 "xyz.openbmc_project.Inventory.Item.Panel", 747 "xyz.openbmc_project.Inventory.Item.Board.Motherboard"}; 748 bool indicatorChassis = false; 749 for (const char* interface : hasIndicatorLed) 750 { 751 if (std::ranges::find(interfaces3, interface) != 752 interfaces3.end()) 753 { 754 indicatorChassis = true; 755 break; 756 } 757 } 758 if (locationIndicatorActive) 759 { 760 if (indicatorChassis) 761 { 762 setSystemLocationIndicatorActive( 763 asyncResp, *locationIndicatorActive); 764 } 765 else 766 { 767 messages::propertyUnknown(asyncResp->res, 768 "LocationIndicatorActive"); 769 } 770 } 771 if (indicatorLed) 772 { 773 if (indicatorChassis) 774 { 775 setIndicatorLedState(asyncResp, *indicatorLed); 776 } 777 else 778 { 779 messages::propertyUnknown(asyncResp->res, 780 "IndicatorLED"); 781 } 782 } 783 return; 784 } 785 786 messages::resourceNotFound(asyncResp->res, "Chassis", chassisId); 787 }); 788 } 789 790 /** 791 * Chassis override class for delivering Chassis Schema 792 * Functions triggers appropriate requests on DBus 793 */ 794 inline void requestRoutesChassis(App& app) 795 { 796 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/") 797 .privileges(redfish::privileges::getChassis) 798 .methods(boost::beast::http::verb::get)( 799 std::bind_front(handleChassisGet, std::ref(app))); 800 801 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/") 802 .privileges(redfish::privileges::patchChassis) 803 .methods(boost::beast::http::verb::patch)( 804 std::bind_front(handleChassisPatch, std::ref(app))); 805 } 806 807 inline void 808 doChassisPowerCycle(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 809 { 810 constexpr std::array<std::string_view, 1> interfaces = { 811 "xyz.openbmc_project.State.Chassis"}; 812 813 // Use mapper to get subtree paths. 814 dbus::utility::getSubTreePaths( 815 "/", 0, interfaces, 816 [asyncResp]( 817 const boost::system::error_code& ec, 818 const dbus::utility::MapperGetSubTreePathsResponse& chassisList) { 819 if (ec) 820 { 821 BMCWEB_LOG_ERROR("[mapper] Bad D-Bus request error: {}", ec); 822 messages::internalError(asyncResp->res); 823 return; 824 } 825 826 const char* processName = "xyz.openbmc_project.State.Chassis"; 827 const char* interfaceName = "xyz.openbmc_project.State.Chassis"; 828 const char* destProperty = "RequestedPowerTransition"; 829 const std::string propertyValue = 830 "xyz.openbmc_project.State.Chassis.Transition.PowerCycle"; 831 std::string objectPath = 832 "/xyz/openbmc_project/state/chassis_system0"; 833 834 /* Look for system reset chassis path */ 835 if ((std::ranges::find(chassisList, objectPath)) == 836 chassisList.end()) 837 { 838 /* We prefer to reset the full chassis_system, but if it doesn't 839 * exist on some platforms, fall back to a host-only power reset 840 */ 841 objectPath = "/xyz/openbmc_project/state/chassis0"; 842 } 843 844 setDbusProperty(asyncResp, "ResetType", processName, objectPath, 845 interfaceName, destProperty, propertyValue); 846 }); 847 } 848 849 inline void handleChassisResetActionInfoPost( 850 App& app, const crow::Request& req, 851 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 852 const std::string& /*chassisId*/) 853 { 854 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 855 { 856 return; 857 } 858 BMCWEB_LOG_DEBUG("Post Chassis Reset."); 859 860 std::string resetType; 861 862 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType)) 863 { 864 return; 865 } 866 867 if (resetType != "PowerCycle") 868 { 869 BMCWEB_LOG_DEBUG("Invalid property value for ResetType: {}", resetType); 870 messages::actionParameterNotSupported(asyncResp->res, resetType, 871 "ResetType"); 872 873 return; 874 } 875 doChassisPowerCycle(asyncResp); 876 } 877 878 /** 879 * ChassisResetAction class supports the POST method for the Reset 880 * action. 881 * Function handles POST method request. 882 * Analyzes POST body before sending Reset request data to D-Bus. 883 */ 884 885 inline void requestRoutesChassisResetAction(App& app) 886 { 887 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/Actions/Chassis.Reset/") 888 .privileges(redfish::privileges::postChassis) 889 .methods(boost::beast::http::verb::post)( 890 std::bind_front(handleChassisResetActionInfoPost, std::ref(app))); 891 } 892 893 inline void handleChassisResetActionInfoGet( 894 App& app, const crow::Request& req, 895 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 896 const std::string& chassisId) 897 { 898 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 899 { 900 return; 901 } 902 asyncResp->res.jsonValue["@odata.type"] = "#ActionInfo.v1_1_2.ActionInfo"; 903 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format( 904 "/redfish/v1/Chassis/{}/ResetActionInfo", chassisId); 905 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 906 907 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 908 nlohmann::json::array_t parameters; 909 nlohmann::json::object_t parameter; 910 parameter["Name"] = "ResetType"; 911 parameter["Required"] = true; 912 parameter["DataType"] = action_info::ParameterTypes::String; 913 nlohmann::json::array_t allowed; 914 allowed.emplace_back("PowerCycle"); 915 parameter["AllowableValues"] = std::move(allowed); 916 parameters.emplace_back(std::move(parameter)); 917 918 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 919 } 920 921 /** 922 * ChassisResetActionInfo derived class for delivering Chassis 923 * ResetType AllowableValues using ResetInfo schema. 924 */ 925 inline void requestRoutesChassisResetActionInfo(App& app) 926 { 927 BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/ResetActionInfo/") 928 .privileges(redfish::privileges::getActionInfo) 929 .methods(boost::beast::http::verb::get)( 930 std::bind_front(handleChassisResetActionInfoGet, std::ref(app))); 931 } 932 933 } // namespace redfish 934