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