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