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