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