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 #include "pcie.hpp" 21 #include "redfish_util.hpp" 22 23 #include <boost/container/flat_map.hpp> 24 #include <node.hpp> 25 #include <utils/fw_utils.hpp> 26 #include <utils/json_utils.hpp> 27 28 #include <variant> 29 30 namespace redfish 31 { 32 33 /** 34 * @brief Updates the Functional State of DIMMs 35 * 36 * @param[in] aResp Shared pointer for completing asynchronous calls 37 * @param[in] dimmState Dimm's Functional state, true/false 38 * 39 * @return None. 40 */ 41 inline void updateDimmProperties(const std::shared_ptr<AsyncResp>& aResp, 42 const std::variant<bool>& dimmState) 43 { 44 const bool* isDimmFunctional = std::get_if<bool>(&dimmState); 45 if (isDimmFunctional == nullptr) 46 { 47 messages::internalError(aResp->res); 48 return; 49 } 50 BMCWEB_LOG_DEBUG << "Dimm Functional: " << *isDimmFunctional; 51 52 // Set it as Enabled if at least one DIMM is functional 53 // Update STATE only if previous State was DISABLED and current Dimm is 54 // ENABLED. 55 nlohmann::json& prevMemSummary = 56 aResp->res.jsonValue["MemorySummary"]["Status"]["State"]; 57 if (prevMemSummary == "Disabled") 58 { 59 if (*isDimmFunctional == true) 60 { 61 aResp->res.jsonValue["MemorySummary"]["Status"]["State"] = 62 "Enabled"; 63 } 64 } 65 } 66 67 /* 68 * @brief Update "ProcessorSummary" "Count" based on Cpu PresenceState 69 * 70 * @param[in] aResp Shared pointer for completing asynchronous calls 71 * @param[in] cpuPresenceState CPU present or not 72 * 73 * @return None. 74 */ 75 inline void modifyCpuPresenceState(const std::shared_ptr<AsyncResp>& aResp, 76 const std::variant<bool>& cpuPresenceState) 77 { 78 const bool* isCpuPresent = std::get_if<bool>(&cpuPresenceState); 79 80 if (isCpuPresent == nullptr) 81 { 82 messages::internalError(aResp->res); 83 return; 84 } 85 BMCWEB_LOG_DEBUG << "Cpu Present: " << *isCpuPresent; 86 87 if (*isCpuPresent == true) 88 { 89 nlohmann::json& procCount = 90 aResp->res.jsonValue["ProcessorSummary"]["Count"]; 91 auto procCountPtr = 92 procCount.get_ptr<nlohmann::json::number_integer_t*>(); 93 if (procCountPtr != nullptr) 94 { 95 // shouldn't be possible to be nullptr 96 *procCountPtr += 1; 97 } 98 } 99 } 100 101 /* 102 * @brief Update "ProcessorSummary" "Status" "State" based on 103 * CPU Functional State 104 * 105 * @param[in] aResp Shared pointer for completing asynchronous calls 106 * @param[in] cpuFunctionalState is CPU functional true/false 107 * 108 * @return None. 109 */ 110 inline void 111 modifyCpuFunctionalState(const std::shared_ptr<AsyncResp>& aResp, 112 const std::variant<bool>& cpuFunctionalState) 113 { 114 const bool* isCpuFunctional = std::get_if<bool>(&cpuFunctionalState); 115 116 if (isCpuFunctional == nullptr) 117 { 118 messages::internalError(aResp->res); 119 return; 120 } 121 BMCWEB_LOG_DEBUG << "Cpu Functional: " << *isCpuFunctional; 122 123 nlohmann::json& prevProcState = 124 aResp->res.jsonValue["ProcessorSummary"]["Status"]["State"]; 125 126 // Set it as Enabled if at least one CPU is functional 127 // Update STATE only if previous State was Non_Functional and current CPU is 128 // Functional. 129 if (prevProcState == "Disabled") 130 { 131 if (*isCpuFunctional == true) 132 { 133 aResp->res.jsonValue["ProcessorSummary"]["Status"]["State"] = 134 "Enabled"; 135 } 136 } 137 } 138 139 /* 140 * @brief Retrieves computer system properties over dbus 141 * 142 * @param[in] aResp Shared pointer for completing asynchronous calls 143 * @param[in] name Computer system name from request 144 * 145 * @return None. 146 */ 147 inline void 148 getComputerSystem(const std::shared_ptr<AsyncResp>& aResp, 149 const std::shared_ptr<HealthPopulate>& systemHealth) 150 { 151 BMCWEB_LOG_DEBUG << "Get available system components."; 152 153 crow::connections::systemBus->async_method_call( 154 [aResp, systemHealth]( 155 const boost::system::error_code ec, 156 const std::vector<std::pair< 157 std::string, 158 std::vector<std::pair<std::string, std::vector<std::string>>>>>& 159 subtree) { 160 if (ec) 161 { 162 BMCWEB_LOG_DEBUG << "DBUS response error"; 163 messages::internalError(aResp->res); 164 return; 165 } 166 // Iterate over all retrieved ObjectPaths. 167 for (const std::pair<std::string, 168 std::vector<std::pair< 169 std::string, std::vector<std::string>>>>& 170 object : subtree) 171 { 172 const std::string& path = object.first; 173 BMCWEB_LOG_DEBUG << "Got path: " << path; 174 const std::vector< 175 std::pair<std::string, std::vector<std::string>>>& 176 connectionNames = object.second; 177 if (connectionNames.size() < 1) 178 { 179 continue; 180 } 181 182 auto memoryHealth = std::make_shared<HealthPopulate>( 183 aResp, aResp->res.jsonValue["MemorySummary"]["Status"]); 184 185 auto cpuHealth = std::make_shared<HealthPopulate>( 186 aResp, aResp->res.jsonValue["ProcessorSummary"]["Status"]); 187 188 systemHealth->children.emplace_back(memoryHealth); 189 systemHealth->children.emplace_back(cpuHealth); 190 191 // This is not system, so check if it's cpu, dimm, UUID or 192 // BiosVer 193 for (const auto& connection : connectionNames) 194 { 195 for (const auto& interfaceName : connection.second) 196 { 197 if (interfaceName == 198 "xyz.openbmc_project.Inventory.Item.Dimm") 199 { 200 BMCWEB_LOG_DEBUG 201 << "Found Dimm, now get its properties."; 202 203 crow::connections::systemBus->async_method_call( 204 [aResp, service{connection.first}, 205 path(std::move(path))]( 206 const boost::system::error_code ec2, 207 const std::vector< 208 std::pair<std::string, VariantType>>& 209 properties) { 210 if (ec2) 211 { 212 BMCWEB_LOG_ERROR 213 << "DBUS response error " << ec2; 214 messages::internalError(aResp->res); 215 return; 216 } 217 BMCWEB_LOG_DEBUG << "Got " 218 << properties.size() 219 << " Dimm properties."; 220 221 if (properties.size() > 0) 222 { 223 for (const std::pair<std::string, 224 VariantType>& 225 property : properties) 226 { 227 if (property.first != 228 "MemorySizeInKB") 229 { 230 continue; 231 } 232 const uint32_t* value = 233 std::get_if<uint32_t>( 234 &property.second); 235 if (value == nullptr) 236 { 237 BMCWEB_LOG_DEBUG 238 << "Find incorrect type of " 239 "MemorySize"; 240 continue; 241 } 242 nlohmann::json& totalMemory = 243 aResp->res 244 .jsonValue["MemorySummar" 245 "y"] 246 ["TotalSystemMe" 247 "moryGiB"]; 248 uint64_t* preValue = 249 totalMemory 250 .get_ptr<uint64_t*>(); 251 if (preValue == nullptr) 252 { 253 continue; 254 } 255 aResp->res 256 .jsonValue["MemorySummary"] 257 ["TotalSystemMemoryGi" 258 "B"] = 259 *value / (1024 * 1024) + 260 *preValue; 261 aResp->res 262 .jsonValue["MemorySummary"] 263 ["Status"]["State"] = 264 "Enabled"; 265 } 266 } 267 else 268 { 269 auto getDimmProperties = 270 [aResp]( 271 const boost::system::error_code 272 ec3, 273 const std::variant<bool>& 274 dimmState) { 275 if (ec3) 276 { 277 BMCWEB_LOG_ERROR 278 << "DBUS response " 279 "error " 280 << ec3; 281 return; 282 } 283 updateDimmProperties(aResp, 284 dimmState); 285 }; 286 crow::connections::systemBus 287 ->async_method_call( 288 std::move(getDimmProperties), 289 service, path, 290 "org.freedesktop.DBus." 291 "Properties", 292 "Get", 293 "xyz.openbmc_project.State." 294 "Decorator.OperationalStatus", 295 "Functional"); 296 } 297 }, 298 connection.first, path, 299 "org.freedesktop.DBus.Properties", "GetAll", 300 "xyz.openbmc_project.Inventory.Item.Dimm"); 301 302 memoryHealth->inventory.emplace_back(path); 303 } 304 else if (interfaceName == 305 "xyz.openbmc_project.Inventory.Item.Cpu") 306 { 307 BMCWEB_LOG_DEBUG 308 << "Found Cpu, now get its properties."; 309 310 crow::connections::systemBus->async_method_call( 311 [aResp, service{connection.first}, 312 path(std::move(path))]( 313 const boost::system::error_code ec2, 314 const std::vector< 315 std::pair<std::string, VariantType>>& 316 properties) { 317 if (ec2) 318 { 319 BMCWEB_LOG_ERROR 320 << "DBUS response error " << ec2; 321 messages::internalError(aResp->res); 322 return; 323 } 324 BMCWEB_LOG_DEBUG << "Got " 325 << properties.size() 326 << " Cpu properties."; 327 328 if (properties.size() > 0) 329 { 330 const uint64_t* processorId = nullptr; 331 const std::string* procFamily = nullptr; 332 nlohmann::json& procSummary = 333 aResp->res.jsonValue["ProcessorSumm" 334 "ary"]; 335 nlohmann::json& procCount = 336 procSummary["Count"]; 337 338 auto procCountPtr = procCount.get_ptr< 339 nlohmann::json:: 340 number_integer_t*>(); 341 if (procCountPtr == nullptr) 342 { 343 messages::internalError(aResp->res); 344 return; 345 } 346 for (const auto& property : properties) 347 { 348 349 if (property.first == "Id") 350 { 351 processorId = 352 std::get_if<uint64_t>( 353 &property.second); 354 if (nullptr != procFamily) 355 { 356 break; 357 } 358 continue; 359 } 360 361 if (property.first == "Family") 362 { 363 procFamily = 364 std::get_if<std::string>( 365 &property.second); 366 if (nullptr != processorId) 367 { 368 break; 369 } 370 continue; 371 } 372 } 373 374 if (procFamily != nullptr && 375 processorId != nullptr) 376 { 377 if (procCountPtr != nullptr && 378 *processorId != 0) 379 { 380 *procCountPtr += 1; 381 procSummary["Status"]["State"] = 382 "Enabled"; 383 384 procSummary["Model"] = 385 *procFamily; 386 } 387 } 388 } 389 else 390 { 391 auto getCpuPresenceState = 392 [aResp]( 393 const boost::system::error_code 394 ec3, 395 const std::variant<bool>& 396 cpuPresenceCheck) { 397 if (ec3) 398 { 399 BMCWEB_LOG_ERROR 400 << "DBUS response " 401 "error " 402 << ec3; 403 return; 404 } 405 modifyCpuPresenceState( 406 aResp, cpuPresenceCheck); 407 }; 408 409 auto getCpuFunctionalState = 410 [aResp]( 411 const boost::system::error_code 412 ec3, 413 const std::variant<bool>& 414 cpuFunctionalCheck) { 415 if (ec3) 416 { 417 BMCWEB_LOG_ERROR 418 << "DBUS response " 419 "error " 420 << ec3; 421 return; 422 } 423 modifyCpuFunctionalState( 424 aResp, cpuFunctionalCheck); 425 }; 426 // Get the Presence of CPU 427 crow::connections::systemBus 428 ->async_method_call( 429 std::move(getCpuPresenceState), 430 service, path, 431 "org.freedesktop.DBus." 432 "Properties", 433 "Get", 434 "xyz.openbmc_project.Inventory." 435 "Item", 436 "Present"); 437 438 // Get the Functional State 439 crow::connections::systemBus 440 ->async_method_call( 441 std::move( 442 getCpuFunctionalState), 443 service, path, 444 "org.freedesktop.DBus." 445 "Properties", 446 "Get", 447 "xyz.openbmc_project.State." 448 "Decorator." 449 "OperationalStatus", 450 "Functional"); 451 452 // Get the MODEL from 453 // xyz.openbmc_project.Inventory.Decorator.Asset 454 // support it later as Model is Empty 455 // currently. 456 } 457 }, 458 connection.first, path, 459 "org.freedesktop.DBus.Properties", "GetAll", 460 "xyz.openbmc_project.Inventory.Item.Cpu"); 461 462 cpuHealth->inventory.emplace_back(path); 463 } 464 else if (interfaceName == 465 "xyz.openbmc_project.Common.UUID") 466 { 467 BMCWEB_LOG_DEBUG 468 << "Found UUID, now get its properties."; 469 crow::connections::systemBus->async_method_call( 470 [aResp]( 471 const boost::system::error_code ec3, 472 const std::vector< 473 std::pair<std::string, VariantType>>& 474 properties) { 475 if (ec3) 476 { 477 BMCWEB_LOG_DEBUG 478 << "DBUS response error " << ec3; 479 messages::internalError(aResp->res); 480 return; 481 } 482 BMCWEB_LOG_DEBUG << "Got " 483 << properties.size() 484 << " UUID properties."; 485 for (const std::pair<std::string, 486 VariantType>& 487 property : properties) 488 { 489 if (property.first == "UUID") 490 { 491 const std::string* value = 492 std::get_if<std::string>( 493 &property.second); 494 495 if (value != nullptr) 496 { 497 std::string valueStr = *value; 498 if (valueStr.size() == 32) 499 { 500 valueStr.insert(8, 1, '-'); 501 valueStr.insert(13, 1, '-'); 502 valueStr.insert(18, 1, '-'); 503 valueStr.insert(23, 1, '-'); 504 } 505 BMCWEB_LOG_DEBUG << "UUID = " 506 << valueStr; 507 aResp->res.jsonValue["UUID"] = 508 valueStr; 509 } 510 } 511 } 512 }, 513 connection.first, path, 514 "org.freedesktop.DBus.Properties", "GetAll", 515 "xyz.openbmc_project.Common.UUID"); 516 } 517 else if (interfaceName == 518 "xyz.openbmc_project.Inventory.Item.System") 519 { 520 crow::connections::systemBus->async_method_call( 521 [aResp]( 522 const boost::system::error_code ec2, 523 const std::vector< 524 std::pair<std::string, VariantType>>& 525 propertiesList) { 526 if (ec2) 527 { 528 // doesn't have to include this 529 // interface 530 return; 531 } 532 BMCWEB_LOG_DEBUG 533 << "Got " << propertiesList.size() 534 << " properties for system"; 535 for (const std::pair<std::string, 536 VariantType>& 537 property : propertiesList) 538 { 539 const std::string& propertyName = 540 property.first; 541 if ((propertyName == "PartNumber") || 542 (propertyName == "SerialNumber") || 543 (propertyName == "Manufacturer") || 544 (propertyName == "Model")) 545 { 546 const std::string* value = 547 std::get_if<std::string>( 548 &property.second); 549 if (value != nullptr) 550 { 551 aResp->res 552 .jsonValue[propertyName] = 553 *value; 554 } 555 } 556 } 557 558 // Grab the bios version 559 fw_util::populateFirmwareInformation( 560 aResp, fw_util::biosPurpose, 561 "BiosVersion", false); 562 }, 563 connection.first, path, 564 "org.freedesktop.DBus.Properties", "GetAll", 565 "xyz.openbmc_project.Inventory.Decorator." 566 "Asset"); 567 568 crow::connections::systemBus->async_method_call( 569 [aResp]( 570 const boost::system::error_code ec2, 571 const std::variant<std::string>& property) { 572 if (ec2) 573 { 574 // doesn't have to include this 575 // interface 576 return; 577 } 578 579 const std::string* value = 580 std::get_if<std::string>(&property); 581 if (value != nullptr) 582 { 583 aResp->res.jsonValue["AssetTag"] = 584 *value; 585 } 586 }, 587 connection.first, path, 588 "org.freedesktop.DBus.Properties", "Get", 589 "xyz.openbmc_project.Inventory.Decorator." 590 "AssetTag", 591 "AssetTag"); 592 } 593 } 594 } 595 } 596 }, 597 "xyz.openbmc_project.ObjectMapper", 598 "/xyz/openbmc_project/object_mapper", 599 "xyz.openbmc_project.ObjectMapper", "GetSubTree", 600 "/xyz/openbmc_project/inventory", int32_t(0), 601 std::array<const char*, 5>{ 602 "xyz.openbmc_project.Inventory.Decorator.Asset", 603 "xyz.openbmc_project.Inventory.Item.Cpu", 604 "xyz.openbmc_project.Inventory.Item.Dimm", 605 "xyz.openbmc_project.Inventory.Item.System", 606 "xyz.openbmc_project.Common.UUID", 607 }); 608 } 609 610 /** 611 * @brief Retrieves host state properties over dbus 612 * 613 * @param[in] aResp Shared pointer for completing asynchronous calls. 614 * 615 * @return None. 616 */ 617 inline void getHostState(const std::shared_ptr<AsyncResp>& aResp) 618 { 619 BMCWEB_LOG_DEBUG << "Get host information."; 620 crow::connections::systemBus->async_method_call( 621 [aResp](const boost::system::error_code ec, 622 const std::variant<std::string>& hostState) { 623 if (ec) 624 { 625 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 626 messages::internalError(aResp->res); 627 return; 628 } 629 630 const std::string* s = std::get_if<std::string>(&hostState); 631 BMCWEB_LOG_DEBUG << "Host state: " << *s; 632 if (s != nullptr) 633 { 634 // Verify Host State 635 if (*s == "xyz.openbmc_project.State.Host.HostState.Running") 636 { 637 aResp->res.jsonValue["PowerState"] = "On"; 638 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 639 } 640 else if (*s == "xyz.openbmc_project.State.Host.HostState." 641 "Quiesced") 642 { 643 aResp->res.jsonValue["PowerState"] = "On"; 644 aResp->res.jsonValue["Status"]["State"] = "Quiesced"; 645 } 646 else if (*s == "xyz.openbmc_project.State.Host.HostState." 647 "DiagnosticMode") 648 { 649 aResp->res.jsonValue["PowerState"] = "On"; 650 aResp->res.jsonValue["Status"]["State"] = "InTest"; 651 } 652 else 653 { 654 aResp->res.jsonValue["PowerState"] = "Off"; 655 aResp->res.jsonValue["Status"]["State"] = "Disabled"; 656 } 657 } 658 }, 659 "xyz.openbmc_project.State.Host", "/xyz/openbmc_project/state/host0", 660 "org.freedesktop.DBus.Properties", "Get", 661 "xyz.openbmc_project.State.Host", "CurrentHostState"); 662 } 663 664 /** 665 * @brief Translates boot source DBUS property value to redfish. 666 * 667 * @param[in] dbusSource The boot source in DBUS speak. 668 * 669 * @return Returns as a string, the boot source in Redfish terms. If translation 670 * cannot be done, returns an empty string. 671 */ 672 inline std::string dbusToRfBootSource(const std::string& dbusSource) 673 { 674 if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Default") 675 { 676 return "None"; 677 } 678 if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Disk") 679 { 680 return "Hdd"; 681 } 682 if (dbusSource == 683 "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia") 684 { 685 return "Cd"; 686 } 687 if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Network") 688 { 689 return "Pxe"; 690 } 691 if (dbusSource == 692 "xyz.openbmc_project.Control.Boot.Source.Sources.RemovableMedia") 693 { 694 return "Usb"; 695 } 696 return ""; 697 } 698 699 /** 700 * @brief Translates boot mode DBUS property value to redfish. 701 * 702 * @param[in] dbusMode The boot mode in DBUS speak. 703 * 704 * @return Returns as a string, the boot mode in Redfish terms. If translation 705 * cannot be done, returns an empty string. 706 */ 707 inline std::string dbusToRfBootMode(const std::string& dbusMode) 708 { 709 if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") 710 { 711 return "None"; 712 } 713 if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe") 714 { 715 return "Diags"; 716 } 717 if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup") 718 { 719 return "BiosSetup"; 720 } 721 return ""; 722 } 723 724 /** 725 * @brief Translates boot source from Redfish to the DBus boot paths. 726 * 727 * @param[in] rfSource The boot source in Redfish. 728 * @param[out] bootSource The DBus source 729 * @param[out] bootMode the DBus boot mode 730 * 731 * @return Integer error code. 732 */ 733 inline int assignBootParameters(const std::shared_ptr<AsyncResp>& aResp, 734 const std::string& rfSource, 735 std::string& bootSource, std::string& bootMode) 736 { 737 // The caller has initialized the bootSource and bootMode to: 738 // bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"; 739 // bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Default"; 740 // Only modify the bootSource/bootMode variable needed to achieve the 741 // desired boot action. 742 743 if (rfSource == "None") 744 { 745 return 0; 746 } 747 if (rfSource == "Pxe") 748 { 749 bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Network"; 750 } 751 else if (rfSource == "Hdd") 752 { 753 bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Disk"; 754 } 755 else if (rfSource == "Diags") 756 { 757 bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe"; 758 } 759 else if (rfSource == "Cd") 760 { 761 bootSource = 762 "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia"; 763 } 764 else if (rfSource == "BiosSetup") 765 { 766 bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup"; 767 } 768 else if (rfSource == "Usb") 769 { 770 bootSource = 771 "xyz.openbmc_project.Control.Boot.Source.Sources.RemovableMedia"; 772 } 773 else 774 { 775 BMCWEB_LOG_DEBUG << "Invalid property value for " 776 "BootSourceOverrideTarget: " 777 << bootSource; 778 messages::propertyValueNotInList(aResp->res, rfSource, 779 "BootSourceTargetOverride"); 780 return -1; 781 } 782 return 0; 783 } 784 785 /** 786 * @brief Retrieves boot mode over DBUS and fills out the response 787 * 788 * @param[in] aResp Shared pointer for generating response message. 789 * @param[in] bootDbusObj The dbus object to query for boot properties. 790 * 791 * @return None. 792 */ 793 inline void getBootMode(const std::shared_ptr<AsyncResp>& aResp, 794 const std::string& bootDbusObj) 795 { 796 crow::connections::systemBus->async_method_call( 797 [aResp](const boost::system::error_code ec, 798 const std::variant<std::string>& bootMode) { 799 if (ec) 800 { 801 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 802 messages::internalError(aResp->res); 803 return; 804 } 805 806 const std::string* bootModeStr = 807 std::get_if<std::string>(&bootMode); 808 809 if (!bootModeStr) 810 { 811 messages::internalError(aResp->res); 812 return; 813 } 814 815 BMCWEB_LOG_DEBUG << "Boot mode: " << *bootModeStr; 816 817 // TODO (Santosh): Do we need to support override mode? 818 aResp->res.jsonValue["Boot"]["BootSourceOverrideMode"] = "Legacy"; 819 aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget@Redfish." 820 "AllowableValues"] = { 821 "None", "Pxe", "Hdd", "Cd", "Diags", "BiosSetup", "Usb"}; 822 823 if (*bootModeStr != 824 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") 825 { 826 auto rfMode = dbusToRfBootMode(*bootModeStr); 827 if (!rfMode.empty()) 828 { 829 aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = 830 rfMode; 831 } 832 } 833 834 // If the BootSourceOverrideTarget is still "None" at the end, 835 // reset the BootSourceOverrideEnabled to indicate that 836 // overrides are disabled 837 if (aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] == 838 "None") 839 { 840 aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = 841 "Disabled"; 842 } 843 }, 844 "xyz.openbmc_project.Settings", bootDbusObj, 845 "org.freedesktop.DBus.Properties", "Get", 846 "xyz.openbmc_project.Control.Boot.Mode", "BootMode"); 847 } 848 849 /** 850 * @brief Retrieves boot source over DBUS 851 * 852 * @param[in] aResp Shared pointer for generating response message. 853 * @param[in] oneTimeEnable Boolean to indicate boot properties are one-time. 854 * 855 * @return None. 856 */ 857 inline void getBootSource(std::shared_ptr<AsyncResp> aResp, bool oneTimeEnabled) 858 { 859 std::string bootDbusObj = 860 oneTimeEnabled ? "/xyz/openbmc_project/control/host0/boot/one_time" 861 : "/xyz/openbmc_project/control/host0/boot"; 862 863 BMCWEB_LOG_DEBUG << "Is one time: " << oneTimeEnabled; 864 aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = 865 (oneTimeEnabled) ? "Once" : "Continuous"; 866 867 crow::connections::systemBus->async_method_call( 868 [aResp, bootDbusObj](const boost::system::error_code ec, 869 const std::variant<std::string>& bootSource) { 870 if (ec) 871 { 872 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 873 messages::internalError(aResp->res); 874 return; 875 } 876 877 const std::string* bootSourceStr = 878 std::get_if<std::string>(&bootSource); 879 880 if (!bootSourceStr) 881 { 882 messages::internalError(aResp->res); 883 return; 884 } 885 BMCWEB_LOG_DEBUG << "Boot source: " << *bootSourceStr; 886 887 auto rfSource = dbusToRfBootSource(*bootSourceStr); 888 if (!rfSource.empty()) 889 { 890 aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = 891 rfSource; 892 } 893 }, 894 "xyz.openbmc_project.Settings", bootDbusObj, 895 "org.freedesktop.DBus.Properties", "Get", 896 "xyz.openbmc_project.Control.Boot.Source", "BootSource"); 897 getBootMode(std::move(aResp), std::move(bootDbusObj)); 898 } 899 900 /** 901 * @brief Retrieves "One time" enabled setting over DBUS and calls function to 902 * get boot source and boot mode. 903 * 904 * @param[in] aResp Shared pointer for generating response message. 905 * 906 * @return None. 907 */ 908 inline void getBootProperties(const std::shared_ptr<AsyncResp>& aResp) 909 { 910 BMCWEB_LOG_DEBUG << "Get boot information."; 911 912 crow::connections::systemBus->async_method_call( 913 [aResp](const boost::system::error_code ec, 914 const std::variant<bool>& oneTime) { 915 if (ec) 916 { 917 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 918 // not an error, don't have to have the interface 919 return; 920 } 921 922 const bool* oneTimePtr = std::get_if<bool>(&oneTime); 923 924 if (!oneTimePtr) 925 { 926 messages::internalError(aResp->res); 927 return; 928 } 929 getBootSource(aResp, *oneTimePtr); 930 }, 931 "xyz.openbmc_project.Settings", 932 "/xyz/openbmc_project/control/host0/boot/one_time", 933 "org.freedesktop.DBus.Properties", "Get", 934 "xyz.openbmc_project.Object.Enable", "Enabled"); 935 } 936 937 /** 938 * @brief Retrieves the Last Reset Time 939 * 940 * "Reset" is an overloaded term in Redfish, "Reset" includes power on 941 * and power off. Even though this is the "system" Redfish object look at the 942 * chassis D-Bus interface for the LastStateChangeTime since this has the 943 * last power operation time. 944 * 945 * @param[in] aResp Shared pointer for generating response message. 946 * 947 * @return None. 948 */ 949 inline void getLastResetTime(const std::shared_ptr<AsyncResp>& aResp) 950 { 951 BMCWEB_LOG_DEBUG << "Getting System Last Reset Time"; 952 953 crow::connections::systemBus->async_method_call( 954 [aResp](const boost::system::error_code ec, 955 std::variant<uint64_t>& lastResetTime) { 956 if (ec) 957 { 958 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 959 return; 960 } 961 962 const uint64_t* lastResetTimePtr = 963 std::get_if<uint64_t>(&lastResetTime); 964 965 if (!lastResetTimePtr) 966 { 967 messages::internalError(aResp->res); 968 return; 969 } 970 // LastStateChangeTime is epoch time, in milliseconds 971 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/33e8e1dd64da53a66e888d33dc82001305cd0bf9/xyz/openbmc_project/State/Chassis.interface.yaml#L19 972 time_t lastResetTimeStamp = 973 static_cast<time_t>(*lastResetTimePtr / 1000); 974 975 // Convert to ISO 8601 standard 976 aResp->res.jsonValue["LastResetTime"] = 977 crow::utility::getDateTime(lastResetTimeStamp); 978 }, 979 "xyz.openbmc_project.State.Chassis", 980 "/xyz/openbmc_project/state/chassis0", 981 "org.freedesktop.DBus.Properties", "Get", 982 "xyz.openbmc_project.State.Chassis", "LastStateChangeTime"); 983 } 984 985 /** 986 * @brief Retrieves Automatic Retry properties. Known on D-Bus as AutoReboot. 987 * 988 * @param[in] aResp Shared pointer for generating response message. 989 * 990 * @return None. 991 */ 992 inline void getAutomaticRetry(const std::shared_ptr<AsyncResp>& aResp) 993 { 994 BMCWEB_LOG_DEBUG << "Get Automatic Retry policy"; 995 996 crow::connections::systemBus->async_method_call( 997 [aResp](const boost::system::error_code ec, 998 std::variant<bool>& autoRebootEnabled) { 999 if (ec) 1000 { 1001 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 1002 return; 1003 } 1004 1005 const bool* autoRebootEnabledPtr = 1006 std::get_if<bool>(&autoRebootEnabled); 1007 1008 if (!autoRebootEnabledPtr) 1009 { 1010 messages::internalError(aResp->res); 1011 return; 1012 } 1013 1014 BMCWEB_LOG_DEBUG << "Auto Reboot: " << *autoRebootEnabledPtr; 1015 if (*autoRebootEnabledPtr == true) 1016 { 1017 aResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = 1018 "RetryAttempts"; 1019 // If AutomaticRetry (AutoReboot) is enabled see how many 1020 // attempts are left 1021 crow::connections::systemBus->async_method_call( 1022 [aResp](const boost::system::error_code ec2, 1023 std::variant<uint32_t>& autoRebootAttemptsLeft) { 1024 if (ec2) 1025 { 1026 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec2; 1027 return; 1028 } 1029 1030 const uint32_t* autoRebootAttemptsLeftPtr = 1031 std::get_if<uint32_t>(&autoRebootAttemptsLeft); 1032 1033 if (!autoRebootAttemptsLeftPtr) 1034 { 1035 messages::internalError(aResp->res); 1036 return; 1037 } 1038 1039 BMCWEB_LOG_DEBUG << "Auto Reboot Attempts Left: " 1040 << *autoRebootAttemptsLeftPtr; 1041 1042 aResp->res 1043 .jsonValue["Boot"] 1044 ["RemainingAutomaticRetryAttempts"] = 1045 *autoRebootAttemptsLeftPtr; 1046 }, 1047 "xyz.openbmc_project.State.Host", 1048 "/xyz/openbmc_project/state/host0", 1049 "org.freedesktop.DBus.Properties", "Get", 1050 "xyz.openbmc_project.Control.Boot.RebootAttempts", 1051 "AttemptsLeft"); 1052 } 1053 else 1054 { 1055 aResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = 1056 "Disabled"; 1057 } 1058 1059 // Not on D-Bus. Hardcoded here: 1060 // https://github.com/openbmc/phosphor-state-manager/blob/1dbbef42675e94fb1f78edb87d6b11380260535a/meson_options.txt#L71 1061 aResp->res.jsonValue["Boot"]["AutomaticRetryAttempts"] = 3; 1062 1063 // "AutomaticRetryConfig" can be 3 values, Disabled, RetryAlways, 1064 // and RetryAttempts. OpenBMC only supports Disabled and 1065 // RetryAttempts. 1066 aResp->res.jsonValue["Boot"]["AutomaticRetryConfig@Redfish." 1067 "AllowableValues"] = {"Disabled", 1068 "RetryAttempts"}; 1069 }, 1070 "xyz.openbmc_project.Settings", 1071 "/xyz/openbmc_project/control/host0/auto_reboot", 1072 "org.freedesktop.DBus.Properties", "Get", 1073 "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot"); 1074 } 1075 1076 /** 1077 * @brief Retrieves power restore policy over DBUS. 1078 * 1079 * @param[in] aResp Shared pointer for generating response message. 1080 * 1081 * @return None. 1082 */ 1083 inline void getPowerRestorePolicy(const std::shared_ptr<AsyncResp>& aResp) 1084 { 1085 BMCWEB_LOG_DEBUG << "Get power restore policy"; 1086 1087 crow::connections::systemBus->async_method_call( 1088 [aResp](const boost::system::error_code ec, 1089 std::variant<std::string>& policy) { 1090 if (ec) 1091 { 1092 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1093 return; 1094 } 1095 1096 const boost::container::flat_map<std::string, std::string> 1097 policyMaps = { 1098 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1099 "AlwaysOn", 1100 "AlwaysOn"}, 1101 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1102 "AlwaysOff", 1103 "AlwaysOff"}, 1104 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1105 "LastState", 1106 "LastState"}}; 1107 1108 const std::string* policyPtr = std::get_if<std::string>(&policy); 1109 1110 if (!policyPtr) 1111 { 1112 messages::internalError(aResp->res); 1113 return; 1114 } 1115 1116 auto policyMapsIt = policyMaps.find(*policyPtr); 1117 if (policyMapsIt == policyMaps.end()) 1118 { 1119 messages::internalError(aResp->res); 1120 return; 1121 } 1122 1123 aResp->res.jsonValue["PowerRestorePolicy"] = policyMapsIt->second; 1124 }, 1125 "xyz.openbmc_project.Settings", 1126 "/xyz/openbmc_project/control/host0/power_restore_policy", 1127 "org.freedesktop.DBus.Properties", "Get", 1128 "xyz.openbmc_project.Control.Power.RestorePolicy", 1129 "PowerRestorePolicy"); 1130 } 1131 1132 /** 1133 * @brief Sets boot properties into DBUS object(s). 1134 * 1135 * @param[in] aResp Shared pointer for generating response message. 1136 * @param[in] oneTimeEnabled Is "one-time" setting already enabled. 1137 * @param[in] bootSource The boot source to set. 1138 * @param[in] bootEnable The source override "enable" to set. 1139 * 1140 * @return Integer error code. 1141 */ 1142 inline void setBootModeOrSource(std::shared_ptr<AsyncResp> aResp, 1143 bool oneTimeEnabled, 1144 std::optional<std::string> bootSource, 1145 std::optional<std::string> bootEnable) 1146 { 1147 std::string bootSourceStr = 1148 "xyz.openbmc_project.Control.Boot.Source.Sources.Default"; 1149 std::string bootModeStr = 1150 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"; 1151 bool oneTimeSetting = oneTimeEnabled; 1152 bool useBootSource = true; 1153 1154 // Validate incoming parameters 1155 if (bootEnable) 1156 { 1157 if (*bootEnable == "Once") 1158 { 1159 oneTimeSetting = true; 1160 } 1161 else if (*bootEnable == "Continuous") 1162 { 1163 oneTimeSetting = false; 1164 } 1165 else if (*bootEnable == "Disabled") 1166 { 1167 BMCWEB_LOG_DEBUG << "Boot source override will be disabled"; 1168 oneTimeSetting = false; 1169 useBootSource = false; 1170 } 1171 else 1172 { 1173 BMCWEB_LOG_DEBUG << "Unsupported value for " 1174 "BootSourceOverrideEnabled: " 1175 << *bootEnable; 1176 messages::propertyValueNotInList(aResp->res, *bootEnable, 1177 "BootSourceOverrideEnabled"); 1178 return; 1179 } 1180 } 1181 1182 if (bootSource && useBootSource) 1183 { 1184 // Source target specified 1185 BMCWEB_LOG_DEBUG << "Boot source: " << *bootSource; 1186 // Figure out which DBUS interface and property to use 1187 if (assignBootParameters(aResp, *bootSource, bootSourceStr, 1188 bootModeStr)) 1189 { 1190 BMCWEB_LOG_DEBUG 1191 << "Invalid property value for BootSourceOverrideTarget: " 1192 << *bootSource; 1193 messages::propertyValueNotInList(aResp->res, *bootSource, 1194 "BootSourceTargetOverride"); 1195 return; 1196 } 1197 } 1198 1199 // Act on validated parameters 1200 BMCWEB_LOG_DEBUG << "DBUS boot source: " << bootSourceStr; 1201 BMCWEB_LOG_DEBUG << "DBUS boot mode: " << bootModeStr; 1202 const char* bootObj = 1203 oneTimeSetting ? "/xyz/openbmc_project/control/host0/boot/one_time" 1204 : "/xyz/openbmc_project/control/host0/boot"; 1205 1206 crow::connections::systemBus->async_method_call( 1207 [aResp](const boost::system::error_code ec) { 1208 if (ec) 1209 { 1210 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1211 messages::internalError(aResp->res); 1212 return; 1213 } 1214 BMCWEB_LOG_DEBUG << "Boot source update done."; 1215 }, 1216 "xyz.openbmc_project.Settings", bootObj, 1217 "org.freedesktop.DBus.Properties", "Set", 1218 "xyz.openbmc_project.Control.Boot.Source", "BootSource", 1219 std::variant<std::string>(bootSourceStr)); 1220 1221 crow::connections::systemBus->async_method_call( 1222 [aResp](const boost::system::error_code ec) { 1223 if (ec) 1224 { 1225 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1226 messages::internalError(aResp->res); 1227 return; 1228 } 1229 BMCWEB_LOG_DEBUG << "Boot mode update done."; 1230 }, 1231 "xyz.openbmc_project.Settings", bootObj, 1232 "org.freedesktop.DBus.Properties", "Set", 1233 "xyz.openbmc_project.Control.Boot.Mode", "BootMode", 1234 std::variant<std::string>(bootModeStr)); 1235 1236 crow::connections::systemBus->async_method_call( 1237 [aResp{std::move(aResp)}](const boost::system::error_code ec) { 1238 if (ec) 1239 { 1240 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1241 messages::internalError(aResp->res); 1242 return; 1243 } 1244 BMCWEB_LOG_DEBUG << "Boot enable update done."; 1245 }, 1246 "xyz.openbmc_project.Settings", 1247 "/xyz/openbmc_project/control/host0/boot/one_time", 1248 "org.freedesktop.DBus.Properties", "Set", 1249 "xyz.openbmc_project.Object.Enable", "Enabled", 1250 std::variant<bool>(oneTimeSetting)); 1251 } 1252 1253 /** 1254 * @brief Retrieves "One time" enabled setting over DBUS and calls function to 1255 * set boot source/boot mode properties. 1256 * 1257 * @param[in] aResp Shared pointer for generating response message. 1258 * @param[in] bootSource The boot source from incoming RF request. 1259 * @param[in] bootEnable The boot override enable from incoming RF request. 1260 * 1261 * @return Integer error code. 1262 */ 1263 inline void setBootSourceProperties(const std::shared_ptr<AsyncResp>& aResp, 1264 std::optional<std::string> bootSource, 1265 std::optional<std::string> bootEnable) 1266 { 1267 BMCWEB_LOG_DEBUG << "Set boot information."; 1268 1269 crow::connections::systemBus->async_method_call( 1270 [aResp, bootSource{std::move(bootSource)}, 1271 bootEnable{std::move(bootEnable)}](const boost::system::error_code ec, 1272 const std::variant<bool>& oneTime) { 1273 if (ec) 1274 { 1275 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1276 messages::internalError(aResp->res); 1277 return; 1278 } 1279 1280 const bool* oneTimePtr = std::get_if<bool>(&oneTime); 1281 1282 if (!oneTimePtr) 1283 { 1284 messages::internalError(aResp->res); 1285 return; 1286 } 1287 1288 BMCWEB_LOG_DEBUG << "Got one time: " << *oneTimePtr; 1289 1290 setBootModeOrSource(aResp, *oneTimePtr, std::move(bootSource), 1291 std::move(bootEnable)); 1292 }, 1293 "xyz.openbmc_project.Settings", 1294 "/xyz/openbmc_project/control/host0/boot/one_time", 1295 "org.freedesktop.DBus.Properties", "Get", 1296 "xyz.openbmc_project.Object.Enable", "Enabled"); 1297 } 1298 1299 /** 1300 * @brief Sets automaticRetry (Auto Reboot) 1301 * 1302 * @param[in] aResp Shared pointer for generating response message. 1303 * @param[in] automaticRetryConfig "AutomaticRetryConfig" from request. 1304 * 1305 * @return None. 1306 */ 1307 inline void setAutomaticRetry(const std::shared_ptr<AsyncResp>& aResp, 1308 const std::string&& automaticRetryConfig) 1309 { 1310 BMCWEB_LOG_DEBUG << "Set Automatic Retry."; 1311 1312 // OpenBMC only supports "Disabled" and "RetryAttempts". 1313 bool autoRebootEnabled; 1314 1315 if (automaticRetryConfig == "Disabled") 1316 { 1317 autoRebootEnabled = false; 1318 } 1319 else if (automaticRetryConfig == "RetryAttempts") 1320 { 1321 autoRebootEnabled = true; 1322 } 1323 else 1324 { 1325 BMCWEB_LOG_DEBUG << "Invalid property value for " 1326 "AutomaticRetryConfig: " 1327 << automaticRetryConfig; 1328 messages::propertyValueNotInList(aResp->res, automaticRetryConfig, 1329 "AutomaticRetryConfig"); 1330 return; 1331 } 1332 1333 crow::connections::systemBus->async_method_call( 1334 [aResp](const boost::system::error_code ec) { 1335 if (ec) 1336 { 1337 messages::internalError(aResp->res); 1338 return; 1339 } 1340 }, 1341 "xyz.openbmc_project.Settings", 1342 "/xyz/openbmc_project/control/host0/auto_reboot", 1343 "org.freedesktop.DBus.Properties", "Set", 1344 "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", 1345 std::variant<bool>(autoRebootEnabled)); 1346 } 1347 1348 /** 1349 * @brief Sets power restore policy properties. 1350 * 1351 * @param[in] aResp Shared pointer for generating response message. 1352 * @param[in] policy power restore policy properties from request. 1353 * 1354 * @return None. 1355 */ 1356 inline void setPowerRestorePolicy(const std::shared_ptr<AsyncResp>& aResp, 1357 std::optional<std::string> policy) 1358 { 1359 BMCWEB_LOG_DEBUG << "Set power restore policy."; 1360 1361 const boost::container::flat_map<std::string, std::string> policyMaps = { 1362 {"AlwaysOn", "xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1363 "AlwaysOn"}, 1364 {"AlwaysOff", "xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1365 "AlwaysOff"}, 1366 {"LastState", "xyz.openbmc_project.Control.Power.RestorePolicy.Policy." 1367 "LastState"}}; 1368 1369 std::string powerRestorPolicy; 1370 1371 auto policyMapsIt = policyMaps.find(*policy); 1372 if (policyMapsIt == policyMaps.end()) 1373 { 1374 messages::internalError(aResp->res); 1375 return; 1376 } 1377 1378 powerRestorPolicy = policyMapsIt->second; 1379 1380 crow::connections::systemBus->async_method_call( 1381 [aResp](const boost::system::error_code ec) { 1382 if (ec) 1383 { 1384 messages::internalError(aResp->res); 1385 return; 1386 } 1387 }, 1388 "xyz.openbmc_project.Settings", 1389 "/xyz/openbmc_project/control/host0/power_restore_policy", 1390 "org.freedesktop.DBus.Properties", "Set", 1391 "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", 1392 std::variant<std::string>(powerRestorPolicy)); 1393 } 1394 1395 #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE 1396 /** 1397 * @brief Retrieves provisioning status 1398 * 1399 * @param[in] aResp Shared pointer for completing asynchronous calls. 1400 * 1401 * @return None. 1402 */ 1403 inline void getProvisioningStatus(std::shared_ptr<AsyncResp> aResp) 1404 { 1405 BMCWEB_LOG_DEBUG << "Get OEM information."; 1406 crow::connections::systemBus->async_method_call( 1407 [aResp](const boost::system::error_code ec, 1408 const std::vector<std::pair<std::string, VariantType>>& 1409 propertiesList) { 1410 nlohmann::json& oemPFR = 1411 aResp->res.jsonValue["Oem"]["OpenBmc"]["FirmwareProvisioning"]; 1412 aResp->res.jsonValue["Oem"]["OpenBmc"]["@odata.type"] = 1413 "#OemComputerSystem.OpenBmc"; 1414 oemPFR["@odata.type"] = "#OemComputerSystem.FirmwareProvisioning"; 1415 1416 if (ec) 1417 { 1418 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1419 // not an error, don't have to have the interface 1420 oemPFR["ProvisioningStatus"] = "NotProvisioned"; 1421 return; 1422 } 1423 1424 const bool* provState = nullptr; 1425 const bool* lockState = nullptr; 1426 for (const std::pair<std::string, VariantType>& property : 1427 propertiesList) 1428 { 1429 if (property.first == "UfmProvisioned") 1430 { 1431 provState = std::get_if<bool>(&property.second); 1432 } 1433 else if (property.first == "UfmLocked") 1434 { 1435 lockState = std::get_if<bool>(&property.second); 1436 } 1437 } 1438 1439 if ((provState == nullptr) || (lockState == nullptr)) 1440 { 1441 BMCWEB_LOG_DEBUG << "Unable to get PFR attributes."; 1442 messages::internalError(aResp->res); 1443 return; 1444 } 1445 1446 if (*provState == true) 1447 { 1448 if (*lockState == true) 1449 { 1450 oemPFR["ProvisioningStatus"] = "ProvisionedAndLocked"; 1451 } 1452 else 1453 { 1454 oemPFR["ProvisioningStatus"] = "ProvisionedButNotLocked"; 1455 } 1456 } 1457 else 1458 { 1459 oemPFR["ProvisioningStatus"] = "NotProvisioned"; 1460 } 1461 }, 1462 "xyz.openbmc_project.PFR.Manager", "/xyz/openbmc_project/pfr", 1463 "org.freedesktop.DBus.Properties", "GetAll", 1464 "xyz.openbmc_project.PFR.Attributes"); 1465 } 1466 #endif 1467 1468 /** 1469 * @brief Translates watchdog timeout action DBUS property value to redfish. 1470 * 1471 * @param[in] dbusAction The watchdog timeout action in D-BUS. 1472 * 1473 * @return Returns as a string, the timeout action in Redfish terms. If 1474 * translation cannot be done, returns an empty string. 1475 */ 1476 inline std::string dbusToRfWatchdogAction(const std::string& dbusAction) 1477 { 1478 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.None") 1479 { 1480 return "None"; 1481 } 1482 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.HardReset") 1483 { 1484 return "ResetSystem"; 1485 } 1486 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.PowerOff") 1487 { 1488 return "PowerDown"; 1489 } 1490 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.PowerCycle") 1491 { 1492 return "PowerCycle"; 1493 } 1494 1495 return ""; 1496 } 1497 1498 /** 1499 *@brief Translates timeout action from Redfish to DBUS property value. 1500 * 1501 *@param[in] rfAction The timeout action in Redfish. 1502 * 1503 *@return Returns as a string, the time_out action as expected by DBUS. 1504 *If translation cannot be done, returns an empty string. 1505 */ 1506 1507 inline std::string rfToDbusWDTTimeOutAct(const std::string& rfAction) 1508 { 1509 if (rfAction == "None") 1510 { 1511 return "xyz.openbmc_project.State.Watchdog.Action.None"; 1512 } 1513 if (rfAction == "PowerCycle") 1514 { 1515 return "xyz.openbmc_project.State.Watchdog.Action.PowerCycle"; 1516 } 1517 if (rfAction == "PowerDown") 1518 { 1519 return "xyz.openbmc_project.State.Watchdog.Action.PowerOff"; 1520 } 1521 if (rfAction == "ResetSystem") 1522 { 1523 return "xyz.openbmc_project.State.Watchdog.Action.HardReset"; 1524 } 1525 1526 return ""; 1527 } 1528 1529 /** 1530 * @brief Retrieves host watchdog timer properties over DBUS 1531 * 1532 * @param[in] aResp Shared pointer for completing asynchronous calls. 1533 * 1534 * @return None. 1535 */ 1536 inline void getHostWatchdogTimer(const std::shared_ptr<AsyncResp>& aResp) 1537 { 1538 BMCWEB_LOG_DEBUG << "Get host watchodg"; 1539 crow::connections::systemBus->async_method_call( 1540 [aResp](const boost::system::error_code ec, 1541 PropertiesType& properties) { 1542 if (ec) 1543 { 1544 // watchdog service is stopped 1545 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1546 return; 1547 } 1548 1549 BMCWEB_LOG_DEBUG << "Got " << properties.size() << " wdt prop."; 1550 1551 nlohmann::json& hostWatchdogTimer = 1552 aResp->res.jsonValue["HostWatchdogTimer"]; 1553 1554 // watchdog service is running/enabled 1555 hostWatchdogTimer["Status"]["State"] = "Enabled"; 1556 1557 for (const auto& property : properties) 1558 { 1559 BMCWEB_LOG_DEBUG << "prop=" << property.first; 1560 if (property.first == "Enabled") 1561 { 1562 const bool* state = std::get_if<bool>(&property.second); 1563 1564 if (!state) 1565 { 1566 messages::internalError(aResp->res); 1567 continue; 1568 } 1569 1570 hostWatchdogTimer["FunctionEnabled"] = *state; 1571 } 1572 else if (property.first == "ExpireAction") 1573 { 1574 const std::string* s = 1575 std::get_if<std::string>(&property.second); 1576 if (!s) 1577 { 1578 messages::internalError(aResp->res); 1579 continue; 1580 } 1581 1582 std::string action = dbusToRfWatchdogAction(*s); 1583 if (action.empty()) 1584 { 1585 messages::internalError(aResp->res); 1586 continue; 1587 } 1588 hostWatchdogTimer["TimeoutAction"] = action; 1589 } 1590 } 1591 }, 1592 "xyz.openbmc_project.Watchdog", "/xyz/openbmc_project/watchdog/host0", 1593 "org.freedesktop.DBus.Properties", "GetAll", 1594 "xyz.openbmc_project.State.Watchdog"); 1595 } 1596 1597 /** 1598 * @brief Sets Host WatchDog Timer properties. 1599 * 1600 * @param[in] aResp Shared pointer for generating response message. 1601 * @param[in] wdtEnable The WDTimer Enable value (true/false) from incoming 1602 * RF request. 1603 * @param[in] wdtTimeOutAction The WDT Timeout action, from incoming RF request. 1604 * 1605 * @return None. 1606 */ 1607 inline void setWDTProperties(const std::shared_ptr<AsyncResp>& aResp, 1608 const std::optional<bool> wdtEnable, 1609 const std::optional<std::string>& wdtTimeOutAction) 1610 { 1611 BMCWEB_LOG_DEBUG << "Set host watchdog"; 1612 1613 if (wdtTimeOutAction) 1614 { 1615 std::string wdtTimeOutActStr = rfToDbusWDTTimeOutAct(*wdtTimeOutAction); 1616 // check if TimeOut Action is Valid 1617 if (wdtTimeOutActStr.empty()) 1618 { 1619 BMCWEB_LOG_DEBUG << "Unsupported value for TimeoutAction: " 1620 << *wdtTimeOutAction; 1621 messages::propertyValueNotInList(aResp->res, *wdtTimeOutAction, 1622 "TimeoutAction"); 1623 return; 1624 } 1625 1626 crow::connections::systemBus->async_method_call( 1627 [aResp](const boost::system::error_code ec) { 1628 if (ec) 1629 { 1630 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1631 messages::internalError(aResp->res); 1632 return; 1633 } 1634 }, 1635 "xyz.openbmc_project.Watchdog", 1636 "/xyz/openbmc_project/watchdog/host0", 1637 "org.freedesktop.DBus.Properties", "Set", 1638 "xyz.openbmc_project.State.Watchdog", "ExpireAction", 1639 std::variant<std::string>(wdtTimeOutActStr)); 1640 } 1641 1642 if (wdtEnable) 1643 { 1644 crow::connections::systemBus->async_method_call( 1645 [aResp](const boost::system::error_code ec) { 1646 if (ec) 1647 { 1648 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1649 messages::internalError(aResp->res); 1650 return; 1651 } 1652 }, 1653 "xyz.openbmc_project.Watchdog", 1654 "/xyz/openbmc_project/watchdog/host0", 1655 "org.freedesktop.DBus.Properties", "Set", 1656 "xyz.openbmc_project.State.Watchdog", "Enabled", 1657 std::variant<bool>(*wdtEnable)); 1658 } 1659 } 1660 1661 /** 1662 * SystemsCollection derived class for delivering ComputerSystems Collection 1663 * Schema 1664 */ 1665 class SystemsCollection : public Node 1666 { 1667 public: 1668 SystemsCollection(App& app) : Node(app, "/redfish/v1/Systems/") 1669 { 1670 entityPrivileges = { 1671 {boost::beast::http::verb::get, {{"Login"}}}, 1672 {boost::beast::http::verb::head, {{"Login"}}}, 1673 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1674 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1675 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1676 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1677 } 1678 1679 private: 1680 void doGet(crow::Response& res, const crow::Request&, 1681 const std::vector<std::string>&) override 1682 { 1683 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 1684 res.jsonValue["@odata.type"] = 1685 "#ComputerSystemCollection.ComputerSystemCollection"; 1686 res.jsonValue["@odata.id"] = "/redfish/v1/Systems"; 1687 res.jsonValue["Name"] = "Computer System Collection"; 1688 1689 crow::connections::systemBus->async_method_call( 1690 [asyncResp](const boost::system::error_code ec, 1691 const std::variant<std::string>& /*hostName*/) { 1692 nlohmann::json& ifaceArray = 1693 asyncResp->res.jsonValue["Members"]; 1694 ifaceArray = nlohmann::json::array(); 1695 auto& count = asyncResp->res.jsonValue["Members@odata.count"]; 1696 count = 0; 1697 ifaceArray.push_back( 1698 {{"@odata.id", "/redfish/v1/Systems/system"}}); 1699 if (!ec) 1700 { 1701 BMCWEB_LOG_DEBUG << "Hypervisor is available"; 1702 ifaceArray.push_back( 1703 {{"@odata.id", "/redfish/v1/Systems/hypervisor"}}); 1704 count = ifaceArray.size(); 1705 return; 1706 } 1707 }, 1708 "xyz.openbmc_project.Settings", 1709 "/xyz/openbmc_project/network/hypervisor", 1710 "org.freedesktop.DBus.Properties", "Get", 1711 "xyz.openbmc_project.Network.SystemConfiguration", "HostName"); 1712 } 1713 }; 1714 1715 /** 1716 * SystemActionsReset class supports handle POST method for Reset action. 1717 * The class retrieves and sends data directly to D-Bus. 1718 */ 1719 class SystemActionsReset : public Node 1720 { 1721 public: 1722 SystemActionsReset(App& app) : 1723 Node(app, "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset/") 1724 { 1725 entityPrivileges = { 1726 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1727 } 1728 1729 private: 1730 /** 1731 * Function handles POST method request. 1732 * Analyzes POST body message before sends Reset request data to D-Bus. 1733 */ 1734 void doPost(crow::Response& res, const crow::Request& req, 1735 const std::vector<std::string>&) override 1736 { 1737 auto asyncResp = std::make_shared<AsyncResp>(res); 1738 1739 std::string resetType; 1740 if (!json_util::readJson(req, res, "ResetType", resetType)) 1741 { 1742 return; 1743 } 1744 1745 // Get the command and host vs. chassis 1746 std::string command; 1747 bool hostCommand; 1748 if ((resetType == "On") || (resetType == "ForceOn")) 1749 { 1750 command = "xyz.openbmc_project.State.Host.Transition.On"; 1751 hostCommand = true; 1752 } 1753 else if (resetType == "ForceOff") 1754 { 1755 command = "xyz.openbmc_project.State.Chassis.Transition.Off"; 1756 hostCommand = false; 1757 } 1758 else if (resetType == "ForceRestart") 1759 { 1760 command = 1761 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot"; 1762 hostCommand = true; 1763 } 1764 else if (resetType == "GracefulShutdown") 1765 { 1766 command = "xyz.openbmc_project.State.Host.Transition.Off"; 1767 hostCommand = true; 1768 } 1769 else if (resetType == "GracefulRestart") 1770 { 1771 command = 1772 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot"; 1773 hostCommand = true; 1774 } 1775 else if (resetType == "PowerCycle") 1776 { 1777 command = "xyz.openbmc_project.State.Host.Transition.Reboot"; 1778 hostCommand = true; 1779 } 1780 else if (resetType == "Nmi") 1781 { 1782 doNMI(asyncResp); 1783 return; 1784 } 1785 else 1786 { 1787 messages::actionParameterUnknown(res, "Reset", resetType); 1788 return; 1789 } 1790 1791 if (hostCommand) 1792 { 1793 crow::connections::systemBus->async_method_call( 1794 [asyncResp, resetType](const boost::system::error_code ec) { 1795 if (ec) 1796 { 1797 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 1798 if (ec.value() == boost::asio::error::invalid_argument) 1799 { 1800 messages::actionParameterNotSupported( 1801 asyncResp->res, resetType, "Reset"); 1802 } 1803 else 1804 { 1805 messages::internalError(asyncResp->res); 1806 } 1807 return; 1808 } 1809 messages::success(asyncResp->res); 1810 }, 1811 "xyz.openbmc_project.State.Host", 1812 "/xyz/openbmc_project/state/host0", 1813 "org.freedesktop.DBus.Properties", "Set", 1814 "xyz.openbmc_project.State.Host", "RequestedHostTransition", 1815 std::variant<std::string>{command}); 1816 } 1817 else 1818 { 1819 crow::connections::systemBus->async_method_call( 1820 [asyncResp, resetType](const boost::system::error_code ec) { 1821 if (ec) 1822 { 1823 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 1824 if (ec.value() == boost::asio::error::invalid_argument) 1825 { 1826 messages::actionParameterNotSupported( 1827 asyncResp->res, resetType, "Reset"); 1828 } 1829 else 1830 { 1831 messages::internalError(asyncResp->res); 1832 } 1833 return; 1834 } 1835 messages::success(asyncResp->res); 1836 }, 1837 "xyz.openbmc_project.State.Chassis", 1838 "/xyz/openbmc_project/state/chassis0", 1839 "org.freedesktop.DBus.Properties", "Set", 1840 "xyz.openbmc_project.State.Chassis", "RequestedPowerTransition", 1841 std::variant<std::string>{command}); 1842 } 1843 } 1844 /** 1845 * Function transceives data with dbus directly. 1846 */ 1847 void doNMI(const std::shared_ptr<AsyncResp>& asyncResp) 1848 { 1849 constexpr char const* serviceName = 1850 "xyz.openbmc_project.Control.Host.NMI"; 1851 constexpr char const* objectPath = 1852 "/xyz/openbmc_project/control/host0/nmi"; 1853 constexpr char const* interfaceName = 1854 "xyz.openbmc_project.Control.Host.NMI"; 1855 constexpr char const* method = "NMI"; 1856 1857 crow::connections::systemBus->async_method_call( 1858 [asyncResp](const boost::system::error_code ec) { 1859 if (ec) 1860 { 1861 BMCWEB_LOG_ERROR << " Bad D-Bus request error: " << ec; 1862 messages::internalError(asyncResp->res); 1863 return; 1864 } 1865 messages::success(asyncResp->res); 1866 }, 1867 serviceName, objectPath, interfaceName, method); 1868 } 1869 }; 1870 1871 /** 1872 * Systems derived class for delivering Computer Systems Schema. 1873 */ 1874 class Systems : public Node 1875 { 1876 public: 1877 /* 1878 * Default Constructor 1879 */ 1880 Systems(App& app) : Node(app, "/redfish/v1/Systems/system/") 1881 { 1882 entityPrivileges = { 1883 {boost::beast::http::verb::get, {{"Login"}}}, 1884 {boost::beast::http::verb::head, {{"Login"}}}, 1885 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 1886 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 1887 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 1888 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 1889 } 1890 1891 private: 1892 /** 1893 * Functions triggers appropriate requests on DBus 1894 */ 1895 void doGet(crow::Response& res, const crow::Request&, 1896 const std::vector<std::string>&) override 1897 { 1898 res.jsonValue["@odata.type"] = "#ComputerSystem.v1_12_0.ComputerSystem"; 1899 res.jsonValue["Name"] = "system"; 1900 res.jsonValue["Id"] = "system"; 1901 res.jsonValue["SystemType"] = "Physical"; 1902 res.jsonValue["Description"] = "Computer System"; 1903 res.jsonValue["ProcessorSummary"]["Count"] = 0; 1904 res.jsonValue["ProcessorSummary"]["Status"]["State"] = "Disabled"; 1905 res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] = uint64_t(0); 1906 res.jsonValue["MemorySummary"]["Status"]["State"] = "Disabled"; 1907 res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system"; 1908 1909 res.jsonValue["Processors"] = { 1910 {"@odata.id", "/redfish/v1/Systems/system/Processors"}}; 1911 res.jsonValue["Memory"] = { 1912 {"@odata.id", "/redfish/v1/Systems/system/Memory"}}; 1913 res.jsonValue["Storage"] = { 1914 {"@odata.id", "/redfish/v1/Systems/system/Storage"}}; 1915 1916 res.jsonValue["Actions"]["#ComputerSystem.Reset"] = { 1917 {"target", 1918 "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"}, 1919 {"@Redfish.ActionInfo", 1920 "/redfish/v1/Systems/system/ResetActionInfo"}}; 1921 1922 res.jsonValue["LogServices"] = { 1923 {"@odata.id", "/redfish/v1/Systems/system/LogServices"}}; 1924 1925 res.jsonValue["Bios"] = { 1926 {"@odata.id", "/redfish/v1/Systems/system/Bios"}}; 1927 1928 res.jsonValue["Links"]["ManagedBy"] = { 1929 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 1930 1931 res.jsonValue["Status"] = { 1932 {"Health", "OK"}, 1933 {"State", "Enabled"}, 1934 }; 1935 auto asyncResp = std::make_shared<AsyncResp>(res); 1936 1937 constexpr const std::array<const char*, 4> inventoryForSystems = { 1938 "xyz.openbmc_project.Inventory.Item.Dimm", 1939 "xyz.openbmc_project.Inventory.Item.Cpu", 1940 "xyz.openbmc_project.Inventory.Item.Drive", 1941 "xyz.openbmc_project.Inventory.Item.StorageController"}; 1942 1943 auto health = std::make_shared<HealthPopulate>(asyncResp); 1944 crow::connections::systemBus->async_method_call( 1945 [health](const boost::system::error_code ec, 1946 std::vector<std::string>& resp) { 1947 if (ec) 1948 { 1949 // no inventory 1950 return; 1951 } 1952 1953 health->inventory = std::move(resp); 1954 }, 1955 "xyz.openbmc_project.ObjectMapper", 1956 "/xyz/openbmc_project/object_mapper", 1957 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 1958 int32_t(0), inventoryForSystems); 1959 1960 health->populate(); 1961 1962 getMainChassisId(asyncResp, [](const std::string& chassisId, 1963 const std::shared_ptr<AsyncResp>& aRsp) { 1964 aRsp->res.jsonValue["Links"]["Chassis"] = { 1965 {{"@odata.id", "/redfish/v1/Chassis/" + chassisId}}}; 1966 }); 1967 1968 getIndicatorLedState(asyncResp); 1969 getComputerSystem(asyncResp, health); 1970 getHostState(asyncResp); 1971 getBootProperties(asyncResp); 1972 getPCIeDeviceList(asyncResp, "PCIeDevices"); 1973 getHostWatchdogTimer(asyncResp); 1974 getPowerRestorePolicy(asyncResp); 1975 getAutomaticRetry(asyncResp); 1976 getLastResetTime(asyncResp); 1977 #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE 1978 getProvisioningStatus(asyncResp); 1979 #endif 1980 } 1981 1982 void doPatch(crow::Response& res, const crow::Request& req, 1983 const std::vector<std::string>&) override 1984 { 1985 std::optional<std::string> indicatorLed; 1986 std::optional<nlohmann::json> bootProps; 1987 std::optional<nlohmann::json> wdtTimerProps; 1988 std::optional<std::string> powerRestorePolicy; 1989 auto asyncResp = std::make_shared<AsyncResp>(res); 1990 1991 if (!json_util::readJson(req, res, "IndicatorLED", indicatorLed, "Boot", 1992 bootProps, "WatchdogTimer", wdtTimerProps, 1993 "PowerRestorePolicy", powerRestorePolicy)) 1994 { 1995 return; 1996 } 1997 1998 res.result(boost::beast::http::status::no_content); 1999 2000 if (wdtTimerProps) 2001 { 2002 std::optional<bool> wdtEnable; 2003 std::optional<std::string> wdtTimeOutAction; 2004 2005 if (!json_util::readJson(*wdtTimerProps, asyncResp->res, 2006 "FunctionEnabled", wdtEnable, 2007 "TimeoutAction", wdtTimeOutAction)) 2008 { 2009 return; 2010 } 2011 setWDTProperties(asyncResp, std::move(wdtEnable), 2012 std::move(wdtTimeOutAction)); 2013 } 2014 2015 if (bootProps) 2016 { 2017 std::optional<std::string> bootSource; 2018 std::optional<std::string> bootEnable; 2019 std::optional<std::string> automaticRetryConfig; 2020 2021 if (!json_util::readJson( 2022 *bootProps, asyncResp->res, "BootSourceOverrideTarget", 2023 bootSource, "BootSourceOverrideEnabled", bootEnable, 2024 "AutomaticRetryConfig", automaticRetryConfig)) 2025 { 2026 return; 2027 } 2028 if (bootSource || bootEnable) 2029 { 2030 setBootSourceProperties(asyncResp, std::move(bootSource), 2031 std::move(bootEnable)); 2032 } 2033 if (automaticRetryConfig) 2034 { 2035 setAutomaticRetry(asyncResp, std::move(*automaticRetryConfig)); 2036 } 2037 } 2038 2039 if (indicatorLed) 2040 { 2041 setIndicatorLedState(asyncResp, std::move(*indicatorLed)); 2042 } 2043 2044 if (powerRestorePolicy) 2045 { 2046 setPowerRestorePolicy(asyncResp, std::move(*powerRestorePolicy)); 2047 } 2048 } 2049 }; 2050 2051 /** 2052 * SystemResetActionInfo derived class for delivering Computer Systems 2053 * ResetType AllowableValues using ResetInfo schema. 2054 */ 2055 class SystemResetActionInfo : public Node 2056 { 2057 public: 2058 /* 2059 * Default Constructor 2060 */ 2061 SystemResetActionInfo(App& app) : 2062 Node(app, "/redfish/v1/Systems/system/ResetActionInfo/") 2063 { 2064 entityPrivileges = { 2065 {boost::beast::http::verb::get, {{"Login"}}}, 2066 {boost::beast::http::verb::head, {{"Login"}}}, 2067 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}, 2068 {boost::beast::http::verb::put, {{"ConfigureComponents"}}}, 2069 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}}, 2070 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}}; 2071 } 2072 2073 private: 2074 /** 2075 * Functions triggers appropriate requests on DBus 2076 */ 2077 void doGet(crow::Response& res, const crow::Request&, 2078 const std::vector<std::string>&) override 2079 { 2080 res.jsonValue = { 2081 {"@odata.type", "#ActionInfo.v1_1_2.ActionInfo"}, 2082 {"@odata.id", "/redfish/v1/Systems/system/ResetActionInfo"}, 2083 {"Name", "Reset Action Info"}, 2084 {"Id", "ResetActionInfo"}, 2085 {"Parameters", 2086 {{{"Name", "ResetType"}, 2087 {"Required", true}, 2088 {"DataType", "String"}, 2089 {"AllowableValues", 2090 {"On", "ForceOff", "ForceOn", "ForceRestart", "GracefulRestart", 2091 "GracefulShutdown", "PowerCycle", "Nmi"}}}}}}; 2092 res.end(); 2093 } 2094 }; 2095 } // namespace redfish 2096