1 /* 2 // Copyright (c) 2018 Intel Corporation 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 // Unless required by applicable law or agreed to in writing, software 11 // distributed under the License is distributed on an "AS IS" BASIS, 12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 // See the License for the specific language governing permissions and 14 // limitations under the License. 15 */ 16 #pragma once 17 18 #include "app.hpp" 19 #include "dbus_singleton.hpp" 20 #include "dbus_utility.hpp" 21 #include "health.hpp" 22 #include "led.hpp" 23 #include "pcie.hpp" 24 #include "query.hpp" 25 #include "redfish_util.hpp" 26 #include "registries/privilege_registry.hpp" 27 #include "utils/dbus_utils.hpp" 28 #include "utils/json_utils.hpp" 29 #include "utils/sw_utils.hpp" 30 #include "utils/time_utils.hpp" 31 32 #include <boost/container/flat_map.hpp> 33 #include <boost/system/error_code.hpp> 34 #include <sdbusplus/asio/property.hpp> 35 #include <sdbusplus/unpack_properties.hpp> 36 37 #include <array> 38 #include <string_view> 39 #include <variant> 40 41 namespace redfish 42 { 43 44 const static std::array<std::pair<std::string_view, std::string_view>, 2> 45 protocolToDBusForSystems{ 46 {{"SSH", "obmc-console-ssh"}, {"IPMI", "phosphor-ipmi-net"}}}; 47 48 /** 49 * @brief Updates the Functional State of DIMMs 50 * 51 * @param[in] aResp Shared pointer for completing asynchronous calls 52 * @param[in] dimmState Dimm's Functional state, true/false 53 * 54 * @return None. 55 */ 56 inline void 57 updateDimmProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 58 bool isDimmFunctional) 59 { 60 BMCWEB_LOG_DEBUG << "Dimm Functional: " << isDimmFunctional; 61 62 // Set it as Enabled if at least one DIMM is functional 63 // Update STATE only if previous State was DISABLED and current Dimm is 64 // ENABLED. 65 const nlohmann::json& prevMemSummary = 66 aResp->res.jsonValue["MemorySummary"]["Status"]["State"]; 67 if (prevMemSummary == "Disabled") 68 { 69 if (isDimmFunctional) 70 { 71 aResp->res.jsonValue["MemorySummary"]["Status"]["State"] = 72 "Enabled"; 73 } 74 } 75 } 76 77 /* 78 * @brief Update "ProcessorSummary" "Count" based on Cpu PresenceState 79 * 80 * @param[in] aResp Shared pointer for completing asynchronous calls 81 * @param[in] cpuPresenceState CPU present or not 82 * 83 * @return None. 84 */ 85 inline void 86 modifyCpuPresenceState(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 87 bool isCpuPresent) 88 { 89 BMCWEB_LOG_DEBUG << "Cpu Present: " << isCpuPresent; 90 91 if (isCpuPresent) 92 { 93 nlohmann::json& procCount = 94 aResp->res.jsonValue["ProcessorSummary"]["Count"]; 95 auto* procCountPtr = 96 procCount.get_ptr<nlohmann::json::number_integer_t*>(); 97 if (procCountPtr != nullptr) 98 { 99 // shouldn't be possible to be nullptr 100 *procCountPtr += 1; 101 } 102 } 103 } 104 105 /* 106 * @brief Update "ProcessorSummary" "Status" "State" based on 107 * CPU Functional State 108 * 109 * @param[in] aResp Shared pointer for completing asynchronous calls 110 * @param[in] cpuFunctionalState is CPU functional true/false 111 * 112 * @return None. 113 */ 114 inline void 115 modifyCpuFunctionalState(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 116 bool isCpuFunctional) 117 { 118 BMCWEB_LOG_DEBUG << "Cpu Functional: " << isCpuFunctional; 119 120 const nlohmann::json& prevProcState = 121 aResp->res.jsonValue["ProcessorSummary"]["Status"]["State"]; 122 123 // Set it as Enabled if at least one CPU is functional 124 // Update STATE only if previous State was Non_Functional and current CPU is 125 // Functional. 126 if (prevProcState == "Disabled") 127 { 128 if (isCpuFunctional) 129 { 130 aResp->res.jsonValue["ProcessorSummary"]["Status"]["State"] = 131 "Enabled"; 132 } 133 } 134 } 135 136 inline void getProcessorProperties( 137 const std::shared_ptr<bmcweb::AsyncResp>& aResp, 138 const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>& 139 properties) 140 { 141 142 BMCWEB_LOG_DEBUG << "Got " << properties.size() << " Cpu properties."; 143 144 // TODO: Get Model 145 146 const uint16_t* coreCount = nullptr; 147 148 const bool success = sdbusplus::unpackPropertiesNoThrow( 149 dbus_utils::UnpackErrorPrinter(), properties, "CoreCount", coreCount); 150 151 if (!success) 152 { 153 messages::internalError(aResp->res); 154 return; 155 } 156 157 if (coreCount != nullptr) 158 { 159 nlohmann::json& coreCountJson = 160 aResp->res.jsonValue["ProcessorSummary"]["CoreCount"]; 161 uint64_t* coreCountJsonPtr = coreCountJson.get_ptr<uint64_t*>(); 162 163 if (coreCountJsonPtr == nullptr) 164 { 165 coreCountJson = *coreCount; 166 } 167 else 168 { 169 *coreCountJsonPtr += *coreCount; 170 } 171 } 172 } 173 174 /* 175 * @brief Get ProcessorSummary fields 176 * 177 * @param[in] aResp Shared pointer for completing asynchronous calls 178 * @param[in] service dbus service for Cpu Information 179 * @param[in] path dbus path for Cpu 180 * 181 * @return None. 182 */ 183 inline void getProcessorSummary(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 184 const std::string& service, 185 const std::string& path) 186 { 187 188 auto getCpuPresenceState = [aResp](const boost::system::error_code& ec3, 189 const bool cpuPresenceCheck) { 190 if (ec3) 191 { 192 BMCWEB_LOG_ERROR << "DBUS response error " << ec3; 193 return; 194 } 195 modifyCpuPresenceState(aResp, cpuPresenceCheck); 196 }; 197 198 auto getCpuFunctionalState = [aResp](const boost::system::error_code& ec3, 199 const bool cpuFunctionalCheck) { 200 if (ec3) 201 { 202 BMCWEB_LOG_ERROR << "DBUS response error " << ec3; 203 return; 204 } 205 modifyCpuFunctionalState(aResp, cpuFunctionalCheck); 206 }; 207 208 // Get the Presence of CPU 209 sdbusplus::asio::getProperty<bool>( 210 *crow::connections::systemBus, service, path, 211 "xyz.openbmc_project.Inventory.Item", "Present", 212 std::move(getCpuPresenceState)); 213 214 // Get the Functional State 215 sdbusplus::asio::getProperty<bool>( 216 *crow::connections::systemBus, service, path, 217 "xyz.openbmc_project.State.Decorator.OperationalStatus", "Functional", 218 std::move(getCpuFunctionalState)); 219 220 sdbusplus::asio::getAllProperties( 221 *crow::connections::systemBus, service, path, 222 "xyz.openbmc_project.Inventory.Item.Cpu", 223 [aResp, service, 224 path](const boost::system::error_code& ec2, 225 const dbus::utility::DBusPropertiesMap& properties) { 226 if (ec2) 227 { 228 BMCWEB_LOG_ERROR << "DBUS response error " << ec2; 229 messages::internalError(aResp->res); 230 return; 231 } 232 getProcessorProperties(aResp, properties); 233 }); 234 } 235 236 /* 237 * @brief Retrieves computer system properties over dbus 238 * 239 * @param[in] aResp Shared pointer for completing asynchronous calls 240 * @param[in] systemHealth Shared HealthPopulate pointer 241 * 242 * @return None. 243 */ 244 inline void 245 getComputerSystem(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 246 const std::shared_ptr<HealthPopulate>& systemHealth) 247 { 248 BMCWEB_LOG_DEBUG << "Get available system components."; 249 constexpr std::array<std::string_view, 5> interfaces = { 250 "xyz.openbmc_project.Inventory.Decorator.Asset", 251 "xyz.openbmc_project.Inventory.Item.Cpu", 252 "xyz.openbmc_project.Inventory.Item.Dimm", 253 "xyz.openbmc_project.Inventory.Item.System", 254 "xyz.openbmc_project.Common.UUID", 255 }; 256 dbus::utility::getSubTree( 257 "/xyz/openbmc_project/inventory", 0, interfaces, 258 [aResp, 259 systemHealth](const boost::system::error_code& ec, 260 const dbus::utility::MapperGetSubTreeResponse& subtree) { 261 if (ec) 262 { 263 BMCWEB_LOG_DEBUG << "DBUS response error"; 264 messages::internalError(aResp->res); 265 return; 266 } 267 // Iterate over all retrieved ObjectPaths. 268 for (const std::pair< 269 std::string, 270 std::vector<std::pair<std::string, std::vector<std::string>>>>& 271 object : subtree) 272 { 273 const std::string& path = object.first; 274 BMCWEB_LOG_DEBUG << "Got path: " << path; 275 const std::vector<std::pair<std::string, std::vector<std::string>>>& 276 connectionNames = object.second; 277 if (connectionNames.empty()) 278 { 279 continue; 280 } 281 282 auto memoryHealth = std::make_shared<HealthPopulate>( 283 aResp, "/MemorySummary/Status"_json_pointer); 284 285 auto cpuHealth = std::make_shared<HealthPopulate>( 286 aResp, "/ProcessorSummary/Status"_json_pointer); 287 288 systemHealth->children.emplace_back(memoryHealth); 289 systemHealth->children.emplace_back(cpuHealth); 290 291 // This is not system, so check if it's cpu, dimm, UUID or 292 // BiosVer 293 for (const auto& connection : connectionNames) 294 { 295 for (const auto& interfaceName : connection.second) 296 { 297 if (interfaceName == 298 "xyz.openbmc_project.Inventory.Item.Dimm") 299 { 300 BMCWEB_LOG_DEBUG 301 << "Found Dimm, now get its properties."; 302 303 sdbusplus::asio::getAllProperties( 304 *crow::connections::systemBus, connection.first, 305 path, "xyz.openbmc_project.Inventory.Item.Dimm", 306 [aResp, service{connection.first}, 307 path](const boost::system::error_code& ec2, 308 const dbus::utility::DBusPropertiesMap& 309 properties) { 310 if (ec2) 311 { 312 BMCWEB_LOG_ERROR << "DBUS response error " 313 << ec2; 314 messages::internalError(aResp->res); 315 return; 316 } 317 BMCWEB_LOG_DEBUG << "Got " << properties.size() 318 << " Dimm properties."; 319 320 if (properties.empty()) 321 { 322 sdbusplus::asio::getProperty<bool>( 323 *crow::connections::systemBus, service, 324 path, 325 "xyz.openbmc_project.State." 326 "Decorator.OperationalStatus", 327 "Functional", 328 [aResp]( 329 const boost::system::error_code& ec3, 330 bool dimmState) { 331 if (ec3) 332 { 333 BMCWEB_LOG_ERROR 334 << "DBUS response error " << ec3; 335 return; 336 } 337 updateDimmProperties(aResp, dimmState); 338 }); 339 return; 340 } 341 342 const uint32_t* memorySizeInKB = nullptr; 343 344 const bool success = 345 sdbusplus::unpackPropertiesNoThrow( 346 dbus_utils::UnpackErrorPrinter(), 347 properties, "MemorySizeInKB", 348 memorySizeInKB); 349 350 if (!success) 351 { 352 messages::internalError(aResp->res); 353 return; 354 } 355 356 if (memorySizeInKB != nullptr) 357 { 358 nlohmann::json& totalMemory = 359 aResp->res 360 .jsonValue["MemorySummary"] 361 ["TotalSystemMemoryGiB"]; 362 const uint64_t* preValue = 363 totalMemory.get_ptr<const uint64_t*>(); 364 if (preValue == nullptr) 365 { 366 aResp->res 367 .jsonValue["MemorySummary"] 368 ["TotalSystemMemoryGiB"] = 369 *memorySizeInKB / (1024 * 1024); 370 } 371 else 372 { 373 aResp->res 374 .jsonValue["MemorySummary"] 375 ["TotalSystemMemoryGiB"] = 376 *memorySizeInKB / (1024 * 1024) + 377 *preValue; 378 } 379 aResp->res.jsonValue["MemorySummary"]["Status"] 380 ["State"] = "Enabled"; 381 } 382 }); 383 384 memoryHealth->inventory.emplace_back(path); 385 } 386 else if (interfaceName == 387 "xyz.openbmc_project.Inventory.Item.Cpu") 388 { 389 BMCWEB_LOG_DEBUG 390 << "Found Cpu, now get its properties."; 391 392 getProcessorSummary(aResp, connection.first, path); 393 394 cpuHealth->inventory.emplace_back(path); 395 } 396 else if (interfaceName == "xyz.openbmc_project.Common.UUID") 397 { 398 BMCWEB_LOG_DEBUG 399 << "Found UUID, now get its properties."; 400 401 sdbusplus::asio::getAllProperties( 402 *crow::connections::systemBus, connection.first, 403 path, "xyz.openbmc_project.Common.UUID", 404 [aResp](const boost::system::error_code& ec3, 405 const dbus::utility::DBusPropertiesMap& 406 properties) { 407 if (ec3) 408 { 409 BMCWEB_LOG_DEBUG << "DBUS response error " 410 << ec3; 411 messages::internalError(aResp->res); 412 return; 413 } 414 BMCWEB_LOG_DEBUG << "Got " << properties.size() 415 << " UUID properties."; 416 417 const std::string* uUID = nullptr; 418 419 const bool success = 420 sdbusplus::unpackPropertiesNoThrow( 421 dbus_utils::UnpackErrorPrinter(), 422 properties, "UUID", uUID); 423 424 if (!success) 425 { 426 messages::internalError(aResp->res); 427 return; 428 } 429 430 if (uUID != nullptr) 431 { 432 std::string valueStr = *uUID; 433 if (valueStr.size() == 32) 434 { 435 valueStr.insert(8, 1, '-'); 436 valueStr.insert(13, 1, '-'); 437 valueStr.insert(18, 1, '-'); 438 valueStr.insert(23, 1, '-'); 439 } 440 BMCWEB_LOG_DEBUG << "UUID = " << valueStr; 441 aResp->res.jsonValue["UUID"] = valueStr; 442 } 443 }); 444 } 445 else if (interfaceName == 446 "xyz.openbmc_project.Inventory.Item.System") 447 { 448 sdbusplus::asio::getAllProperties( 449 *crow::connections::systemBus, connection.first, 450 path, 451 "xyz.openbmc_project.Inventory.Decorator.Asset", 452 [aResp](const boost::system::error_code& ec2, 453 const dbus::utility::DBusPropertiesMap& 454 propertiesList) { 455 if (ec2) 456 { 457 // doesn't have to include this 458 // interface 459 return; 460 } 461 BMCWEB_LOG_DEBUG << "Got " << propertiesList.size() 462 << " properties for system"; 463 464 const std::string* partNumber = nullptr; 465 const std::string* serialNumber = nullptr; 466 const std::string* manufacturer = nullptr; 467 const std::string* model = nullptr; 468 const std::string* subModel = nullptr; 469 470 const bool success = 471 sdbusplus::unpackPropertiesNoThrow( 472 dbus_utils::UnpackErrorPrinter(), 473 propertiesList, "PartNumber", partNumber, 474 "SerialNumber", serialNumber, 475 "Manufacturer", manufacturer, "Model", 476 model, "SubModel", subModel); 477 478 if (!success) 479 { 480 messages::internalError(aResp->res); 481 return; 482 } 483 484 if (partNumber != nullptr) 485 { 486 aResp->res.jsonValue["PartNumber"] = 487 *partNumber; 488 } 489 490 if (serialNumber != nullptr) 491 { 492 aResp->res.jsonValue["SerialNumber"] = 493 *serialNumber; 494 } 495 496 if (manufacturer != nullptr) 497 { 498 aResp->res.jsonValue["Manufacturer"] = 499 *manufacturer; 500 } 501 502 if (model != nullptr) 503 { 504 aResp->res.jsonValue["Model"] = *model; 505 } 506 507 if (subModel != nullptr) 508 { 509 aResp->res.jsonValue["SubModel"] = *subModel; 510 } 511 512 // Grab the bios version 513 sw_util::populateSoftwareInformation( 514 aResp, sw_util::biosPurpose, "BiosVersion", 515 false); 516 }); 517 518 sdbusplus::asio::getProperty<std::string>( 519 *crow::connections::systemBus, connection.first, 520 path, 521 "xyz.openbmc_project.Inventory.Decorator." 522 "AssetTag", 523 "AssetTag", 524 [aResp](const boost::system::error_code& ec2, 525 const std::string& value) { 526 if (ec2) 527 { 528 // doesn't have to include this 529 // interface 530 return; 531 } 532 533 aResp->res.jsonValue["AssetTag"] = value; 534 }); 535 } 536 } 537 break; 538 } 539 } 540 }); 541 } 542 543 /** 544 * @brief Retrieves host state properties over dbus 545 * 546 * @param[in] aResp Shared pointer for completing asynchronous calls. 547 * 548 * @return None. 549 */ 550 inline void getHostState(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 551 { 552 BMCWEB_LOG_DEBUG << "Get host information."; 553 sdbusplus::asio::getProperty<std::string>( 554 *crow::connections::systemBus, "xyz.openbmc_project.State.Host", 555 "/xyz/openbmc_project/state/host0", "xyz.openbmc_project.State.Host", 556 "CurrentHostState", 557 [aResp](const boost::system::error_code& ec, 558 const std::string& hostState) { 559 if (ec) 560 { 561 if (ec == boost::system::errc::host_unreachable) 562 { 563 // Service not available, no error, just don't return 564 // host state info 565 BMCWEB_LOG_DEBUG << "Service not available " << ec; 566 return; 567 } 568 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 569 messages::internalError(aResp->res); 570 return; 571 } 572 573 BMCWEB_LOG_DEBUG << "Host state: " << hostState; 574 // Verify Host State 575 if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") 576 { 577 aResp->res.jsonValue["PowerState"] = "On"; 578 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 579 } 580 else if (hostState == 581 "xyz.openbmc_project.State.Host.HostState.Quiesced") 582 { 583 aResp->res.jsonValue["PowerState"] = "On"; 584 aResp->res.jsonValue["Status"]["State"] = "Quiesced"; 585 } 586 else if (hostState == 587 "xyz.openbmc_project.State.Host.HostState.DiagnosticMode") 588 { 589 aResp->res.jsonValue["PowerState"] = "On"; 590 aResp->res.jsonValue["Status"]["State"] = "InTest"; 591 } 592 else if ( 593 hostState == 594 "xyz.openbmc_project.State.Host.HostState.TransitioningToRunning") 595 { 596 aResp->res.jsonValue["PowerState"] = "PoweringOn"; 597 aResp->res.jsonValue["Status"]["State"] = "Starting"; 598 } 599 else if (hostState == 600 "xyz.openbmc_project.State.Host.HostState.TransitioningToOff") 601 { 602 aResp->res.jsonValue["PowerState"] = "PoweringOff"; 603 aResp->res.jsonValue["Status"]["State"] = "Disabled"; 604 } 605 else 606 { 607 aResp->res.jsonValue["PowerState"] = "Off"; 608 aResp->res.jsonValue["Status"]["State"] = "Disabled"; 609 } 610 }); 611 } 612 613 /** 614 * @brief Translates boot source DBUS property value to redfish. 615 * 616 * @param[in] dbusSource The boot source in DBUS speak. 617 * 618 * @return Returns as a string, the boot source in Redfish terms. If translation 619 * cannot be done, returns an empty string. 620 */ 621 inline std::string dbusToRfBootSource(const std::string& dbusSource) 622 { 623 if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Default") 624 { 625 return "None"; 626 } 627 if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Disk") 628 { 629 return "Hdd"; 630 } 631 if (dbusSource == 632 "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia") 633 { 634 return "Cd"; 635 } 636 if (dbusSource == "xyz.openbmc_project.Control.Boot.Source.Sources.Network") 637 { 638 return "Pxe"; 639 } 640 if (dbusSource == 641 "xyz.openbmc_project.Control.Boot.Source.Sources.RemovableMedia") 642 { 643 return "Usb"; 644 } 645 return ""; 646 } 647 648 /** 649 * @brief Translates boot type DBUS property value to redfish. 650 * 651 * @param[in] dbusType The boot type in DBUS speak. 652 * 653 * @return Returns as a string, the boot type in Redfish terms. If translation 654 * cannot be done, returns an empty string. 655 */ 656 inline std::string dbusToRfBootType(const std::string& dbusType) 657 { 658 if (dbusType == "xyz.openbmc_project.Control.Boot.Type.Types.Legacy") 659 { 660 return "Legacy"; 661 } 662 if (dbusType == "xyz.openbmc_project.Control.Boot.Type.Types.EFI") 663 { 664 return "UEFI"; 665 } 666 return ""; 667 } 668 669 /** 670 * @brief Translates boot mode DBUS property value to redfish. 671 * 672 * @param[in] dbusMode The boot mode in DBUS speak. 673 * 674 * @return Returns as a string, the boot mode in Redfish terms. If translation 675 * cannot be done, returns an empty string. 676 */ 677 inline std::string dbusToRfBootMode(const std::string& dbusMode) 678 { 679 if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") 680 { 681 return "None"; 682 } 683 if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe") 684 { 685 return "Diags"; 686 } 687 if (dbusMode == "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup") 688 { 689 return "BiosSetup"; 690 } 691 return ""; 692 } 693 694 /** 695 * @brief Translates boot progress DBUS property value to redfish. 696 * 697 * @param[in] dbusBootProgress The boot progress in DBUS speak. 698 * 699 * @return Returns as a string, the boot progress in Redfish terms. If 700 * translation cannot be done, returns "None". 701 */ 702 inline std::string dbusToRfBootProgress(const std::string& dbusBootProgress) 703 { 704 // Now convert the D-Bus BootProgress to the appropriate Redfish 705 // enum 706 std::string rfBpLastState = "None"; 707 if (dbusBootProgress == "xyz.openbmc_project.State.Boot.Progress." 708 "ProgressStages.Unspecified") 709 { 710 rfBpLastState = "None"; 711 } 712 else if (dbusBootProgress == 713 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 714 "PrimaryProcInit") 715 { 716 rfBpLastState = "PrimaryProcessorInitializationStarted"; 717 } 718 else if (dbusBootProgress == 719 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 720 "BusInit") 721 { 722 rfBpLastState = "BusInitializationStarted"; 723 } 724 else if (dbusBootProgress == 725 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 726 "MemoryInit") 727 { 728 rfBpLastState = "MemoryInitializationStarted"; 729 } 730 else if (dbusBootProgress == 731 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 732 "SecondaryProcInit") 733 { 734 rfBpLastState = "SecondaryProcessorInitializationStarted"; 735 } 736 else if (dbusBootProgress == 737 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 738 "PCIInit") 739 { 740 rfBpLastState = "PCIResourceConfigStarted"; 741 } 742 else if (dbusBootProgress == 743 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 744 "SystemSetup") 745 { 746 rfBpLastState = "SetupEntered"; 747 } 748 else if (dbusBootProgress == 749 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 750 "SystemInitComplete") 751 { 752 rfBpLastState = "SystemHardwareInitializationComplete"; 753 } 754 else if (dbusBootProgress == 755 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 756 "OSStart") 757 { 758 rfBpLastState = "OSBootStarted"; 759 } 760 else if (dbusBootProgress == 761 "xyz.openbmc_project.State.Boot.Progress.ProgressStages." 762 "OSRunning") 763 { 764 rfBpLastState = "OSRunning"; 765 } 766 else 767 { 768 BMCWEB_LOG_DEBUG << "Unsupported D-Bus BootProgress " 769 << dbusBootProgress; 770 // Just return the default 771 } 772 return rfBpLastState; 773 } 774 775 /** 776 * @brief Translates boot source from Redfish to the DBus boot paths. 777 * 778 * @param[in] rfSource The boot source in Redfish. 779 * @param[out] bootSource The DBus source 780 * @param[out] bootMode the DBus boot mode 781 * 782 * @return Integer error code. 783 */ 784 inline int assignBootParameters(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 785 const std::string& rfSource, 786 std::string& bootSource, std::string& bootMode) 787 { 788 bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Default"; 789 bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular"; 790 791 if (rfSource == "None") 792 { 793 return 0; 794 } 795 if (rfSource == "Pxe") 796 { 797 bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Network"; 798 } 799 else if (rfSource == "Hdd") 800 { 801 bootSource = "xyz.openbmc_project.Control.Boot.Source.Sources.Disk"; 802 } 803 else if (rfSource == "Diags") 804 { 805 bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Safe"; 806 } 807 else if (rfSource == "Cd") 808 { 809 bootSource = 810 "xyz.openbmc_project.Control.Boot.Source.Sources.ExternalMedia"; 811 } 812 else if (rfSource == "BiosSetup") 813 { 814 bootMode = "xyz.openbmc_project.Control.Boot.Mode.Modes.Setup"; 815 } 816 else if (rfSource == "Usb") 817 { 818 bootSource = 819 "xyz.openbmc_project.Control.Boot.Source.Sources.RemovableMedia"; 820 } 821 else 822 { 823 BMCWEB_LOG_DEBUG 824 << "Invalid property value for BootSourceOverrideTarget: " 825 << bootSource; 826 messages::propertyValueNotInList(aResp->res, rfSource, 827 "BootSourceTargetOverride"); 828 return -1; 829 } 830 return 0; 831 } 832 833 /** 834 * @brief Retrieves boot progress of the system 835 * 836 * @param[in] aResp Shared pointer for generating response message. 837 * 838 * @return None. 839 */ 840 inline void getBootProgress(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 841 { 842 sdbusplus::asio::getProperty<std::string>( 843 *crow::connections::systemBus, "xyz.openbmc_project.State.Host", 844 "/xyz/openbmc_project/state/host0", 845 "xyz.openbmc_project.State.Boot.Progress", "BootProgress", 846 [aResp](const boost::system::error_code& ec, 847 const std::string& bootProgressStr) { 848 if (ec) 849 { 850 // BootProgress is an optional object so just do nothing if 851 // not found 852 return; 853 } 854 855 BMCWEB_LOG_DEBUG << "Boot Progress: " << bootProgressStr; 856 857 aResp->res.jsonValue["BootProgress"]["LastState"] = 858 dbusToRfBootProgress(bootProgressStr); 859 }); 860 } 861 862 /** 863 * @brief Retrieves boot progress Last Update of the system 864 * 865 * @param[in] aResp Shared pointer for generating response message. 866 * 867 * @return None. 868 */ 869 inline void getBootProgressLastStateTime( 870 const std::shared_ptr<bmcweb::AsyncResp>& aResp) 871 { 872 sdbusplus::asio::getProperty<uint64_t>( 873 *crow::connections::systemBus, "xyz.openbmc_project.State.Host", 874 "/xyz/openbmc_project/state/host0", 875 "xyz.openbmc_project.State.Boot.Progress", "BootProgressLastUpdate", 876 [aResp](const boost::system::error_code& ec, 877 const uint64_t lastStateTime) { 878 if (ec) 879 { 880 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 881 return; 882 } 883 884 // BootProgressLastUpdate is the last time the BootProgress property 885 // was updated. The time is the Epoch time, number of microseconds 886 // since 1 Jan 1970 00::00::00 UTC." 887 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/master/ 888 // yaml/xyz/openbmc_project/State/Boot/Progress.interface.yaml#L11 889 890 // Convert to ISO 8601 standard 891 aResp->res.jsonValue["BootProgress"]["LastStateTime"] = 892 redfish::time_utils::getDateTimeUintUs(lastStateTime); 893 }); 894 } 895 896 /** 897 * @brief Retrieves boot override type over DBUS and fills out the response 898 * 899 * @param[in] aResp Shared pointer for generating response message. 900 * 901 * @return None. 902 */ 903 904 inline void getBootOverrideType(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 905 { 906 sdbusplus::asio::getProperty<std::string>( 907 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 908 "/xyz/openbmc_project/control/host0/boot", 909 "xyz.openbmc_project.Control.Boot.Type", "BootType", 910 [aResp](const boost::system::error_code& ec, 911 const std::string& bootType) { 912 if (ec) 913 { 914 // not an error, don't have to have the interface 915 return; 916 } 917 918 BMCWEB_LOG_DEBUG << "Boot type: " << bootType; 919 920 aResp->res.jsonValue["Boot"] 921 ["BootSourceOverrideMode@Redfish.AllowableValues"] = 922 nlohmann::json::array_t({"Legacy", "UEFI"}); 923 924 auto rfType = dbusToRfBootType(bootType); 925 if (rfType.empty()) 926 { 927 messages::internalError(aResp->res); 928 return; 929 } 930 931 aResp->res.jsonValue["Boot"]["BootSourceOverrideMode"] = rfType; 932 }); 933 } 934 935 /** 936 * @brief Retrieves boot override mode over DBUS and fills out the response 937 * 938 * @param[in] aResp Shared pointer for generating response message. 939 * 940 * @return None. 941 */ 942 943 inline void getBootOverrideMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 944 { 945 sdbusplus::asio::getProperty<std::string>( 946 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 947 "/xyz/openbmc_project/control/host0/boot", 948 "xyz.openbmc_project.Control.Boot.Mode", "BootMode", 949 [aResp](const boost::system::error_code& ec, 950 const std::string& bootModeStr) { 951 if (ec) 952 { 953 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 954 messages::internalError(aResp->res); 955 return; 956 } 957 958 BMCWEB_LOG_DEBUG << "Boot mode: " << bootModeStr; 959 960 aResp->res 961 .jsonValue["Boot"] 962 ["BootSourceOverrideTarget@Redfish.AllowableValues"] = { 963 "None", "Pxe", "Hdd", "Cd", "Diags", "BiosSetup", "Usb"}; 964 965 if (bootModeStr != 966 "xyz.openbmc_project.Control.Boot.Mode.Modes.Regular") 967 { 968 auto rfMode = dbusToRfBootMode(bootModeStr); 969 if (!rfMode.empty()) 970 { 971 aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = 972 rfMode; 973 } 974 } 975 }); 976 } 977 978 /** 979 * @brief Retrieves boot override source over DBUS 980 * 981 * @param[in] aResp Shared pointer for generating response message. 982 * 983 * @return None. 984 */ 985 986 inline void 987 getBootOverrideSource(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 988 { 989 sdbusplus::asio::getProperty<std::string>( 990 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 991 "/xyz/openbmc_project/control/host0/boot", 992 "xyz.openbmc_project.Control.Boot.Source", "BootSource", 993 [aResp](const boost::system::error_code& ec, 994 const std::string& bootSourceStr) { 995 if (ec) 996 { 997 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 998 if (ec.value() == boost::asio::error::host_unreachable) 999 { 1000 return; 1001 } 1002 messages::internalError(aResp->res); 1003 return; 1004 } 1005 1006 BMCWEB_LOG_DEBUG << "Boot source: " << bootSourceStr; 1007 1008 auto rfSource = dbusToRfBootSource(bootSourceStr); 1009 if (!rfSource.empty()) 1010 { 1011 aResp->res.jsonValue["Boot"]["BootSourceOverrideTarget"] = rfSource; 1012 } 1013 1014 // Get BootMode as BootSourceOverrideTarget is constructed 1015 // from both BootSource and BootMode 1016 getBootOverrideMode(aResp); 1017 }); 1018 } 1019 1020 /** 1021 * @brief This functions abstracts all the logic behind getting a 1022 * "BootSourceOverrideEnabled" property from an overall boot override enable 1023 * state 1024 * 1025 * @param[in] aResp Shared pointer for generating response message. 1026 * 1027 * @return None. 1028 */ 1029 1030 inline void 1031 processBootOverrideEnable(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1032 const bool bootOverrideEnableSetting) 1033 { 1034 if (!bootOverrideEnableSetting) 1035 { 1036 aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = "Disabled"; 1037 return; 1038 } 1039 1040 // If boot source override is enabled, we need to check 'one_time' 1041 // property to set a correct value for the "BootSourceOverrideEnabled" 1042 sdbusplus::asio::getProperty<bool>( 1043 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1044 "/xyz/openbmc_project/control/host0/boot/one_time", 1045 "xyz.openbmc_project.Object.Enable", "Enabled", 1046 [aResp](const boost::system::error_code& ec, bool oneTimeSetting) { 1047 if (ec) 1048 { 1049 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1050 messages::internalError(aResp->res); 1051 return; 1052 } 1053 1054 if (oneTimeSetting) 1055 { 1056 aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = "Once"; 1057 } 1058 else 1059 { 1060 aResp->res.jsonValue["Boot"]["BootSourceOverrideEnabled"] = 1061 "Continuous"; 1062 } 1063 }); 1064 } 1065 1066 /** 1067 * @brief Retrieves boot override enable over DBUS 1068 * 1069 * @param[in] aResp Shared pointer for generating response message. 1070 * 1071 * @return None. 1072 */ 1073 1074 inline void 1075 getBootOverrideEnable(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1076 { 1077 sdbusplus::asio::getProperty<bool>( 1078 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1079 "/xyz/openbmc_project/control/host0/boot", 1080 "xyz.openbmc_project.Object.Enable", "Enabled", 1081 [aResp](const boost::system::error_code& ec, 1082 const bool bootOverrideEnable) { 1083 if (ec) 1084 { 1085 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1086 if (ec.value() == boost::asio::error::host_unreachable) 1087 { 1088 return; 1089 } 1090 messages::internalError(aResp->res); 1091 return; 1092 } 1093 1094 processBootOverrideEnable(aResp, bootOverrideEnable); 1095 }); 1096 } 1097 1098 /** 1099 * @brief Retrieves boot source override properties 1100 * 1101 * @param[in] aResp Shared pointer for generating response message. 1102 * 1103 * @return None. 1104 */ 1105 inline void getBootProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1106 { 1107 BMCWEB_LOG_DEBUG << "Get boot information."; 1108 1109 getBootOverrideSource(aResp); 1110 getBootOverrideType(aResp); 1111 getBootOverrideEnable(aResp); 1112 } 1113 1114 /** 1115 * @brief Retrieves the Last Reset Time 1116 * 1117 * "Reset" is an overloaded term in Redfish, "Reset" includes power on 1118 * and power off. Even though this is the "system" Redfish object look at the 1119 * chassis D-Bus interface for the LastStateChangeTime since this has the 1120 * last power operation time. 1121 * 1122 * @param[in] aResp Shared pointer for generating response message. 1123 * 1124 * @return None. 1125 */ 1126 inline void getLastResetTime(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1127 { 1128 BMCWEB_LOG_DEBUG << "Getting System Last Reset Time"; 1129 1130 sdbusplus::asio::getProperty<uint64_t>( 1131 *crow::connections::systemBus, "xyz.openbmc_project.State.Chassis", 1132 "/xyz/openbmc_project/state/chassis0", 1133 "xyz.openbmc_project.State.Chassis", "LastStateChangeTime", 1134 [aResp](const boost::system::error_code& ec, uint64_t lastResetTime) { 1135 if (ec) 1136 { 1137 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 1138 return; 1139 } 1140 1141 // LastStateChangeTime is epoch time, in milliseconds 1142 // https://github.com/openbmc/phosphor-dbus-interfaces/blob/33e8e1dd64da53a66e888d33dc82001305cd0bf9/xyz/openbmc_project/State/Chassis.interface.yaml#L19 1143 uint64_t lastResetTimeStamp = lastResetTime / 1000; 1144 1145 // Convert to ISO 8601 standard 1146 aResp->res.jsonValue["LastResetTime"] = 1147 redfish::time_utils::getDateTimeUint(lastResetTimeStamp); 1148 }); 1149 } 1150 1151 /** 1152 * @brief Retrieves Automatic Retry properties. Known on D-Bus as AutoReboot. 1153 * 1154 * @param[in] aResp Shared pointer for generating response message. 1155 * 1156 * @return None. 1157 */ 1158 inline void getAutomaticRetry(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1159 { 1160 BMCWEB_LOG_DEBUG << "Get Automatic Retry policy"; 1161 1162 sdbusplus::asio::getProperty<bool>( 1163 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1164 "/xyz/openbmc_project/control/host0/auto_reboot", 1165 "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", 1166 [aResp](const boost::system::error_code& ec, bool autoRebootEnabled) { 1167 if (ec) 1168 { 1169 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec; 1170 return; 1171 } 1172 1173 BMCWEB_LOG_DEBUG << "Auto Reboot: " << autoRebootEnabled; 1174 if (autoRebootEnabled) 1175 { 1176 aResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = 1177 "RetryAttempts"; 1178 // If AutomaticRetry (AutoReboot) is enabled see how many 1179 // attempts are left 1180 sdbusplus::asio::getProperty<uint32_t>( 1181 *crow::connections::systemBus, "xyz.openbmc_project.State.Host", 1182 "/xyz/openbmc_project/state/host0", 1183 "xyz.openbmc_project.Control.Boot.RebootAttempts", 1184 "AttemptsLeft", 1185 [aResp](const boost::system::error_code& ec2, 1186 const uint32_t autoRebootAttemptsLeft) { 1187 if (ec2) 1188 { 1189 BMCWEB_LOG_DEBUG << "D-BUS response error " << ec2; 1190 return; 1191 } 1192 1193 BMCWEB_LOG_DEBUG << "Auto Reboot Attempts Left: " 1194 << autoRebootAttemptsLeft; 1195 1196 aResp->res 1197 .jsonValue["Boot"]["RemainingAutomaticRetryAttempts"] = 1198 autoRebootAttemptsLeft; 1199 }); 1200 } 1201 else 1202 { 1203 aResp->res.jsonValue["Boot"]["AutomaticRetryConfig"] = "Disabled"; 1204 } 1205 1206 // Not on D-Bus. Hardcoded here: 1207 // https://github.com/openbmc/phosphor-state-manager/blob/1dbbef42675e94fb1f78edb87d6b11380260535a/meson_options.txt#L71 1208 aResp->res.jsonValue["Boot"]["AutomaticRetryAttempts"] = 3; 1209 1210 // "AutomaticRetryConfig" can be 3 values, Disabled, RetryAlways, 1211 // and RetryAttempts. OpenBMC only supports Disabled and 1212 // RetryAttempts. 1213 aResp->res.jsonValue["Boot"] 1214 ["AutomaticRetryConfig@Redfish.AllowableValues"] = { 1215 "Disabled", "RetryAttempts"}; 1216 }); 1217 } 1218 1219 /** 1220 * @brief Retrieves power restore policy over DBUS. 1221 * 1222 * @param[in] aResp Shared pointer for generating response message. 1223 * 1224 * @return None. 1225 */ 1226 inline void 1227 getPowerRestorePolicy(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1228 { 1229 BMCWEB_LOG_DEBUG << "Get power restore policy"; 1230 1231 sdbusplus::asio::getProperty<std::string>( 1232 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 1233 "/xyz/openbmc_project/control/host0/power_restore_policy", 1234 "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", 1235 [aResp](const boost::system::error_code& ec, 1236 const std::string& policy) { 1237 if (ec) 1238 { 1239 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1240 return; 1241 } 1242 1243 const boost::container::flat_map<std::string, std::string> policyMaps = { 1244 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn", 1245 "AlwaysOn"}, 1246 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff", 1247 "AlwaysOff"}, 1248 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore", 1249 "LastState"}, 1250 // Return `AlwaysOff` when power restore policy set to "None" 1251 {"xyz.openbmc_project.Control.Power.RestorePolicy.Policy.None", 1252 "AlwaysOff"}}; 1253 1254 auto policyMapsIt = policyMaps.find(policy); 1255 if (policyMapsIt == policyMaps.end()) 1256 { 1257 messages::internalError(aResp->res); 1258 return; 1259 } 1260 1261 aResp->res.jsonValue["PowerRestorePolicy"] = policyMapsIt->second; 1262 }); 1263 } 1264 1265 /** 1266 * @brief Get TrustedModuleRequiredToBoot property. Determines whether or not 1267 * TPM is required for booting the host. 1268 * 1269 * @param[in] aResp Shared pointer for generating response message. 1270 * 1271 * @return None. 1272 */ 1273 inline void getTrustedModuleRequiredToBoot( 1274 const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1275 { 1276 BMCWEB_LOG_DEBUG << "Get TPM required to boot."; 1277 constexpr std::array<std::string_view, 1> interfaces = { 1278 "xyz.openbmc_project.Control.TPM.Policy"}; 1279 dbus::utility::getSubTree( 1280 "/", 0, interfaces, 1281 [aResp](const boost::system::error_code& ec, 1282 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1283 if (ec) 1284 { 1285 BMCWEB_LOG_DEBUG << "DBUS response error on TPM.Policy GetSubTree" 1286 << ec; 1287 // This is an optional D-Bus object so just return if 1288 // error occurs 1289 return; 1290 } 1291 if (subtree.empty()) 1292 { 1293 // As noted above, this is an optional interface so just return 1294 // if there is no instance found 1295 return; 1296 } 1297 1298 /* When there is more than one TPMEnable object... */ 1299 if (subtree.size() > 1) 1300 { 1301 BMCWEB_LOG_DEBUG 1302 << "DBUS response has more than 1 TPM Enable object:" 1303 << subtree.size(); 1304 // Throw an internal Error and return 1305 messages::internalError(aResp->res); 1306 return; 1307 } 1308 1309 // Make sure the Dbus response map has a service and objectPath 1310 // field 1311 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 1312 { 1313 BMCWEB_LOG_DEBUG << "TPM.Policy mapper error!"; 1314 messages::internalError(aResp->res); 1315 return; 1316 } 1317 1318 const std::string& path = subtree[0].first; 1319 const std::string& serv = subtree[0].second.begin()->first; 1320 1321 // Valid TPM Enable object found, now reading the current value 1322 sdbusplus::asio::getProperty<bool>( 1323 *crow::connections::systemBus, serv, path, 1324 "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", 1325 [aResp](const boost::system::error_code& ec2, bool tpmRequired) { 1326 if (ec2) 1327 { 1328 BMCWEB_LOG_DEBUG << "D-BUS response error on TPM.Policy Get" 1329 << ec2; 1330 messages::internalError(aResp->res); 1331 return; 1332 } 1333 1334 if (tpmRequired) 1335 { 1336 aResp->res.jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = 1337 "Required"; 1338 } 1339 else 1340 { 1341 aResp->res.jsonValue["Boot"]["TrustedModuleRequiredToBoot"] = 1342 "Disabled"; 1343 } 1344 }); 1345 }); 1346 } 1347 1348 /** 1349 * @brief Set TrustedModuleRequiredToBoot property. Determines whether or not 1350 * TPM is required for booting the host. 1351 * 1352 * @param[in] aResp Shared pointer for generating response message. 1353 * @param[in] tpmRequired Value to set TPM Required To Boot property to. 1354 * 1355 * @return None. 1356 */ 1357 inline void setTrustedModuleRequiredToBoot( 1358 const std::shared_ptr<bmcweb::AsyncResp>& aResp, const bool tpmRequired) 1359 { 1360 BMCWEB_LOG_DEBUG << "Set TrustedModuleRequiredToBoot."; 1361 constexpr std::array<std::string_view, 1> interfaces = { 1362 "xyz.openbmc_project.Control.TPM.Policy"}; 1363 dbus::utility::getSubTree( 1364 "/", 0, interfaces, 1365 [aResp, 1366 tpmRequired](const boost::system::error_code& ec, 1367 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1368 if (ec) 1369 { 1370 BMCWEB_LOG_DEBUG << "DBUS response error on TPM.Policy GetSubTree" 1371 << ec; 1372 messages::internalError(aResp->res); 1373 return; 1374 } 1375 if (subtree.empty()) 1376 { 1377 messages::propertyValueNotInList(aResp->res, "ComputerSystem", 1378 "TrustedModuleRequiredToBoot"); 1379 return; 1380 } 1381 1382 /* When there is more than one TPMEnable object... */ 1383 if (subtree.size() > 1) 1384 { 1385 BMCWEB_LOG_DEBUG 1386 << "DBUS response has more than 1 TPM Enable object:" 1387 << subtree.size(); 1388 // Throw an internal Error and return 1389 messages::internalError(aResp->res); 1390 return; 1391 } 1392 1393 // Make sure the Dbus response map has a service and objectPath 1394 // field 1395 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 1396 { 1397 BMCWEB_LOG_DEBUG << "TPM.Policy mapper error!"; 1398 messages::internalError(aResp->res); 1399 return; 1400 } 1401 1402 const std::string& path = subtree[0].first; 1403 const std::string& serv = subtree[0].second.begin()->first; 1404 1405 if (serv.empty()) 1406 { 1407 BMCWEB_LOG_DEBUG << "TPM.Policy service mapper error!"; 1408 messages::internalError(aResp->res); 1409 return; 1410 } 1411 1412 // Valid TPM Enable object found, now setting the value 1413 crow::connections::systemBus->async_method_call( 1414 [aResp](const boost::system::error_code& ec2) { 1415 if (ec2) 1416 { 1417 BMCWEB_LOG_DEBUG 1418 << "DBUS response error: Set TrustedModuleRequiredToBoot" 1419 << ec2; 1420 messages::internalError(aResp->res); 1421 return; 1422 } 1423 BMCWEB_LOG_DEBUG << "Set TrustedModuleRequiredToBoot done."; 1424 }, 1425 serv, path, "org.freedesktop.DBus.Properties", "Set", 1426 "xyz.openbmc_project.Control.TPM.Policy", "TPMEnable", 1427 dbus::utility::DbusVariantType(tpmRequired)); 1428 }); 1429 } 1430 1431 /** 1432 * @brief Sets boot properties into DBUS object(s). 1433 * 1434 * @param[in] aResp Shared pointer for generating response message. 1435 * @param[in] bootType The boot type to set. 1436 * @return Integer error code. 1437 */ 1438 inline void setBootType(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1439 const std::optional<std::string>& bootType) 1440 { 1441 std::string bootTypeStr; 1442 1443 if (!bootType) 1444 { 1445 return; 1446 } 1447 1448 // Source target specified 1449 BMCWEB_LOG_DEBUG << "Boot type: " << *bootType; 1450 // Figure out which DBUS interface and property to use 1451 if (*bootType == "Legacy") 1452 { 1453 bootTypeStr = "xyz.openbmc_project.Control.Boot.Type.Types.Legacy"; 1454 } 1455 else if (*bootType == "UEFI") 1456 { 1457 bootTypeStr = "xyz.openbmc_project.Control.Boot.Type.Types.EFI"; 1458 } 1459 else 1460 { 1461 BMCWEB_LOG_DEBUG << "Invalid property value for " 1462 "BootSourceOverrideMode: " 1463 << *bootType; 1464 messages::propertyValueNotInList(aResp->res, *bootType, 1465 "BootSourceOverrideMode"); 1466 return; 1467 } 1468 1469 // Act on validated parameters 1470 BMCWEB_LOG_DEBUG << "DBUS boot type: " << bootTypeStr; 1471 1472 crow::connections::systemBus->async_method_call( 1473 [aResp](const boost::system::error_code& ec) { 1474 if (ec) 1475 { 1476 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1477 if (ec.value() == boost::asio::error::host_unreachable) 1478 { 1479 messages::resourceNotFound(aResp->res, "Set", "BootType"); 1480 return; 1481 } 1482 messages::internalError(aResp->res); 1483 return; 1484 } 1485 BMCWEB_LOG_DEBUG << "Boot type update done."; 1486 }, 1487 "xyz.openbmc_project.Settings", 1488 "/xyz/openbmc_project/control/host0/boot", 1489 "org.freedesktop.DBus.Properties", "Set", 1490 "xyz.openbmc_project.Control.Boot.Type", "BootType", 1491 dbus::utility::DbusVariantType(bootTypeStr)); 1492 } 1493 1494 /** 1495 * @brief Sets boot properties into DBUS object(s). 1496 * 1497 * @param[in] aResp Shared pointer for generating response message. 1498 * @param[in] bootType The boot type to set. 1499 * @return Integer error code. 1500 */ 1501 inline void setBootEnable(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1502 const std::optional<std::string>& bootEnable) 1503 { 1504 if (!bootEnable) 1505 { 1506 return; 1507 } 1508 // Source target specified 1509 BMCWEB_LOG_DEBUG << "Boot enable: " << *bootEnable; 1510 1511 bool bootOverrideEnable = false; 1512 bool bootOverridePersistent = false; 1513 // Figure out which DBUS interface and property to use 1514 if (*bootEnable == "Disabled") 1515 { 1516 bootOverrideEnable = false; 1517 } 1518 else if (*bootEnable == "Once") 1519 { 1520 bootOverrideEnable = true; 1521 bootOverridePersistent = false; 1522 } 1523 else if (*bootEnable == "Continuous") 1524 { 1525 bootOverrideEnable = true; 1526 bootOverridePersistent = true; 1527 } 1528 else 1529 { 1530 BMCWEB_LOG_DEBUG 1531 << "Invalid property value for BootSourceOverrideEnabled: " 1532 << *bootEnable; 1533 messages::propertyValueNotInList(aResp->res, *bootEnable, 1534 "BootSourceOverrideEnabled"); 1535 return; 1536 } 1537 1538 // Act on validated parameters 1539 BMCWEB_LOG_DEBUG << "DBUS boot override enable: " << bootOverrideEnable; 1540 1541 crow::connections::systemBus->async_method_call( 1542 [aResp](const boost::system::error_code& ec2) { 1543 if (ec2) 1544 { 1545 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 1546 messages::internalError(aResp->res); 1547 return; 1548 } 1549 BMCWEB_LOG_DEBUG << "Boot override enable update done."; 1550 }, 1551 "xyz.openbmc_project.Settings", 1552 "/xyz/openbmc_project/control/host0/boot", 1553 "org.freedesktop.DBus.Properties", "Set", 1554 "xyz.openbmc_project.Object.Enable", "Enabled", 1555 dbus::utility::DbusVariantType(bootOverrideEnable)); 1556 1557 if (!bootOverrideEnable) 1558 { 1559 return; 1560 } 1561 1562 // In case boot override is enabled we need to set correct value for the 1563 // 'one_time' enable DBus interface 1564 BMCWEB_LOG_DEBUG << "DBUS boot override persistent: " 1565 << bootOverridePersistent; 1566 1567 crow::connections::systemBus->async_method_call( 1568 [aResp](const boost::system::error_code& ec) { 1569 if (ec) 1570 { 1571 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1572 messages::internalError(aResp->res); 1573 return; 1574 } 1575 BMCWEB_LOG_DEBUG << "Boot one_time update done."; 1576 }, 1577 "xyz.openbmc_project.Settings", 1578 "/xyz/openbmc_project/control/host0/boot/one_time", 1579 "org.freedesktop.DBus.Properties", "Set", 1580 "xyz.openbmc_project.Object.Enable", "Enabled", 1581 dbus::utility::DbusVariantType(!bootOverridePersistent)); 1582 } 1583 1584 /** 1585 * @brief Sets boot properties into DBUS object(s). 1586 * 1587 * @param[in] aResp Shared pointer for generating response message. 1588 * @param[in] bootSource The boot source to set. 1589 * 1590 * @return Integer error code. 1591 */ 1592 inline void setBootModeOrSource(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1593 const std::optional<std::string>& bootSource) 1594 { 1595 std::string bootSourceStr; 1596 std::string bootModeStr; 1597 1598 if (!bootSource) 1599 { 1600 return; 1601 } 1602 1603 // Source target specified 1604 BMCWEB_LOG_DEBUG << "Boot source: " << *bootSource; 1605 // Figure out which DBUS interface and property to use 1606 if (assignBootParameters(aResp, *bootSource, bootSourceStr, bootModeStr) != 1607 0) 1608 { 1609 BMCWEB_LOG_DEBUG 1610 << "Invalid property value for BootSourceOverrideTarget: " 1611 << *bootSource; 1612 messages::propertyValueNotInList(aResp->res, *bootSource, 1613 "BootSourceTargetOverride"); 1614 return; 1615 } 1616 1617 // Act on validated parameters 1618 BMCWEB_LOG_DEBUG << "DBUS boot source: " << bootSourceStr; 1619 BMCWEB_LOG_DEBUG << "DBUS boot mode: " << bootModeStr; 1620 1621 crow::connections::systemBus->async_method_call( 1622 [aResp](const boost::system::error_code& ec) { 1623 if (ec) 1624 { 1625 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1626 messages::internalError(aResp->res); 1627 return; 1628 } 1629 BMCWEB_LOG_DEBUG << "Boot source update done."; 1630 }, 1631 "xyz.openbmc_project.Settings", 1632 "/xyz/openbmc_project/control/host0/boot", 1633 "org.freedesktop.DBus.Properties", "Set", 1634 "xyz.openbmc_project.Control.Boot.Source", "BootSource", 1635 dbus::utility::DbusVariantType(bootSourceStr)); 1636 1637 crow::connections::systemBus->async_method_call( 1638 [aResp](const boost::system::error_code& ec) { 1639 if (ec) 1640 { 1641 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1642 messages::internalError(aResp->res); 1643 return; 1644 } 1645 BMCWEB_LOG_DEBUG << "Boot mode update done."; 1646 }, 1647 "xyz.openbmc_project.Settings", 1648 "/xyz/openbmc_project/control/host0/boot", 1649 "org.freedesktop.DBus.Properties", "Set", 1650 "xyz.openbmc_project.Control.Boot.Mode", "BootMode", 1651 dbus::utility::DbusVariantType(bootModeStr)); 1652 } 1653 1654 /** 1655 * @brief Sets Boot source override properties. 1656 * 1657 * @param[in] aResp Shared pointer for generating response message. 1658 * @param[in] bootSource The boot source from incoming RF request. 1659 * @param[in] bootType The boot type from incoming RF request. 1660 * @param[in] bootEnable The boot override enable from incoming RF request. 1661 * 1662 * @return Integer error code. 1663 */ 1664 1665 inline void setBootProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1666 const std::optional<std::string>& bootSource, 1667 const std::optional<std::string>& bootType, 1668 const std::optional<std::string>& bootEnable) 1669 { 1670 BMCWEB_LOG_DEBUG << "Set boot information."; 1671 1672 setBootModeOrSource(aResp, bootSource); 1673 setBootType(aResp, bootType); 1674 setBootEnable(aResp, bootEnable); 1675 } 1676 1677 /** 1678 * @brief Sets AssetTag 1679 * 1680 * @param[in] aResp Shared pointer for generating response message. 1681 * @param[in] assetTag "AssetTag" from request. 1682 * 1683 * @return None. 1684 */ 1685 inline void setAssetTag(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1686 const std::string& assetTag) 1687 { 1688 constexpr std::array<std::string_view, 1> interfaces = { 1689 "xyz.openbmc_project.Inventory.Item.System"}; 1690 dbus::utility::getSubTree( 1691 "/xyz/openbmc_project/inventory", 0, interfaces, 1692 [aResp, 1693 assetTag](const boost::system::error_code& ec, 1694 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1695 if (ec) 1696 { 1697 BMCWEB_LOG_DEBUG << "D-Bus response error on GetSubTree " << ec; 1698 messages::internalError(aResp->res); 1699 return; 1700 } 1701 if (subtree.empty()) 1702 { 1703 BMCWEB_LOG_DEBUG << "Can't find system D-Bus object!"; 1704 messages::internalError(aResp->res); 1705 return; 1706 } 1707 // Assume only 1 system D-Bus object 1708 // Throw an error if there is more than 1 1709 if (subtree.size() > 1) 1710 { 1711 BMCWEB_LOG_DEBUG << "Found more than 1 system D-Bus object!"; 1712 messages::internalError(aResp->res); 1713 return; 1714 } 1715 if (subtree[0].first.empty() || subtree[0].second.size() != 1) 1716 { 1717 BMCWEB_LOG_DEBUG << "Asset Tag Set mapper error!"; 1718 messages::internalError(aResp->res); 1719 return; 1720 } 1721 1722 const std::string& path = subtree[0].first; 1723 const std::string& service = subtree[0].second.begin()->first; 1724 1725 if (service.empty()) 1726 { 1727 BMCWEB_LOG_DEBUG << "Asset Tag Set service mapper error!"; 1728 messages::internalError(aResp->res); 1729 return; 1730 } 1731 1732 crow::connections::systemBus->async_method_call( 1733 [aResp](const boost::system::error_code& ec2) { 1734 if (ec2) 1735 { 1736 BMCWEB_LOG_DEBUG << "D-Bus response error on AssetTag Set " 1737 << ec2; 1738 messages::internalError(aResp->res); 1739 return; 1740 } 1741 }, 1742 service, path, "org.freedesktop.DBus.Properties", "Set", 1743 "xyz.openbmc_project.Inventory.Decorator.AssetTag", "AssetTag", 1744 dbus::utility::DbusVariantType(assetTag)); 1745 }); 1746 } 1747 1748 /** 1749 * @brief Sets automaticRetry (Auto Reboot) 1750 * 1751 * @param[in] aResp Shared pointer for generating response message. 1752 * @param[in] automaticRetryConfig "AutomaticRetryConfig" from request. 1753 * 1754 * @return None. 1755 */ 1756 inline void setAutomaticRetry(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1757 const std::string& automaticRetryConfig) 1758 { 1759 BMCWEB_LOG_DEBUG << "Set Automatic Retry."; 1760 1761 // OpenBMC only supports "Disabled" and "RetryAttempts". 1762 bool autoRebootEnabled = false; 1763 1764 if (automaticRetryConfig == "Disabled") 1765 { 1766 autoRebootEnabled = false; 1767 } 1768 else if (automaticRetryConfig == "RetryAttempts") 1769 { 1770 autoRebootEnabled = true; 1771 } 1772 else 1773 { 1774 BMCWEB_LOG_DEBUG << "Invalid property value for AutomaticRetryConfig: " 1775 << automaticRetryConfig; 1776 messages::propertyValueNotInList(aResp->res, automaticRetryConfig, 1777 "AutomaticRetryConfig"); 1778 return; 1779 } 1780 1781 crow::connections::systemBus->async_method_call( 1782 [aResp](const boost::system::error_code& ec) { 1783 if (ec) 1784 { 1785 messages::internalError(aResp->res); 1786 return; 1787 } 1788 }, 1789 "xyz.openbmc_project.Settings", 1790 "/xyz/openbmc_project/control/host0/auto_reboot", 1791 "org.freedesktop.DBus.Properties", "Set", 1792 "xyz.openbmc_project.Control.Boot.RebootPolicy", "AutoReboot", 1793 dbus::utility::DbusVariantType(autoRebootEnabled)); 1794 } 1795 1796 /** 1797 * @brief Sets power restore policy properties. 1798 * 1799 * @param[in] aResp Shared pointer for generating response message. 1800 * @param[in] policy power restore policy properties from request. 1801 * 1802 * @return None. 1803 */ 1804 inline void 1805 setPowerRestorePolicy(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1806 const std::string& policy) 1807 { 1808 BMCWEB_LOG_DEBUG << "Set power restore policy."; 1809 1810 const boost::container::flat_map<std::string, std::string> policyMaps = { 1811 {"AlwaysOn", 1812 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOn"}, 1813 {"AlwaysOff", 1814 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.AlwaysOff"}, 1815 {"LastState", 1816 "xyz.openbmc_project.Control.Power.RestorePolicy.Policy.Restore"}}; 1817 1818 std::string powerRestorPolicy; 1819 1820 auto policyMapsIt = policyMaps.find(policy); 1821 if (policyMapsIt == policyMaps.end()) 1822 { 1823 messages::propertyValueNotInList(aResp->res, policy, 1824 "PowerRestorePolicy"); 1825 return; 1826 } 1827 1828 powerRestorPolicy = policyMapsIt->second; 1829 1830 crow::connections::systemBus->async_method_call( 1831 [aResp](const boost::system::error_code& ec) { 1832 if (ec) 1833 { 1834 messages::internalError(aResp->res); 1835 return; 1836 } 1837 }, 1838 "xyz.openbmc_project.Settings", 1839 "/xyz/openbmc_project/control/host0/power_restore_policy", 1840 "org.freedesktop.DBus.Properties", "Set", 1841 "xyz.openbmc_project.Control.Power.RestorePolicy", "PowerRestorePolicy", 1842 dbus::utility::DbusVariantType(powerRestorPolicy)); 1843 } 1844 1845 #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE 1846 /** 1847 * @brief Retrieves provisioning status 1848 * 1849 * @param[in] aResp Shared pointer for completing asynchronous calls. 1850 * 1851 * @return None. 1852 */ 1853 inline void getProvisioningStatus(std::shared_ptr<bmcweb::AsyncResp> aResp) 1854 { 1855 BMCWEB_LOG_DEBUG << "Get OEM information."; 1856 sdbusplus::asio::getAllProperties( 1857 *crow::connections::systemBus, "xyz.openbmc_project.PFR.Manager", 1858 "/xyz/openbmc_project/pfr", "xyz.openbmc_project.PFR.Attributes", 1859 [aResp](const boost::system::error_code& ec, 1860 const dbus::utility::DBusPropertiesMap& propertiesList) { 1861 nlohmann::json& oemPFR = 1862 aResp->res.jsonValue["Oem"]["OpenBmc"]["FirmwareProvisioning"]; 1863 aResp->res.jsonValue["Oem"]["OpenBmc"]["@odata.type"] = 1864 "#OemComputerSystem.OpenBmc"; 1865 oemPFR["@odata.type"] = "#OemComputerSystem.FirmwareProvisioning"; 1866 1867 if (ec) 1868 { 1869 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 1870 // not an error, don't have to have the interface 1871 oemPFR["ProvisioningStatus"] = "NotProvisioned"; 1872 return; 1873 } 1874 1875 const bool* provState = nullptr; 1876 const bool* lockState = nullptr; 1877 1878 const bool success = sdbusplus::unpackPropertiesNoThrow( 1879 dbus_utils::UnpackErrorPrinter(), propertiesList, "UfmProvisioned", 1880 provState, "UfmLocked", lockState); 1881 1882 if (!success) 1883 { 1884 messages::internalError(aResp->res); 1885 return; 1886 } 1887 1888 if ((provState == nullptr) || (lockState == nullptr)) 1889 { 1890 BMCWEB_LOG_DEBUG << "Unable to get PFR attributes."; 1891 messages::internalError(aResp->res); 1892 return; 1893 } 1894 1895 if (*provState == true) 1896 { 1897 if (*lockState == true) 1898 { 1899 oemPFR["ProvisioningStatus"] = "ProvisionedAndLocked"; 1900 } 1901 else 1902 { 1903 oemPFR["ProvisioningStatus"] = "ProvisionedButNotLocked"; 1904 } 1905 } 1906 else 1907 { 1908 oemPFR["ProvisioningStatus"] = "NotProvisioned"; 1909 } 1910 }); 1911 } 1912 #endif 1913 1914 /** 1915 * @brief Translate the PowerMode to a response message. 1916 * 1917 * @param[in] aResp Shared pointer for generating response message. 1918 * @param[in] modeValue PowerMode value to be translated 1919 * 1920 * @return None. 1921 */ 1922 inline void translatePowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 1923 const std::string& modeValue) 1924 { 1925 if (modeValue == "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static") 1926 { 1927 aResp->res.jsonValue["PowerMode"] = "Static"; 1928 } 1929 else if ( 1930 modeValue == 1931 "xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance") 1932 { 1933 aResp->res.jsonValue["PowerMode"] = "MaximumPerformance"; 1934 } 1935 else if (modeValue == 1936 "xyz.openbmc_project.Control.Power.Mode.PowerMode.PowerSaving") 1937 { 1938 aResp->res.jsonValue["PowerMode"] = "PowerSaving"; 1939 } 1940 else if (modeValue == 1941 "xyz.openbmc_project.Control.Power.Mode.PowerMode.OEM") 1942 { 1943 aResp->res.jsonValue["PowerMode"] = "OEM"; 1944 } 1945 else 1946 { 1947 // Any other values would be invalid 1948 BMCWEB_LOG_DEBUG << "PowerMode value was not valid: " << modeValue; 1949 messages::internalError(aResp->res); 1950 } 1951 } 1952 1953 /** 1954 * @brief Retrieves system power mode 1955 * 1956 * @param[in] aResp Shared pointer for generating response message. 1957 * 1958 * @return None. 1959 */ 1960 inline void getPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 1961 { 1962 BMCWEB_LOG_DEBUG << "Get power mode."; 1963 1964 // Get Power Mode object path: 1965 constexpr std::array<std::string_view, 1> interfaces = { 1966 "xyz.openbmc_project.Control.Power.Mode"}; 1967 dbus::utility::getSubTree( 1968 "/", 0, interfaces, 1969 [aResp](const boost::system::error_code& ec, 1970 const dbus::utility::MapperGetSubTreeResponse& subtree) { 1971 if (ec) 1972 { 1973 BMCWEB_LOG_DEBUG << "DBUS response error on Power.Mode GetSubTree " 1974 << ec; 1975 // This is an optional D-Bus object so just return if 1976 // error occurs 1977 return; 1978 } 1979 if (subtree.empty()) 1980 { 1981 // As noted above, this is an optional interface so just return 1982 // if there is no instance found 1983 return; 1984 } 1985 if (subtree.size() > 1) 1986 { 1987 // More then one PowerMode object is not supported and is an 1988 // error 1989 BMCWEB_LOG_DEBUG 1990 << "Found more than 1 system D-Bus Power.Mode objects: " 1991 << subtree.size(); 1992 messages::internalError(aResp->res); 1993 return; 1994 } 1995 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 1996 { 1997 BMCWEB_LOG_DEBUG << "Power.Mode mapper error!"; 1998 messages::internalError(aResp->res); 1999 return; 2000 } 2001 const std::string& path = subtree[0].first; 2002 const std::string& service = subtree[0].second.begin()->first; 2003 if (service.empty()) 2004 { 2005 BMCWEB_LOG_DEBUG << "Power.Mode service mapper error!"; 2006 messages::internalError(aResp->res); 2007 return; 2008 } 2009 // Valid Power Mode object found, now read the current value 2010 sdbusplus::asio::getProperty<std::string>( 2011 *crow::connections::systemBus, service, path, 2012 "xyz.openbmc_project.Control.Power.Mode", "PowerMode", 2013 [aResp](const boost::system::error_code& ec2, 2014 const std::string& pmode) { 2015 if (ec2) 2016 { 2017 BMCWEB_LOG_DEBUG << "DBUS response error on PowerMode Get: " 2018 << ec2; 2019 messages::internalError(aResp->res); 2020 return; 2021 } 2022 2023 aResp->res.jsonValue["PowerMode@Redfish.AllowableValues"] = { 2024 "Static", "MaximumPerformance", "PowerSaving"}; 2025 2026 BMCWEB_LOG_DEBUG << "Current power mode: " << pmode; 2027 translatePowerMode(aResp, pmode); 2028 }); 2029 }); 2030 } 2031 2032 /** 2033 * @brief Validate the specified mode is valid and return the PowerMode 2034 * name associated with that string 2035 * 2036 * @param[in] aResp Shared pointer for generating response message. 2037 * @param[in] modeString String representing the desired PowerMode 2038 * 2039 * @return PowerMode value or empty string if mode is not valid 2040 */ 2041 inline std::string 2042 validatePowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2043 const std::string& modeString) 2044 { 2045 std::string mode; 2046 2047 if (modeString == "Static") 2048 { 2049 mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.Static"; 2050 } 2051 else if (modeString == "MaximumPerformance") 2052 { 2053 mode = 2054 "xyz.openbmc_project.Control.Power.Mode.PowerMode.MaximumPerformance"; 2055 } 2056 else if (modeString == "PowerSaving") 2057 { 2058 mode = "xyz.openbmc_project.Control.Power.Mode.PowerMode.PowerSaving"; 2059 } 2060 else 2061 { 2062 messages::propertyValueNotInList(aResp->res, modeString, "PowerMode"); 2063 } 2064 return mode; 2065 } 2066 2067 /** 2068 * @brief Sets system power mode. 2069 * 2070 * @param[in] aResp Shared pointer for generating response message. 2071 * @param[in] pmode System power mode from request. 2072 * 2073 * @return None. 2074 */ 2075 inline void setPowerMode(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2076 const std::string& pmode) 2077 { 2078 BMCWEB_LOG_DEBUG << "Set power mode."; 2079 2080 std::string powerMode = validatePowerMode(aResp, pmode); 2081 if (powerMode.empty()) 2082 { 2083 return; 2084 } 2085 2086 // Get Power Mode object path: 2087 constexpr std::array<std::string_view, 1> interfaces = { 2088 "xyz.openbmc_project.Control.Power.Mode"}; 2089 dbus::utility::getSubTree( 2090 "/", 0, interfaces, 2091 [aResp, 2092 powerMode](const boost::system::error_code& ec, 2093 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2094 if (ec) 2095 { 2096 BMCWEB_LOG_DEBUG << "DBUS response error on Power.Mode GetSubTree " 2097 << ec; 2098 // This is an optional D-Bus object, but user attempted to patch 2099 messages::internalError(aResp->res); 2100 return; 2101 } 2102 if (subtree.empty()) 2103 { 2104 // This is an optional D-Bus object, but user attempted to patch 2105 messages::resourceNotFound(aResp->res, "ComputerSystem", 2106 "PowerMode"); 2107 return; 2108 } 2109 if (subtree.size() > 1) 2110 { 2111 // More then one PowerMode object is not supported and is an 2112 // error 2113 BMCWEB_LOG_DEBUG 2114 << "Found more than 1 system D-Bus Power.Mode objects: " 2115 << subtree.size(); 2116 messages::internalError(aResp->res); 2117 return; 2118 } 2119 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 2120 { 2121 BMCWEB_LOG_DEBUG << "Power.Mode mapper error!"; 2122 messages::internalError(aResp->res); 2123 return; 2124 } 2125 const std::string& path = subtree[0].first; 2126 const std::string& service = subtree[0].second.begin()->first; 2127 if (service.empty()) 2128 { 2129 BMCWEB_LOG_DEBUG << "Power.Mode service mapper error!"; 2130 messages::internalError(aResp->res); 2131 return; 2132 } 2133 2134 BMCWEB_LOG_DEBUG << "Setting power mode(" << powerMode << ") -> " 2135 << path; 2136 2137 // Set the Power Mode property 2138 crow::connections::systemBus->async_method_call( 2139 [aResp](const boost::system::error_code& ec2) { 2140 if (ec2) 2141 { 2142 messages::internalError(aResp->res); 2143 return; 2144 } 2145 }, 2146 service, path, "org.freedesktop.DBus.Properties", "Set", 2147 "xyz.openbmc_project.Control.Power.Mode", "PowerMode", 2148 dbus::utility::DbusVariantType(powerMode)); 2149 }); 2150 } 2151 2152 /** 2153 * @brief Translates watchdog timeout action DBUS property value to redfish. 2154 * 2155 * @param[in] dbusAction The watchdog timeout action in D-BUS. 2156 * 2157 * @return Returns as a string, the timeout action in Redfish terms. If 2158 * translation cannot be done, returns an empty string. 2159 */ 2160 inline std::string dbusToRfWatchdogAction(const std::string& dbusAction) 2161 { 2162 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.None") 2163 { 2164 return "None"; 2165 } 2166 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.HardReset") 2167 { 2168 return "ResetSystem"; 2169 } 2170 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.PowerOff") 2171 { 2172 return "PowerDown"; 2173 } 2174 if (dbusAction == "xyz.openbmc_project.State.Watchdog.Action.PowerCycle") 2175 { 2176 return "PowerCycle"; 2177 } 2178 2179 return ""; 2180 } 2181 2182 /** 2183 *@brief Translates timeout action from Redfish to DBUS property value. 2184 * 2185 *@param[in] rfAction The timeout action in Redfish. 2186 * 2187 *@return Returns as a string, the time_out action as expected by DBUS. 2188 *If translation cannot be done, returns an empty string. 2189 */ 2190 2191 inline std::string rfToDbusWDTTimeOutAct(const std::string& rfAction) 2192 { 2193 if (rfAction == "None") 2194 { 2195 return "xyz.openbmc_project.State.Watchdog.Action.None"; 2196 } 2197 if (rfAction == "PowerCycle") 2198 { 2199 return "xyz.openbmc_project.State.Watchdog.Action.PowerCycle"; 2200 } 2201 if (rfAction == "PowerDown") 2202 { 2203 return "xyz.openbmc_project.State.Watchdog.Action.PowerOff"; 2204 } 2205 if (rfAction == "ResetSystem") 2206 { 2207 return "xyz.openbmc_project.State.Watchdog.Action.HardReset"; 2208 } 2209 2210 return ""; 2211 } 2212 2213 /** 2214 * @brief Retrieves host watchdog timer properties over DBUS 2215 * 2216 * @param[in] aResp Shared pointer for completing asynchronous calls. 2217 * 2218 * @return None. 2219 */ 2220 inline void 2221 getHostWatchdogTimer(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 2222 { 2223 BMCWEB_LOG_DEBUG << "Get host watchodg"; 2224 sdbusplus::asio::getAllProperties( 2225 *crow::connections::systemBus, "xyz.openbmc_project.Watchdog", 2226 "/xyz/openbmc_project/watchdog/host0", 2227 "xyz.openbmc_project.State.Watchdog", 2228 [aResp](const boost::system::error_code& ec, 2229 const dbus::utility::DBusPropertiesMap& properties) { 2230 if (ec) 2231 { 2232 // watchdog service is stopped 2233 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 2234 return; 2235 } 2236 2237 BMCWEB_LOG_DEBUG << "Got " << properties.size() << " wdt prop."; 2238 2239 nlohmann::json& hostWatchdogTimer = 2240 aResp->res.jsonValue["HostWatchdogTimer"]; 2241 2242 // watchdog service is running/enabled 2243 hostWatchdogTimer["Status"]["State"] = "Enabled"; 2244 2245 const bool* enabled = nullptr; 2246 const std::string* expireAction = nullptr; 2247 2248 const bool success = sdbusplus::unpackPropertiesNoThrow( 2249 dbus_utils::UnpackErrorPrinter(), properties, "Enabled", enabled, 2250 "ExpireAction", expireAction); 2251 2252 if (!success) 2253 { 2254 messages::internalError(aResp->res); 2255 return; 2256 } 2257 2258 if (enabled != nullptr) 2259 { 2260 hostWatchdogTimer["FunctionEnabled"] = *enabled; 2261 } 2262 2263 if (expireAction != nullptr) 2264 { 2265 std::string action = dbusToRfWatchdogAction(*expireAction); 2266 if (action.empty()) 2267 { 2268 messages::internalError(aResp->res); 2269 return; 2270 } 2271 hostWatchdogTimer["TimeoutAction"] = action; 2272 } 2273 }); 2274 } 2275 2276 /** 2277 * @brief Sets Host WatchDog Timer properties. 2278 * 2279 * @param[in] aResp Shared pointer for generating response message. 2280 * @param[in] wdtEnable The WDTimer Enable value (true/false) from incoming 2281 * RF request. 2282 * @param[in] wdtTimeOutAction The WDT Timeout action, from incoming RF request. 2283 * 2284 * @return None. 2285 */ 2286 inline void setWDTProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2287 const std::optional<bool> wdtEnable, 2288 const std::optional<std::string>& wdtTimeOutAction) 2289 { 2290 BMCWEB_LOG_DEBUG << "Set host watchdog"; 2291 2292 if (wdtTimeOutAction) 2293 { 2294 std::string wdtTimeOutActStr = rfToDbusWDTTimeOutAct(*wdtTimeOutAction); 2295 // check if TimeOut Action is Valid 2296 if (wdtTimeOutActStr.empty()) 2297 { 2298 BMCWEB_LOG_DEBUG << "Unsupported value for TimeoutAction: " 2299 << *wdtTimeOutAction; 2300 messages::propertyValueNotInList(aResp->res, *wdtTimeOutAction, 2301 "TimeoutAction"); 2302 return; 2303 } 2304 2305 crow::connections::systemBus->async_method_call( 2306 [aResp](const boost::system::error_code& ec) { 2307 if (ec) 2308 { 2309 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 2310 messages::internalError(aResp->res); 2311 return; 2312 } 2313 }, 2314 "xyz.openbmc_project.Watchdog", 2315 "/xyz/openbmc_project/watchdog/host0", 2316 "org.freedesktop.DBus.Properties", "Set", 2317 "xyz.openbmc_project.State.Watchdog", "ExpireAction", 2318 dbus::utility::DbusVariantType(wdtTimeOutActStr)); 2319 } 2320 2321 if (wdtEnable) 2322 { 2323 crow::connections::systemBus->async_method_call( 2324 [aResp](const boost::system::error_code& ec) { 2325 if (ec) 2326 { 2327 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 2328 messages::internalError(aResp->res); 2329 return; 2330 } 2331 }, 2332 "xyz.openbmc_project.Watchdog", 2333 "/xyz/openbmc_project/watchdog/host0", 2334 "org.freedesktop.DBus.Properties", "Set", 2335 "xyz.openbmc_project.State.Watchdog", "Enabled", 2336 dbus::utility::DbusVariantType(*wdtEnable)); 2337 } 2338 } 2339 2340 /** 2341 * @brief Parse the Idle Power Saver properties into json 2342 * 2343 * @param[in] aResp Shared pointer for completing asynchronous calls. 2344 * @param[in] properties IPS property data from DBus. 2345 * 2346 * @return true if successful 2347 */ 2348 inline bool 2349 parseIpsProperties(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2350 const dbus::utility::DBusPropertiesMap& properties) 2351 { 2352 const bool* enabled = nullptr; 2353 const uint8_t* enterUtilizationPercent = nullptr; 2354 const uint64_t* enterDwellTime = nullptr; 2355 const uint8_t* exitUtilizationPercent = nullptr; 2356 const uint64_t* exitDwellTime = nullptr; 2357 2358 const bool success = sdbusplus::unpackPropertiesNoThrow( 2359 dbus_utils::UnpackErrorPrinter(), properties, "Enabled", enabled, 2360 "EnterUtilizationPercent", enterUtilizationPercent, 2361 "ExitUtilizationPercent", exitUtilizationPercent, "ExitDwellTime", 2362 exitDwellTime); 2363 2364 if (!success) 2365 { 2366 return false; 2367 } 2368 2369 if (enabled != nullptr) 2370 { 2371 aResp->res.jsonValue["IdlePowerSaver"]["Enabled"] = *enabled; 2372 } 2373 2374 if (enterUtilizationPercent != nullptr) 2375 { 2376 aResp->res.jsonValue["IdlePowerSaver"]["EnterUtilizationPercent"] = 2377 *enterUtilizationPercent; 2378 } 2379 2380 if (enterDwellTime != nullptr) 2381 { 2382 const std::chrono::duration<uint64_t, std::milli> ms(*enterDwellTime); 2383 aResp->res.jsonValue["IdlePowerSaver"]["EnterDwellTimeSeconds"] = 2384 std::chrono::duration_cast<std::chrono::duration<uint64_t>>(ms) 2385 .count(); 2386 } 2387 2388 if (exitUtilizationPercent != nullptr) 2389 { 2390 aResp->res.jsonValue["IdlePowerSaver"]["ExitUtilizationPercent"] = 2391 *exitUtilizationPercent; 2392 } 2393 2394 if (exitDwellTime != nullptr) 2395 { 2396 const std::chrono::duration<uint64_t, std::milli> ms(*exitDwellTime); 2397 aResp->res.jsonValue["IdlePowerSaver"]["ExitDwellTimeSeconds"] = 2398 std::chrono::duration_cast<std::chrono::duration<uint64_t>>(ms) 2399 .count(); 2400 } 2401 2402 return true; 2403 } 2404 2405 /** 2406 * @brief Retrieves host watchdog timer properties over DBUS 2407 * 2408 * @param[in] aResp Shared pointer for completing asynchronous calls. 2409 * 2410 * @return None. 2411 */ 2412 inline void getIdlePowerSaver(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 2413 { 2414 BMCWEB_LOG_DEBUG << "Get idle power saver parameters"; 2415 2416 // Get IdlePowerSaver object path: 2417 constexpr std::array<std::string_view, 1> interfaces = { 2418 "xyz.openbmc_project.Control.Power.IdlePowerSaver"}; 2419 dbus::utility::getSubTree( 2420 "/", 0, interfaces, 2421 [aResp](const boost::system::error_code& ec, 2422 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2423 if (ec) 2424 { 2425 BMCWEB_LOG_DEBUG 2426 << "DBUS response error on Power.IdlePowerSaver GetSubTree " 2427 << ec; 2428 messages::internalError(aResp->res); 2429 return; 2430 } 2431 if (subtree.empty()) 2432 { 2433 // This is an optional interface so just return 2434 // if there is no instance found 2435 BMCWEB_LOG_DEBUG << "No instances found"; 2436 return; 2437 } 2438 if (subtree.size() > 1) 2439 { 2440 // More then one PowerIdlePowerSaver object is not supported and 2441 // is an error 2442 BMCWEB_LOG_DEBUG << "Found more than 1 system D-Bus " 2443 "Power.IdlePowerSaver objects: " 2444 << subtree.size(); 2445 messages::internalError(aResp->res); 2446 return; 2447 } 2448 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 2449 { 2450 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver mapper error!"; 2451 messages::internalError(aResp->res); 2452 return; 2453 } 2454 const std::string& path = subtree[0].first; 2455 const std::string& service = subtree[0].second.begin()->first; 2456 if (service.empty()) 2457 { 2458 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver service mapper error!"; 2459 messages::internalError(aResp->res); 2460 return; 2461 } 2462 2463 // Valid IdlePowerSaver object found, now read the current values 2464 sdbusplus::asio::getAllProperties( 2465 *crow::connections::systemBus, service, path, 2466 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2467 [aResp](const boost::system::error_code& ec2, 2468 const dbus::utility::DBusPropertiesMap& properties) { 2469 if (ec2) 2470 { 2471 BMCWEB_LOG_ERROR 2472 << "DBUS response error on IdlePowerSaver GetAll: " << ec2; 2473 messages::internalError(aResp->res); 2474 return; 2475 } 2476 2477 if (!parseIpsProperties(aResp, properties)) 2478 { 2479 messages::internalError(aResp->res); 2480 return; 2481 } 2482 }); 2483 }); 2484 2485 BMCWEB_LOG_DEBUG << "EXIT: Get idle power saver parameters"; 2486 } 2487 2488 /** 2489 * @brief Sets Idle Power Saver properties. 2490 * 2491 * @param[in] aResp Shared pointer for generating response message. 2492 * @param[in] ipsEnable The IPS Enable value (true/false) from incoming 2493 * RF request. 2494 * @param[in] ipsEnterUtil The utilization limit to enter idle state. 2495 * @param[in] ipsEnterTime The time the utilization must be below ipsEnterUtil 2496 * before entering idle state. 2497 * @param[in] ipsExitUtil The utilization limit when exiting idle state. 2498 * @param[in] ipsExitTime The time the utilization must be above ipsExutUtil 2499 * before exiting idle state 2500 * 2501 * @return None. 2502 */ 2503 inline void setIdlePowerSaver(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 2504 const std::optional<bool> ipsEnable, 2505 const std::optional<uint8_t> ipsEnterUtil, 2506 const std::optional<uint64_t> ipsEnterTime, 2507 const std::optional<uint8_t> ipsExitUtil, 2508 const std::optional<uint64_t> ipsExitTime) 2509 { 2510 BMCWEB_LOG_DEBUG << "Set idle power saver properties"; 2511 2512 // Get IdlePowerSaver object path: 2513 constexpr std::array<std::string_view, 1> interfaces = { 2514 "xyz.openbmc_project.Control.Power.IdlePowerSaver"}; 2515 dbus::utility::getSubTree( 2516 "/", 0, interfaces, 2517 [aResp, ipsEnable, ipsEnterUtil, ipsEnterTime, ipsExitUtil, 2518 ipsExitTime](const boost::system::error_code& ec, 2519 const dbus::utility::MapperGetSubTreeResponse& subtree) { 2520 if (ec) 2521 { 2522 BMCWEB_LOG_DEBUG 2523 << "DBUS response error on Power.IdlePowerSaver GetSubTree " 2524 << ec; 2525 messages::internalError(aResp->res); 2526 return; 2527 } 2528 if (subtree.empty()) 2529 { 2530 // This is an optional D-Bus object, but user attempted to patch 2531 messages::resourceNotFound(aResp->res, "ComputerSystem", 2532 "IdlePowerSaver"); 2533 return; 2534 } 2535 if (subtree.size() > 1) 2536 { 2537 // More then one PowerIdlePowerSaver object is not supported and 2538 // is an error 2539 BMCWEB_LOG_DEBUG 2540 << "Found more than 1 system D-Bus Power.IdlePowerSaver objects: " 2541 << subtree.size(); 2542 messages::internalError(aResp->res); 2543 return; 2544 } 2545 if ((subtree[0].first.empty()) || (subtree[0].second.size() != 1)) 2546 { 2547 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver mapper error!"; 2548 messages::internalError(aResp->res); 2549 return; 2550 } 2551 const std::string& path = subtree[0].first; 2552 const std::string& service = subtree[0].second.begin()->first; 2553 if (service.empty()) 2554 { 2555 BMCWEB_LOG_DEBUG << "Power.IdlePowerSaver service mapper error!"; 2556 messages::internalError(aResp->res); 2557 return; 2558 } 2559 2560 // Valid Power IdlePowerSaver object found, now set any values that 2561 // need to be updated 2562 2563 if (ipsEnable) 2564 { 2565 crow::connections::systemBus->async_method_call( 2566 [aResp](const boost::system::error_code& ec2) { 2567 if (ec2) 2568 { 2569 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2570 messages::internalError(aResp->res); 2571 return; 2572 } 2573 }, 2574 service, path, "org.freedesktop.DBus.Properties", "Set", 2575 "xyz.openbmc_project.Control.Power.IdlePowerSaver", "Enabled", 2576 dbus::utility::DbusVariantType(*ipsEnable)); 2577 } 2578 if (ipsEnterUtil) 2579 { 2580 crow::connections::systemBus->async_method_call( 2581 [aResp](const boost::system::error_code& ec2) { 2582 if (ec2) 2583 { 2584 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2585 messages::internalError(aResp->res); 2586 return; 2587 } 2588 }, 2589 service, path, "org.freedesktop.DBus.Properties", "Set", 2590 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2591 "EnterUtilizationPercent", 2592 dbus::utility::DbusVariantType(*ipsEnterUtil)); 2593 } 2594 if (ipsEnterTime) 2595 { 2596 // Convert from seconds into milliseconds for DBus 2597 const uint64_t timeMilliseconds = *ipsEnterTime * 1000; 2598 crow::connections::systemBus->async_method_call( 2599 [aResp](const boost::system::error_code& ec2) { 2600 if (ec2) 2601 { 2602 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2603 messages::internalError(aResp->res); 2604 return; 2605 } 2606 }, 2607 service, path, "org.freedesktop.DBus.Properties", "Set", 2608 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2609 "EnterDwellTime", 2610 dbus::utility::DbusVariantType(timeMilliseconds)); 2611 } 2612 if (ipsExitUtil) 2613 { 2614 crow::connections::systemBus->async_method_call( 2615 [aResp](const boost::system::error_code& ec2) { 2616 if (ec2) 2617 { 2618 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2619 messages::internalError(aResp->res); 2620 return; 2621 } 2622 }, 2623 service, path, "org.freedesktop.DBus.Properties", "Set", 2624 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2625 "ExitUtilizationPercent", 2626 dbus::utility::DbusVariantType(*ipsExitUtil)); 2627 } 2628 if (ipsExitTime) 2629 { 2630 // Convert from seconds into milliseconds for DBus 2631 const uint64_t timeMilliseconds = *ipsExitTime * 1000; 2632 crow::connections::systemBus->async_method_call( 2633 [aResp](const boost::system::error_code& ec2) { 2634 if (ec2) 2635 { 2636 BMCWEB_LOG_DEBUG << "DBUS response error " << ec2; 2637 messages::internalError(aResp->res); 2638 return; 2639 } 2640 }, 2641 service, path, "org.freedesktop.DBus.Properties", "Set", 2642 "xyz.openbmc_project.Control.Power.IdlePowerSaver", 2643 "ExitDwellTime", 2644 dbus::utility::DbusVariantType(timeMilliseconds)); 2645 } 2646 }); 2647 2648 BMCWEB_LOG_DEBUG << "EXIT: Set idle power saver parameters"; 2649 } 2650 2651 inline void handleComputerSystemHead( 2652 crow::App& app, const crow::Request& req, 2653 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 2654 { 2655 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2656 { 2657 return; 2658 } 2659 asyncResp->res.addHeader( 2660 boost::beast::http::field::link, 2661 "</redfish/v1/JsonSchemas/ComputerSystemCollection/ComputerSystemCollection.json>; rel=describedby"); 2662 } 2663 2664 /** 2665 * SystemsCollection derived class for delivering ComputerSystems Collection 2666 * Schema 2667 */ 2668 inline void requestRoutesSystemsCollection(App& app) 2669 { 2670 BMCWEB_ROUTE(app, "/redfish/v1/Systems/") 2671 .privileges(redfish::privileges::headComputerSystemCollection) 2672 .methods(boost::beast::http::verb::head)( 2673 std::bind_front(handleComputerSystemHead, std::ref(app))); 2674 2675 BMCWEB_ROUTE(app, "/redfish/v1/Systems/") 2676 .privileges(redfish::privileges::getComputerSystemCollection) 2677 .methods(boost::beast::http::verb::get)( 2678 [&app](const crow::Request& req, 2679 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2680 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2681 { 2682 return; 2683 } 2684 2685 asyncResp->res.addHeader( 2686 boost::beast::http::field::link, 2687 "</redfish/v1/JsonSchemas/ComputerSystemCollection.json>; rel=describedby"); 2688 asyncResp->res.jsonValue["@odata.type"] = 2689 "#ComputerSystemCollection.ComputerSystemCollection"; 2690 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems"; 2691 asyncResp->res.jsonValue["Name"] = "Computer System Collection"; 2692 2693 sdbusplus::asio::getProperty<std::string>( 2694 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 2695 "/xyz/openbmc_project/network/hypervisor", 2696 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 2697 [asyncResp](const boost::system::error_code& ec2, 2698 const std::string& /*hostName*/) { 2699 nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; 2700 ifaceArray = nlohmann::json::array(); 2701 auto& count = asyncResp->res.jsonValue["Members@odata.count"]; 2702 2703 nlohmann::json::object_t system; 2704 system["@odata.id"] = "/redfish/v1/Systems/system"; 2705 ifaceArray.push_back(std::move(system)); 2706 count = ifaceArray.size(); 2707 if (!ec2) 2708 { 2709 BMCWEB_LOG_DEBUG << "Hypervisor is available"; 2710 nlohmann::json::object_t hypervisor; 2711 hypervisor["@odata.id"] = "/redfish/v1/Systems/hypervisor"; 2712 ifaceArray.push_back(std::move(hypervisor)); 2713 count = ifaceArray.size(); 2714 } 2715 }); 2716 }); 2717 } 2718 2719 /** 2720 * Function transceives data with dbus directly. 2721 */ 2722 inline void doNMI(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 2723 { 2724 constexpr char const* serviceName = "xyz.openbmc_project.Control.Host.NMI"; 2725 constexpr char const* objectPath = "/xyz/openbmc_project/control/host0/nmi"; 2726 constexpr char const* interfaceName = 2727 "xyz.openbmc_project.Control.Host.NMI"; 2728 constexpr char const* method = "NMI"; 2729 2730 crow::connections::systemBus->async_method_call( 2731 [asyncResp](const boost::system::error_code& ec) { 2732 if (ec) 2733 { 2734 BMCWEB_LOG_ERROR << " Bad D-Bus request error: " << ec; 2735 messages::internalError(asyncResp->res); 2736 return; 2737 } 2738 messages::success(asyncResp->res); 2739 }, 2740 serviceName, objectPath, interfaceName, method); 2741 } 2742 2743 /** 2744 * SystemActionsReset class supports handle POST method for Reset action. 2745 * The class retrieves and sends data directly to D-Bus. 2746 */ 2747 inline void requestRoutesSystemActionsReset(App& app) 2748 { 2749 /** 2750 * Function handles POST method request. 2751 * Analyzes POST body message before sends Reset request data to D-Bus. 2752 */ 2753 BMCWEB_ROUTE(app, 2754 "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset/") 2755 .privileges(redfish::privileges::postComputerSystem) 2756 .methods(boost::beast::http::verb::post)( 2757 [&app](const crow::Request& req, 2758 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) { 2759 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2760 { 2761 return; 2762 } 2763 std::string resetType; 2764 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", 2765 resetType)) 2766 { 2767 return; 2768 } 2769 2770 // Get the command and host vs. chassis 2771 std::string command; 2772 bool hostCommand = true; 2773 if ((resetType == "On") || (resetType == "ForceOn")) 2774 { 2775 command = "xyz.openbmc_project.State.Host.Transition.On"; 2776 hostCommand = true; 2777 } 2778 else if (resetType == "ForceOff") 2779 { 2780 command = "xyz.openbmc_project.State.Chassis.Transition.Off"; 2781 hostCommand = false; 2782 } 2783 else if (resetType == "ForceRestart") 2784 { 2785 command = 2786 "xyz.openbmc_project.State.Host.Transition.ForceWarmReboot"; 2787 hostCommand = true; 2788 } 2789 else if (resetType == "GracefulShutdown") 2790 { 2791 command = "xyz.openbmc_project.State.Host.Transition.Off"; 2792 hostCommand = true; 2793 } 2794 else if (resetType == "GracefulRestart") 2795 { 2796 command = 2797 "xyz.openbmc_project.State.Host.Transition.GracefulWarmReboot"; 2798 hostCommand = true; 2799 } 2800 else if (resetType == "PowerCycle") 2801 { 2802 command = "xyz.openbmc_project.State.Host.Transition.Reboot"; 2803 hostCommand = true; 2804 } 2805 else if (resetType == "Nmi") 2806 { 2807 doNMI(asyncResp); 2808 return; 2809 } 2810 else 2811 { 2812 messages::actionParameterUnknown(asyncResp->res, "Reset", 2813 resetType); 2814 return; 2815 } 2816 2817 if (hostCommand) 2818 { 2819 crow::connections::systemBus->async_method_call( 2820 [asyncResp, resetType](const boost::system::error_code& ec) { 2821 if (ec) 2822 { 2823 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 2824 if (ec.value() == boost::asio::error::invalid_argument) 2825 { 2826 messages::actionParameterNotSupported( 2827 asyncResp->res, resetType, "Reset"); 2828 } 2829 else 2830 { 2831 messages::internalError(asyncResp->res); 2832 } 2833 return; 2834 } 2835 messages::success(asyncResp->res); 2836 }, 2837 "xyz.openbmc_project.State.Host", 2838 "/xyz/openbmc_project/state/host0", 2839 "org.freedesktop.DBus.Properties", "Set", 2840 "xyz.openbmc_project.State.Host", "RequestedHostTransition", 2841 dbus::utility::DbusVariantType{command}); 2842 } 2843 else 2844 { 2845 crow::connections::systemBus->async_method_call( 2846 [asyncResp, resetType](const boost::system::error_code& ec) { 2847 if (ec) 2848 { 2849 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 2850 if (ec.value() == boost::asio::error::invalid_argument) 2851 { 2852 messages::actionParameterNotSupported( 2853 asyncResp->res, resetType, "Reset"); 2854 } 2855 else 2856 { 2857 messages::internalError(asyncResp->res); 2858 } 2859 return; 2860 } 2861 messages::success(asyncResp->res); 2862 }, 2863 "xyz.openbmc_project.State.Chassis", 2864 "/xyz/openbmc_project/state/chassis0", 2865 "org.freedesktop.DBus.Properties", "Set", 2866 "xyz.openbmc_project.State.Chassis", "RequestedPowerTransition", 2867 dbus::utility::DbusVariantType{command}); 2868 } 2869 }); 2870 } 2871 2872 inline void handleComputerSystemCollectionHead( 2873 App& app, const crow::Request& req, 2874 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 2875 { 2876 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2877 { 2878 return; 2879 } 2880 2881 asyncResp->res.addHeader( 2882 boost::beast::http::field::link, 2883 "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby"); 2884 } 2885 2886 inline void afterPortRequest( 2887 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2888 const boost::system::error_code& ec, 2889 const std::vector<std::tuple<std::string, std::string, bool>>& socketData) 2890 { 2891 if (ec) 2892 { 2893 messages::internalError(asyncResp->res); 2894 return; 2895 } 2896 for (const auto& data : socketData) 2897 { 2898 const std::string& socketPath = get<0>(data); 2899 const std::string& protocolName = get<1>(data); 2900 bool isProtocolEnabled = get<2>(data); 2901 nlohmann::json& dataJson = asyncResp->res.jsonValue["SerialConsole"]; 2902 dataJson[protocolName]["ServiceEnabled"] = isProtocolEnabled; 2903 // need to retrieve port number for 2904 // obmc-console-ssh service 2905 if (protocolName == "SSH") 2906 { 2907 getPortNumber(socketPath, [asyncResp, protocolName]( 2908 const boost::system::error_code ec1, 2909 int portNumber) { 2910 if (ec1) 2911 { 2912 messages::internalError(asyncResp->res); 2913 return; 2914 } 2915 nlohmann::json& dataJson1 = 2916 asyncResp->res.jsonValue["SerialConsole"]; 2917 dataJson1[protocolName]["Port"] = portNumber; 2918 }); 2919 } 2920 } 2921 } 2922 /** 2923 * Systems derived class for delivering Computer Systems Schema. 2924 */ 2925 inline void requestRoutesSystems(App& app) 2926 { 2927 2928 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/") 2929 .privileges(redfish::privileges::headComputerSystem) 2930 .methods(boost::beast::http::verb::head)( 2931 std::bind_front(handleComputerSystemCollectionHead, std::ref(app))); 2932 /** 2933 * Functions triggers appropriate requests on DBus 2934 */ 2935 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/") 2936 .privileges(redfish::privileges::getComputerSystem) 2937 .methods(boost::beast::http::verb::get)( 2938 [&app](const crow::Request& req, 2939 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 2940 const std::string& systemName) { 2941 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 2942 { 2943 return; 2944 } 2945 if (systemName != "system") 2946 { 2947 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 2948 systemName); 2949 return; 2950 } 2951 asyncResp->res.addHeader( 2952 boost::beast::http::field::link, 2953 "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby"); 2954 asyncResp->res.jsonValue["@odata.type"] = 2955 "#ComputerSystem.v1_16_0.ComputerSystem"; 2956 asyncResp->res.jsonValue["Name"] = "system"; 2957 asyncResp->res.jsonValue["Id"] = "system"; 2958 asyncResp->res.jsonValue["SystemType"] = "Physical"; 2959 asyncResp->res.jsonValue["Description"] = "Computer System"; 2960 asyncResp->res.jsonValue["ProcessorSummary"]["Count"] = 0; 2961 asyncResp->res.jsonValue["ProcessorSummary"]["Status"]["State"] = 2962 "Disabled"; 2963 asyncResp->res.jsonValue["MemorySummary"]["TotalSystemMemoryGiB"] = 2964 uint64_t(0); 2965 asyncResp->res.jsonValue["MemorySummary"]["Status"]["State"] = 2966 "Disabled"; 2967 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems/system"; 2968 2969 asyncResp->res.jsonValue["Processors"]["@odata.id"] = 2970 "/redfish/v1/Systems/system/Processors"; 2971 asyncResp->res.jsonValue["Memory"]["@odata.id"] = 2972 "/redfish/v1/Systems/system/Memory"; 2973 asyncResp->res.jsonValue["Storage"]["@odata.id"] = 2974 "/redfish/v1/Systems/system/Storage"; 2975 asyncResp->res.jsonValue["FabricAdapters"]["@odata.id"] = 2976 "/redfish/v1/Systems/system/FabricAdapters"; 2977 2978 asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]["target"] = 2979 "/redfish/v1/Systems/system/Actions/ComputerSystem.Reset"; 2980 asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"] 2981 ["@Redfish.ActionInfo"] = 2982 "/redfish/v1/Systems/system/ResetActionInfo"; 2983 2984 asyncResp->res.jsonValue["LogServices"]["@odata.id"] = 2985 "/redfish/v1/Systems/system/LogServices"; 2986 asyncResp->res.jsonValue["Bios"]["@odata.id"] = 2987 "/redfish/v1/Systems/system/Bios"; 2988 2989 nlohmann::json::array_t managedBy; 2990 nlohmann::json& manager = managedBy.emplace_back(); 2991 manager["@odata.id"] = "/redfish/v1/Managers/bmc"; 2992 asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); 2993 asyncResp->res.jsonValue["Status"]["Health"] = "OK"; 2994 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 2995 2996 // Fill in SerialConsole info 2997 asyncResp->res.jsonValue["SerialConsole"]["MaxConcurrentSessions"] = 15; 2998 asyncResp->res.jsonValue["SerialConsole"]["IPMI"]["ServiceEnabled"] = 2999 true; 3000 3001 // TODO (Gunnar): Should look for obmc-console-ssh@2200.service 3002 asyncResp->res.jsonValue["SerialConsole"]["SSH"]["ServiceEnabled"] = 3003 true; 3004 asyncResp->res.jsonValue["SerialConsole"]["SSH"]["Port"] = 2200; 3005 asyncResp->res 3006 .jsonValue["SerialConsole"]["SSH"]["HotKeySequenceDisplay"] = 3007 "Press ~. to exit console"; 3008 getPortStatusAndPath(std::span{protocolToDBusForSystems}, 3009 std::bind_front(afterPortRequest, asyncResp)); 3010 3011 #ifdef BMCWEB_ENABLE_KVM 3012 // Fill in GraphicalConsole info 3013 asyncResp->res.jsonValue["GraphicalConsole"]["ServiceEnabled"] = true; 3014 asyncResp->res.jsonValue["GraphicalConsole"]["MaxConcurrentSessions"] = 3015 4; 3016 asyncResp->res.jsonValue["GraphicalConsole"]["ConnectTypesSupported"] = 3017 nlohmann::json::array_t({"KVMIP"}); 3018 3019 #endif // BMCWEB_ENABLE_KVM 3020 constexpr std::array<std::string_view, 4> inventoryForSystems{ 3021 "xyz.openbmc_project.Inventory.Item.Dimm", 3022 "xyz.openbmc_project.Inventory.Item.Cpu", 3023 "xyz.openbmc_project.Inventory.Item.Drive", 3024 "xyz.openbmc_project.Inventory.Item.StorageController"}; 3025 3026 auto health = std::make_shared<HealthPopulate>(asyncResp); 3027 dbus::utility::getSubTreePaths( 3028 "/", 0, inventoryForSystems, 3029 [health](const boost::system::error_code& ec, 3030 const std::vector<std::string>& resp) { 3031 if (ec) 3032 { 3033 // no inventory 3034 return; 3035 } 3036 3037 health->inventory = resp; 3038 }); 3039 3040 health->populate(); 3041 3042 getMainChassisId(asyncResp, 3043 [](const std::string& chassisId, 3044 const std::shared_ptr<bmcweb::AsyncResp>& aRsp) { 3045 nlohmann::json::array_t chassisArray; 3046 nlohmann::json& chassis = chassisArray.emplace_back(); 3047 chassis["@odata.id"] = crow::utility::urlFromPieces( 3048 "redfish", "v1", "Chassis", chassisId); 3049 aRsp->res.jsonValue["Links"]["Chassis"] = std::move(chassisArray); 3050 }); 3051 3052 getLocationIndicatorActive(asyncResp); 3053 // TODO (Gunnar): Remove IndicatorLED after enough time has passed 3054 getIndicatorLedState(asyncResp); 3055 getComputerSystem(asyncResp, health); 3056 getHostState(asyncResp); 3057 getBootProperties(asyncResp); 3058 getBootProgress(asyncResp); 3059 getBootProgressLastStateTime(asyncResp); 3060 getPCIeDeviceList(asyncResp, "PCIeDevices"); 3061 getHostWatchdogTimer(asyncResp); 3062 getPowerRestorePolicy(asyncResp); 3063 getAutomaticRetry(asyncResp); 3064 getLastResetTime(asyncResp); 3065 #ifdef BMCWEB_ENABLE_REDFISH_PROVISIONING_FEATURE 3066 getProvisioningStatus(asyncResp); 3067 #endif 3068 getTrustedModuleRequiredToBoot(asyncResp); 3069 getPowerMode(asyncResp); 3070 getIdlePowerSaver(asyncResp); 3071 }); 3072 3073 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/") 3074 .privileges(redfish::privileges::patchComputerSystem) 3075 .methods(boost::beast::http::verb::patch)( 3076 [&app](const crow::Request& req, 3077 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3078 const std::string& systemName) { 3079 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3080 { 3081 return; 3082 } 3083 if (systemName != "system") 3084 { 3085 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3086 systemName); 3087 return; 3088 } 3089 3090 asyncResp->res.addHeader( 3091 boost::beast::http::field::link, 3092 "</redfish/v1/JsonSchemas/ComputerSystem/ComputerSystem.json>; rel=describedby"); 3093 3094 std::optional<bool> locationIndicatorActive; 3095 std::optional<std::string> indicatorLed; 3096 std::optional<std::string> assetTag; 3097 std::optional<std::string> powerRestorePolicy; 3098 std::optional<std::string> powerMode; 3099 std::optional<bool> wdtEnable; 3100 std::optional<std::string> wdtTimeOutAction; 3101 std::optional<std::string> bootSource; 3102 std::optional<std::string> bootType; 3103 std::optional<std::string> bootEnable; 3104 std::optional<std::string> bootAutomaticRetry; 3105 std::optional<bool> bootTrustedModuleRequired; 3106 std::optional<bool> ipsEnable; 3107 std::optional<uint8_t> ipsEnterUtil; 3108 std::optional<uint64_t> ipsEnterTime; 3109 std::optional<uint8_t> ipsExitUtil; 3110 std::optional<uint64_t> ipsExitTime; 3111 3112 // clang-format off 3113 if (!json_util::readJsonPatch( 3114 req, asyncResp->res, 3115 "IndicatorLED", indicatorLed, 3116 "LocationIndicatorActive", locationIndicatorActive, 3117 "AssetTag", assetTag, 3118 "PowerRestorePolicy", powerRestorePolicy, 3119 "PowerMode", powerMode, 3120 "HostWatchdogTimer/FunctionEnabled", wdtEnable, 3121 "HostWatchdogTimer/TimeoutAction", wdtTimeOutAction, 3122 "Boot/BootSourceOverrideTarget", bootSource, 3123 "Boot/BootSourceOverrideMode", bootType, 3124 "Boot/BootSourceOverrideEnabled", bootEnable, 3125 "Boot/AutomaticRetryConfig", bootAutomaticRetry, 3126 "Boot/TrustedModuleRequiredToBoot", bootTrustedModuleRequired, 3127 "IdlePowerSaver/Enabled", ipsEnable, 3128 "IdlePowerSaver/EnterUtilizationPercent", ipsEnterUtil, 3129 "IdlePowerSaver/EnterDwellTimeSeconds", ipsEnterTime, 3130 "IdlePowerSaver/ExitUtilizationPercent", ipsExitUtil, 3131 "IdlePowerSaver/ExitDwellTimeSeconds", ipsExitTime)) 3132 { 3133 return; 3134 } 3135 // clang-format on 3136 3137 asyncResp->res.result(boost::beast::http::status::no_content); 3138 3139 if (assetTag) 3140 { 3141 setAssetTag(asyncResp, *assetTag); 3142 } 3143 3144 if (wdtEnable || wdtTimeOutAction) 3145 { 3146 setWDTProperties(asyncResp, wdtEnable, wdtTimeOutAction); 3147 } 3148 3149 if (bootSource || bootType || bootEnable) 3150 { 3151 setBootProperties(asyncResp, bootSource, bootType, bootEnable); 3152 } 3153 if (bootAutomaticRetry) 3154 { 3155 setAutomaticRetry(asyncResp, *bootAutomaticRetry); 3156 } 3157 3158 if (bootTrustedModuleRequired) 3159 { 3160 setTrustedModuleRequiredToBoot(asyncResp, 3161 *bootTrustedModuleRequired); 3162 } 3163 3164 if (locationIndicatorActive) 3165 { 3166 setLocationIndicatorActive(asyncResp, *locationIndicatorActive); 3167 } 3168 3169 // TODO (Gunnar): Remove IndicatorLED after enough time has 3170 // passed 3171 if (indicatorLed) 3172 { 3173 setIndicatorLedState(asyncResp, *indicatorLed); 3174 asyncResp->res.addHeader(boost::beast::http::field::warning, 3175 "299 - \"IndicatorLED is deprecated. Use " 3176 "LocationIndicatorActive instead.\""); 3177 } 3178 3179 if (powerRestorePolicy) 3180 { 3181 setPowerRestorePolicy(asyncResp, *powerRestorePolicy); 3182 } 3183 3184 if (powerMode) 3185 { 3186 setPowerMode(asyncResp, *powerMode); 3187 } 3188 3189 if (ipsEnable || ipsEnterUtil || ipsEnterTime || ipsExitUtil || 3190 ipsExitTime) 3191 { 3192 setIdlePowerSaver(asyncResp, ipsEnable, ipsEnterUtil, ipsEnterTime, 3193 ipsExitUtil, ipsExitTime); 3194 } 3195 }); 3196 } 3197 3198 inline void handleSystemCollectionResetActionHead( 3199 crow::App& app, const crow::Request& req, 3200 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 3201 { 3202 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3203 { 3204 return; 3205 } 3206 asyncResp->res.addHeader( 3207 boost::beast::http::field::link, 3208 "</redfish/v1/JsonSchemas/ActionInfo/ActionInfo.json>; rel=describedby"); 3209 } 3210 3211 /** 3212 * SystemResetActionInfo derived class for delivering Computer Systems 3213 * ResetType AllowableValues using ResetInfo schema. 3214 */ 3215 inline void requestRoutesSystemResetActionInfo(App& app) 3216 { 3217 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/ResetActionInfo/") 3218 .privileges(redfish::privileges::headActionInfo) 3219 .methods(boost::beast::http::verb::head)(std::bind_front( 3220 handleSystemCollectionResetActionHead, std::ref(app))); 3221 /** 3222 * Functions triggers appropriate requests on DBus 3223 */ 3224 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/ResetActionInfo/") 3225 .privileges(redfish::privileges::getActionInfo) 3226 .methods(boost::beast::http::verb::get)( 3227 [&app](const crow::Request& req, 3228 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 3229 const std::string& systemName) { 3230 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 3231 { 3232 return; 3233 } 3234 if (systemName != "system") 3235 { 3236 messages::resourceNotFound(asyncResp->res, "ComputerSystem", 3237 systemName); 3238 return; 3239 } 3240 3241 asyncResp->res.addHeader( 3242 boost::beast::http::field::link, 3243 "</redfish/v1/JsonSchemas/ActionInfo/ActionInfo.json>; rel=describedby"); 3244 3245 asyncResp->res.jsonValue["@odata.id"] = 3246 "/redfish/v1/Systems/system/ResetActionInfo"; 3247 asyncResp->res.jsonValue["@odata.type"] = 3248 "#ActionInfo.v1_1_2.ActionInfo"; 3249 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 3250 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 3251 3252 nlohmann::json::array_t parameters; 3253 nlohmann::json::object_t parameter; 3254 3255 parameter["Name"] = "ResetType"; 3256 parameter["Required"] = true; 3257 parameter["DataType"] = "String"; 3258 nlohmann::json::array_t allowableValues; 3259 allowableValues.emplace_back("On"); 3260 allowableValues.emplace_back("ForceOff"); 3261 allowableValues.emplace_back("ForceOn"); 3262 allowableValues.emplace_back("ForceRestart"); 3263 allowableValues.emplace_back("GracefulRestart"); 3264 allowableValues.emplace_back("GracefulShutdown"); 3265 allowableValues.emplace_back("PowerCycle"); 3266 allowableValues.emplace_back("Nmi"); 3267 parameter["AllowableValues"] = std::move(allowableValues); 3268 parameters.emplace_back(std::move(parameter)); 3269 3270 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 3271 }); 3272 } 3273 } // namespace redfish 3274