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