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