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