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