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