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