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