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