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