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