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