1 #pragma once 2 3 #include "app.hpp" 4 #include "dbus_singleton.hpp" 5 #include "dbus_utility.hpp" 6 #include "error_messages.hpp" 7 #include "ethernet.hpp" 8 #include "query.hpp" 9 #include "registries/privilege_registry.hpp" 10 #include "utils/ip_utils.hpp" 11 #include "utils/json_utils.hpp" 12 13 #include <boost/url/format.hpp> 14 #include <sdbusplus/asio/property.hpp> 15 16 #include <array> 17 #include <optional> 18 #include <string_view> 19 #include <utility> 20 21 namespace redfish 22 { 23 24 /** 25 * @brief Retrieves hypervisor state properties over dbus 26 * 27 * The hypervisor state object is optional so this function will only set the 28 * state variables if the object is found 29 * 30 * @param[in] asyncResp Shared pointer for completing asynchronous calls. 31 * 32 * @return None. 33 */ 34 inline void 35 getHypervisorState(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 36 { 37 BMCWEB_LOG_DEBUG("Get hypervisor state information."); 38 sdbusplus::asio::getProperty<std::string>( 39 *crow::connections::systemBus, "xyz.openbmc_project.State.Hypervisor", 40 "/xyz/openbmc_project/state/hypervisor0", 41 "xyz.openbmc_project.State.Host", "CurrentHostState", 42 [asyncResp](const boost::system::error_code& ec, 43 const std::string& hostState) { 44 if (ec) 45 { 46 BMCWEB_LOG_DEBUG("DBUS response error {}", ec); 47 // This is an optional D-Bus object so just return if 48 // error occurs 49 return; 50 } 51 52 BMCWEB_LOG_DEBUG("Hypervisor state: {}", hostState); 53 // Verify Host State 54 if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") 55 { 56 asyncResp->res.jsonValue["PowerState"] = "On"; 57 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 58 } 59 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 60 "Quiesced") 61 { 62 asyncResp->res.jsonValue["PowerState"] = "On"; 63 asyncResp->res.jsonValue["Status"]["State"] = "Quiesced"; 64 } 65 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 66 "Standby") 67 { 68 asyncResp->res.jsonValue["PowerState"] = "On"; 69 asyncResp->res.jsonValue["Status"]["State"] = "StandbyOffline"; 70 } 71 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 72 "TransitioningToRunning") 73 { 74 asyncResp->res.jsonValue["PowerState"] = "PoweringOn"; 75 asyncResp->res.jsonValue["Status"]["State"] = "Starting"; 76 } 77 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 78 "TransitioningToOff") 79 { 80 asyncResp->res.jsonValue["PowerState"] = "PoweringOff"; 81 asyncResp->res.jsonValue["Status"]["State"] = "Enabled"; 82 } 83 else if (hostState == "xyz.openbmc_project.State.Host.HostState.Off") 84 { 85 asyncResp->res.jsonValue["PowerState"] = "Off"; 86 asyncResp->res.jsonValue["Status"]["State"] = "Disabled"; 87 } 88 else 89 { 90 messages::internalError(asyncResp->res); 91 return; 92 } 93 }); 94 } 95 96 /** 97 * @brief Populate Actions if any are valid for hypervisor object 98 * 99 * The hypervisor state object is optional so this function will only set the 100 * Action if the object is found 101 * 102 * @param[in] asyncResp Shared pointer for completing asynchronous calls. 103 * 104 * @return None. 105 */ 106 inline void 107 getHypervisorActions(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 108 { 109 BMCWEB_LOG_DEBUG("Get hypervisor actions."); 110 constexpr std::array<std::string_view, 1> interfaces = { 111 "xyz.openbmc_project.State.Host"}; 112 dbus::utility::getDbusObject( 113 "/xyz/openbmc_project/state/hypervisor0", interfaces, 114 [asyncResp]( 115 const boost::system::error_code& ec, 116 const std::vector<std::pair<std::string, std::vector<std::string>>>& 117 objInfo) { 118 if (ec) 119 { 120 BMCWEB_LOG_DEBUG("DBUS response error {}", ec); 121 // This is an optional D-Bus object so just return if 122 // error occurs 123 return; 124 } 125 126 if (objInfo.empty()) 127 { 128 // As noted above, this is an optional interface so just return 129 // if there is no instance found 130 return; 131 } 132 133 if (objInfo.size() > 1) 134 { 135 // More then one hypervisor object is not supported and is an 136 // error 137 messages::internalError(asyncResp->res); 138 return; 139 } 140 141 // Object present so system support limited ComputerSystem Action 142 nlohmann::json& reset = 143 asyncResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]; 144 reset["target"] = 145 "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset"; 146 reset["@Redfish.ActionInfo"] = 147 "/redfish/v1/Systems/hypervisor/ResetActionInfo"; 148 }); 149 } 150 151 inline bool extractHypervisorInterfaceData( 152 const std::string& ethIfaceId, 153 const dbus::utility::ManagedObjectType& dbusData, 154 EthernetInterfaceData& ethData, std::vector<IPv4AddressData>& ipv4Config) 155 { 156 bool idFound = false; 157 for (const auto& objpath : dbusData) 158 { 159 for (const auto& ifacePair : objpath.second) 160 { 161 if (objpath.first == 162 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId) 163 { 164 idFound = true; 165 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress") 166 { 167 for (const auto& propertyPair : ifacePair.second) 168 { 169 if (propertyPair.first == "MACAddress") 170 { 171 const std::string* mac = 172 std::get_if<std::string>(&propertyPair.second); 173 if (mac != nullptr) 174 { 175 ethData.macAddress = *mac; 176 } 177 } 178 } 179 } 180 else if (ifacePair.first == 181 "xyz.openbmc_project.Network.EthernetInterface") 182 { 183 for (const auto& propertyPair : ifacePair.second) 184 { 185 if (propertyPair.first == "DHCPEnabled") 186 { 187 const std::string* dhcp = 188 std::get_if<std::string>(&propertyPair.second); 189 if (dhcp != nullptr) 190 { 191 ethData.dhcpEnabled = *dhcp; 192 break; // Interested on only "DHCPEnabled". 193 // Stop parsing since we got the 194 // "DHCPEnabled" value. 195 } 196 } 197 } 198 } 199 } 200 if (objpath.first == "/xyz/openbmc_project/network/hypervisor/" + 201 ethIfaceId + "/ipv4/addr0") 202 { 203 IPv4AddressData& ipv4Address = ipv4Config.emplace_back(); 204 if (ifacePair.first == "xyz.openbmc_project.Object.Enable") 205 { 206 for (const auto& property : ifacePair.second) 207 { 208 if (property.first == "Enabled") 209 { 210 const bool* intfEnable = 211 std::get_if<bool>(&property.second); 212 if (intfEnable != nullptr) 213 { 214 ipv4Address.isActive = *intfEnable; 215 break; 216 } 217 } 218 } 219 } 220 if (ifacePair.first == "xyz.openbmc_project.Network.IP") 221 { 222 for (const auto& property : ifacePair.second) 223 { 224 if (property.first == "Address") 225 { 226 const std::string* address = 227 std::get_if<std::string>(&property.second); 228 if (address != nullptr) 229 { 230 ipv4Address.address = *address; 231 } 232 } 233 else if (property.first == "Origin") 234 { 235 const std::string* origin = 236 std::get_if<std::string>(&property.second); 237 if (origin != nullptr) 238 { 239 ipv4Address.origin = 240 translateAddressOriginDbusToRedfish(*origin, 241 true); 242 } 243 } 244 else if (property.first == "PrefixLength") 245 { 246 const uint8_t* mask = 247 std::get_if<uint8_t>(&property.second); 248 if (mask != nullptr) 249 { 250 // convert it to the string 251 ipv4Address.netmask = getNetmask(*mask); 252 } 253 } 254 else if (property.first == "Type" || 255 property.first == "Gateway") 256 { 257 // Type & Gateway is not used 258 continue; 259 } 260 else 261 { 262 BMCWEB_LOG_ERROR( 263 "Got extra property: {} on the {} object", 264 property.first, objpath.first.str); 265 } 266 } 267 } 268 } 269 if (objpath.first == "/xyz/openbmc_project/network/hypervisor") 270 { 271 // System configuration shows up in the global namespace, so no 272 // need to check eth number 273 if (ifacePair.first == 274 "xyz.openbmc_project.Network.SystemConfiguration") 275 { 276 for (const auto& propertyPair : ifacePair.second) 277 { 278 if (propertyPair.first == "HostName") 279 { 280 const std::string* hostName = 281 std::get_if<std::string>(&propertyPair.second); 282 if (hostName != nullptr) 283 { 284 ethData.hostName = *hostName; 285 } 286 } 287 else if (propertyPair.first == "DefaultGateway") 288 { 289 const std::string* defaultGateway = 290 std::get_if<std::string>(&propertyPair.second); 291 if (defaultGateway != nullptr) 292 { 293 ethData.defaultGateway = *defaultGateway; 294 } 295 } 296 } 297 } 298 } 299 } 300 } 301 return idFound; 302 } 303 /** 304 * Function that retrieves all properties for given Hypervisor Ethernet 305 * Interface Object from Settings Manager 306 * @param ethIfaceId Hypervisor ethernet interface id to query on DBus 307 * @param callback a function that shall be called to convert Dbus output 308 * into JSON 309 */ 310 template <typename CallbackFunc> 311 void getHypervisorIfaceData(const std::string& ethIfaceId, 312 CallbackFunc&& callback) 313 { 314 sdbusplus::message::object_path path("/"); 315 dbus::utility::getManagedObjects( 316 "xyz.openbmc_project.Settings", path, 317 [ethIfaceId{std::string{ethIfaceId}}, 318 callback{std::forward<CallbackFunc>(callback)}]( 319 const boost::system::error_code& ec, 320 const dbus::utility::ManagedObjectType& resp) { 321 EthernetInterfaceData ethData{}; 322 std::vector<IPv4AddressData> ipv4Data; 323 if (ec) 324 { 325 callback(false, ethData, ipv4Data); 326 return; 327 } 328 329 bool found = extractHypervisorInterfaceData(ethIfaceId, resp, ethData, 330 ipv4Data); 331 if (!found) 332 { 333 BMCWEB_LOG_INFO("Hypervisor Interface not found"); 334 } 335 callback(found, ethData, ipv4Data); 336 }); 337 } 338 339 /** 340 * @brief Sets the Hypervisor Interface IPAddress DBUS 341 * 342 * @param[in] asyncResp Shared pointer for generating response message. 343 * @param[in] ipv4Address Address from the incoming request 344 * @param[in] ethIfaceId Hypervisor Interface Id 345 * 346 * @return None. 347 */ 348 inline void setHypervisorIPv4Address( 349 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 350 const std::string& ethIfaceId, const std::string& ipv4Address) 351 { 352 BMCWEB_LOG_DEBUG("Setting the Hypervisor IPaddress : {} on Iface: {}", 353 ipv4Address, ethIfaceId); 354 sdbusplus::asio::setProperty( 355 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 356 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 357 "xyz.openbmc_project.Network.IP", "Address", ipv4Address, 358 [asyncResp](const boost::system::error_code& ec) { 359 if (ec) 360 { 361 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 362 return; 363 } 364 BMCWEB_LOG_DEBUG("Hypervisor IPaddress is Set"); 365 }); 366 } 367 368 /** 369 * @brief Sets the Hypervisor Interface SubnetMask DBUS 370 * 371 * @param[in] asyncResp Shared pointer for generating response message. 372 * @param[in] subnet SubnetMask from the incoming request 373 * @param[in] ethIfaceId Hypervisor Interface Id 374 * 375 * @return None. 376 */ 377 inline void 378 setHypervisorIPv4Subnet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 379 const std::string& ethIfaceId, const uint8_t subnet) 380 { 381 BMCWEB_LOG_DEBUG("Setting the Hypervisor subnet : {} on Iface: {}", subnet, 382 ethIfaceId); 383 384 sdbusplus::asio::setProperty( 385 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 386 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 387 "xyz.openbmc_project.Network.IP", "PrefixLength", subnet, 388 [asyncResp](const boost::system::error_code& ec) { 389 if (ec) 390 { 391 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 392 return; 393 } 394 BMCWEB_LOG_DEBUG("SubnetMask is Set"); 395 }); 396 } 397 398 /** 399 * @brief Sets the Hypervisor Interface Gateway DBUS 400 * 401 * @param[in] asyncResp Shared pointer for generating response message. 402 * @param[in] gateway Gateway from the incoming request 403 * @param[in] ethIfaceId Hypervisor Interface Id 404 * 405 * @return None. 406 */ 407 inline void setHypervisorIPv4Gateway( 408 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 409 const std::string& gateway) 410 { 411 BMCWEB_LOG_DEBUG( 412 "Setting the DefaultGateway to the last configured gateway"); 413 414 sdbusplus::asio::setProperty( 415 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 416 "/xyz/openbmc_project/network/hypervisor", 417 "xyz.openbmc_project.Network.SystemConfiguration", "DefaultGateway", 418 gateway, [asyncResp](const boost::system::error_code& ec) { 419 if (ec) 420 { 421 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 422 return; 423 } 424 BMCWEB_LOG_DEBUG("Default Gateway is Set"); 425 }); 426 } 427 428 /** 429 * @brief Creates a static IPv4 entry 430 * 431 * @param[in] ifaceId Id of interface upon which to create the IPv4 entry 432 * @param[in] prefixLength IPv4 prefix syntax for the subnet mask 433 * @param[in] gateway IPv4 address of this interfaces gateway 434 * @param[in] address IPv4 address to assign to this interface 435 * @param[io] asyncResp Response object that will be returned to client 436 * 437 * @return None 438 */ 439 inline void 440 createHypervisorIPv4(const std::string& ifaceId, uint8_t prefixLength, 441 const std::string& gateway, const std::string& address, 442 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 443 { 444 setHypervisorIPv4Address(asyncResp, ifaceId, address); 445 setHypervisorIPv4Gateway(asyncResp, gateway); 446 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 447 } 448 449 /** 450 * @brief Deletes given IPv4 interface 451 * 452 * @param[in] ifaceId Id of interface whose IP should be deleted 453 * @param[io] asyncResp Response object that will be returned to client 454 * 455 * @return None 456 */ 457 inline void 458 deleteHypervisorIPv4(const std::string& ifaceId, 459 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 460 { 461 std::string address = "0.0.0.0"; 462 std::string gateway = "0.0.0.0"; 463 const uint8_t prefixLength = 0; 464 setHypervisorIPv4Address(asyncResp, ifaceId, address); 465 setHypervisorIPv4Gateway(asyncResp, gateway); 466 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 467 } 468 469 inline void parseInterfaceData(nlohmann::json& jsonResponse, 470 const std::string& ifaceId, 471 const EthernetInterfaceData& ethData, 472 const std::vector<IPv4AddressData>& ipv4Data) 473 { 474 jsonResponse["Id"] = ifaceId; 475 jsonResponse["@odata.id"] = boost::urls::format( 476 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/{}", ifaceId); 477 jsonResponse["InterfaceEnabled"] = true; 478 jsonResponse["MACAddress"] = ethData.macAddress; 479 480 jsonResponse["HostName"] = ethData.hostName; 481 jsonResponse["DHCPv4"]["DHCPEnabled"] = 482 translateDhcpEnabledToBool(ethData.dhcpEnabled, true); 483 484 nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"]; 485 nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"]; 486 ipv4Array = nlohmann::json::array(); 487 ipv4StaticArray = nlohmann::json::array(); 488 for (const auto& ipv4Config : ipv4Data) 489 { 490 if (ipv4Config.isActive) 491 { 492 nlohmann::json::object_t ipv4; 493 ipv4["AddressOrigin"] = ipv4Config.origin; 494 ipv4["SubnetMask"] = ipv4Config.netmask; 495 ipv4["Address"] = ipv4Config.address; 496 ipv4["Gateway"] = ethData.defaultGateway; 497 498 if (ipv4Config.origin == "Static") 499 { 500 ipv4StaticArray.push_back(ipv4); 501 } 502 ipv4Array.emplace_back(std::move(ipv4)); 503 } 504 } 505 } 506 507 inline void setDHCPEnabled(const std::string& ifaceId, bool ipv4DHCPEnabled, 508 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 509 { 510 const std::string dhcp = getDhcpEnabledEnumeration(ipv4DHCPEnabled, false); 511 sdbusplus::asio::setProperty( 512 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 513 "/xyz/openbmc_project/network/hypervisor/" + ifaceId, 514 "xyz.openbmc_project.Network.EthernetInterface", "DHCPEnabled", dhcp, 515 [asyncResp](const boost::system::error_code& ec) { 516 if (ec) 517 { 518 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); 519 messages::internalError(asyncResp->res); 520 return; 521 } 522 }); 523 524 // Set the IPv4 address origin to the DHCP / Static as per the new value 525 // of the DHCPEnabled property 526 std::string origin; 527 if (!ipv4DHCPEnabled) 528 { 529 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 530 } 531 else 532 { 533 // DHCPEnabled is set to true. Delete the current IPv4 settings 534 // to receive the new values from DHCP server. 535 deleteHypervisorIPv4(ifaceId, asyncResp); 536 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 537 } 538 sdbusplus::asio::setProperty( 539 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 540 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", 541 "xyz.openbmc_project.Network.IP", "Origin", origin, 542 [asyncResp](const boost::system::error_code& ec) { 543 if (ec) 544 { 545 BMCWEB_LOG_ERROR("DBUS response error {}", ec); 546 messages::internalError(asyncResp->res); 547 return; 548 } 549 BMCWEB_LOG_DEBUG("Hypervisor IPaddress Origin is Set"); 550 }); 551 } 552 553 inline void handleHypervisorIPv4StaticPatch( 554 const std::string& ifaceId, const nlohmann::json& input, 555 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 556 { 557 if ((!input.is_array()) || input.empty()) 558 { 559 messages::propertyValueTypeError(asyncResp->res, input, 560 "IPv4StaticAddresses"); 561 return; 562 } 563 564 // Hypervisor considers the first IP address in the array list 565 // as the Hypervisor's virtual management interface supports single IPv4 566 // address 567 const nlohmann::json& thisJson = input[0]; 568 569 if (!thisJson.is_null() && !thisJson.empty()) 570 { 571 // For the error string 572 std::string pathString = "IPv4StaticAddresses/1"; 573 std::optional<std::string> address; 574 std::optional<std::string> subnetMask; 575 std::optional<std::string> gateway; 576 nlohmann::json thisJsonCopy = thisJson; 577 if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address", 578 address, "SubnetMask", subnetMask, "Gateway", 579 gateway)) 580 { 581 messages::propertyValueFormatError(asyncResp->res, thisJson, 582 pathString); 583 return; 584 } 585 586 uint8_t prefixLength = 0; 587 bool errorInEntry = false; 588 if (address) 589 { 590 if (!ip_util::ipv4VerifyIpAndGetBitcount(*address)) 591 { 592 messages::propertyValueFormatError(asyncResp->res, *address, 593 pathString + "/Address"); 594 errorInEntry = true; 595 } 596 } 597 else 598 { 599 messages::propertyMissing(asyncResp->res, pathString + "/Address"); 600 errorInEntry = true; 601 } 602 603 if (subnetMask) 604 { 605 if (!ip_util::ipv4VerifyIpAndGetBitcount(*subnetMask, 606 &prefixLength)) 607 { 608 messages::propertyValueFormatError(asyncResp->res, *subnetMask, 609 pathString + "/SubnetMask"); 610 errorInEntry = true; 611 } 612 } 613 else 614 { 615 messages::propertyMissing(asyncResp->res, 616 pathString + "/SubnetMask"); 617 errorInEntry = true; 618 } 619 620 if (gateway) 621 { 622 if (!ip_util::ipv4VerifyIpAndGetBitcount(*gateway)) 623 { 624 messages::propertyValueFormatError(asyncResp->res, *gateway, 625 pathString + "/Gateway"); 626 errorInEntry = true; 627 } 628 } 629 else 630 { 631 messages::propertyMissing(asyncResp->res, pathString + "/Gateway"); 632 errorInEntry = true; 633 } 634 635 if (errorInEntry) 636 { 637 return; 638 } 639 640 BMCWEB_LOG_DEBUG("Calling createHypervisorIPv4 on : {},{}", ifaceId, 641 *address); 642 createHypervisorIPv4(ifaceId, prefixLength, *gateway, *address, 643 asyncResp); 644 // Set the DHCPEnabled to false since the Static IPv4 is set 645 setDHCPEnabled(ifaceId, false, asyncResp); 646 } 647 else 648 { 649 if (thisJson.is_null()) 650 { 651 deleteHypervisorIPv4(ifaceId, asyncResp); 652 } 653 } 654 } 655 656 inline void handleHypervisorHostnamePatch( 657 const std::string& hostName, 658 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 659 { 660 if (!isHostnameValid(hostName)) 661 { 662 messages::propertyValueFormatError(asyncResp->res, hostName, 663 "HostName"); 664 return; 665 } 666 667 asyncResp->res.jsonValue["HostName"] = hostName; 668 sdbusplus::asio::setProperty( 669 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 670 "/xyz/openbmc_project/network/hypervisor/", 671 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", hostName, 672 [asyncResp](const boost::system::error_code& ec) { 673 if (ec) 674 { 675 messages::internalError(asyncResp->res); 676 } 677 }); 678 } 679 680 inline void 681 setIPv4InterfaceEnabled(const std::string& ifaceId, bool isActive, 682 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 683 { 684 sdbusplus::asio::setProperty( 685 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 686 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", 687 "xyz.openbmc_project.Object.Enable", "Enabled", isActive, 688 [asyncResp](const boost::system::error_code& ec) { 689 if (ec) 690 { 691 messages::internalError(asyncResp->res); 692 } 693 }); 694 } 695 696 inline void handleHypervisorEthernetInterfaceCollectionGet( 697 App& app, const crow::Request& req, 698 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 699 { 700 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 701 { 702 return; 703 } 704 constexpr std::array<std::string_view, 1> interfaces = { 705 "xyz.openbmc_project.Network.EthernetInterface"}; 706 707 dbus::utility::getSubTreePaths( 708 "/xyz/openbmc_project/network/hypervisor", 0, interfaces, 709 [asyncResp]( 710 const boost::system::error_code& ec, 711 const dbus::utility::MapperGetSubTreePathsResponse& ifaceList) { 712 if (ec) 713 { 714 messages::resourceNotFound(asyncResp->res, "System", "hypervisor"); 715 return; 716 } 717 asyncResp->res.jsonValue["@odata.type"] = 718 "#EthernetInterfaceCollection." 719 "EthernetInterfaceCollection"; 720 asyncResp->res.jsonValue["@odata.id"] = 721 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 722 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet " 723 "Interface Collection"; 724 asyncResp->res.jsonValue["Description"] = 725 "Collection of Virtual Management " 726 "Interfaces for the hypervisor"; 727 728 nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; 729 ifaceArray = nlohmann::json::array(); 730 for (const std::string& iface : ifaceList) 731 { 732 sdbusplus::message::object_path path(iface); 733 std::string name = path.filename(); 734 if (name.empty()) 735 { 736 continue; 737 } 738 nlohmann::json::object_t ethIface; 739 ethIface["@odata.id"] = boost::urls::format( 740 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/{}", name); 741 ifaceArray.emplace_back(std::move(ethIface)); 742 } 743 asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size(); 744 }); 745 } 746 747 inline void handleHypervisorEthernetInterfaceGet( 748 App& app, const crow::Request& req, 749 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 750 { 751 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 752 { 753 return; 754 } 755 getHypervisorIfaceData( 756 id, [asyncResp, ifaceId{std::string(id)}]( 757 bool success, const EthernetInterfaceData& ethData, 758 const std::vector<IPv4AddressData>& ipv4Data) { 759 if (!success) 760 { 761 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 762 ifaceId); 763 return; 764 } 765 asyncResp->res.jsonValue["@odata.type"] = 766 "#EthernetInterface.v1_9_0.EthernetInterface"; 767 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet Interface"; 768 asyncResp->res.jsonValue["Description"] = 769 "Hypervisor's Virtual Management Ethernet Interface"; 770 parseInterfaceData(asyncResp->res.jsonValue, ifaceId, ethData, 771 ipv4Data); 772 }); 773 } 774 775 inline void handleHypervisorSystemGet( 776 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 777 { 778 sdbusplus::asio::getProperty<std::string>( 779 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 780 "/xyz/openbmc_project/network/hypervisor", 781 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 782 [asyncResp](const boost::system::error_code& ec, 783 const std::string& /*hostName*/) { 784 if (ec) 785 { 786 messages::resourceNotFound(asyncResp->res, "System", "hypervisor"); 787 return; 788 } 789 BMCWEB_LOG_DEBUG("Hypervisor is available"); 790 791 asyncResp->res.jsonValue["@odata.type"] = 792 "#ComputerSystem.v1_6_0.ComputerSystem"; 793 asyncResp->res.jsonValue["@odata.id"] = 794 "/redfish/v1/Systems/hypervisor"; 795 asyncResp->res.jsonValue["Description"] = "Hypervisor"; 796 asyncResp->res.jsonValue["Name"] = "Hypervisor"; 797 asyncResp->res.jsonValue["Id"] = "hypervisor"; 798 asyncResp->res.jsonValue["SystemType"] = "OS"; 799 nlohmann::json::array_t managedBy; 800 nlohmann::json::object_t manager; 801 manager["@odata.id"] = "/redfish/v1/Managers/bmc"; 802 managedBy.emplace_back(std::move(manager)); 803 asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); 804 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = 805 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 806 getHypervisorState(asyncResp); 807 getHypervisorActions(asyncResp); 808 // TODO: Add "SystemType" : "hypervisor" 809 }); 810 } 811 812 inline void handleHypervisorEthernetInterfacePatch( 813 App& app, const crow::Request& req, 814 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 815 const std::string& ifaceId) 816 { 817 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 818 { 819 return; 820 } 821 std::optional<std::string> hostName; 822 std::optional<std::vector<nlohmann::json>> ipv4StaticAddresses; 823 std::optional<nlohmann::json> ipv4Addresses; 824 std::optional<nlohmann::json> dhcpv4; 825 std::optional<bool> ipv4DHCPEnabled; 826 827 if (!json_util::readJsonPatch(req, asyncResp->res, "HostName", hostName, 828 "IPv4StaticAddresses", ipv4StaticAddresses, 829 "IPv4Addresses", ipv4Addresses, "DHCPv4", 830 dhcpv4)) 831 { 832 return; 833 } 834 835 if (ipv4Addresses) 836 { 837 messages::propertyNotWritable(asyncResp->res, "IPv4Addresses"); 838 return; 839 } 840 841 if (dhcpv4) 842 { 843 if (!json_util::readJson(*dhcpv4, asyncResp->res, "DHCPEnabled", 844 ipv4DHCPEnabled)) 845 { 846 return; 847 } 848 } 849 850 getHypervisorIfaceData( 851 ifaceId, 852 [asyncResp, ifaceId, hostName = std::move(hostName), 853 ipv4StaticAddresses = std::move(ipv4StaticAddresses), ipv4DHCPEnabled, 854 dhcpv4 = std::move(dhcpv4)](bool success, 855 const EthernetInterfaceData& ethData, 856 const std::vector<IPv4AddressData>&) { 857 if (!success) 858 { 859 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 860 ifaceId); 861 return; 862 } 863 864 if (ipv4StaticAddresses) 865 { 866 const nlohmann::json& ipv4Static = *ipv4StaticAddresses; 867 if (ipv4Static.begin() == ipv4Static.end()) 868 { 869 messages::propertyValueTypeError(asyncResp->res, ipv4Static, 870 "IPv4StaticAddresses"); 871 return; 872 } 873 874 // One and only one hypervisor instance supported 875 if (ipv4Static.size() != 1) 876 { 877 messages::propertyValueFormatError(asyncResp->res, ipv4Static, 878 "IPv4StaticAddresses"); 879 return; 880 } 881 882 const nlohmann::json& ipv4Json = ipv4Static[0]; 883 // Check if the param is 'null'. If its null, it means 884 // that user wants to delete the IP address. Deleting 885 // the IP address is allowed only if its statically 886 // configured. Deleting the address originated from DHCP 887 // is not allowed. 888 if ((ipv4Json.is_null()) && 889 (translateDhcpEnabledToBool(ethData.dhcpEnabled, true))) 890 { 891 BMCWEB_LOG_INFO("Ignoring the delete on ipv4StaticAddresses " 892 "as the interface is DHCP enabled"); 893 } 894 else 895 { 896 handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, asyncResp); 897 } 898 } 899 900 if (hostName) 901 { 902 handleHypervisorHostnamePatch(*hostName, asyncResp); 903 } 904 905 if (dhcpv4) 906 { 907 setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); 908 } 909 910 // Set this interface to disabled/inactive. This will be set 911 // to enabled/active by the pldm once the hypervisor 912 // consumes the updated settings from the user. 913 setIPv4InterfaceEnabled(ifaceId, false, asyncResp); 914 }); 915 asyncResp->res.result(boost::beast::http::status::accepted); 916 } 917 918 inline void handleHypervisorResetActionGet( 919 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 920 { 921 // Only return action info if hypervisor D-Bus object present 922 constexpr std::array<std::string_view, 1> interfaces = { 923 "xyz.openbmc_project.State.Host"}; 924 dbus::utility::getDbusObject( 925 "/xyz/openbmc_project/state/hypervisor0", interfaces, 926 [asyncResp]( 927 const boost::system::error_code& ec, 928 const std::vector<std::pair<std::string, std::vector<std::string>>>& 929 objInfo) { 930 if (ec) 931 { 932 BMCWEB_LOG_DEBUG("DBUS response error {}", ec); 933 934 // No hypervisor objects found by mapper 935 if (ec.value() == boost::system::errc::io_error) 936 { 937 messages::resourceNotFound(asyncResp->res, "hypervisor", 938 "ResetActionInfo"); 939 return; 940 } 941 942 messages::internalError(asyncResp->res); 943 return; 944 } 945 946 // One and only one hypervisor instance supported 947 if (objInfo.size() != 1) 948 { 949 messages::internalError(asyncResp->res); 950 return; 951 } 952 953 // The hypervisor object only support the ability to 954 // turn On The system object Action should be utilized 955 // for other operations 956 957 asyncResp->res.jsonValue["@odata.type"] = 958 "#ActionInfo.v1_1_2.ActionInfo"; 959 asyncResp->res.jsonValue["@odata.id"] = 960 "/redfish/v1/Systems/hypervisor/ResetActionInfo"; 961 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 962 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 963 nlohmann::json::array_t parameters; 964 nlohmann::json::object_t parameter; 965 parameter["Name"] = "ResetType"; 966 parameter["Required"] = true; 967 parameter["DataType"] = "String"; 968 nlohmann::json::array_t allowed; 969 allowed.emplace_back("On"); 970 parameter["AllowableValues"] = std::move(allowed); 971 parameters.emplace_back(std::move(parameter)); 972 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 973 }); 974 } 975 976 inline void handleHypervisorSystemResetPost( 977 App& app, const crow::Request& req, 978 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 979 { 980 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 981 { 982 return; 983 } 984 std::optional<std::string> resetType; 985 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType)) 986 { 987 // readJson adds appropriate error to response 988 return; 989 } 990 991 if (!resetType) 992 { 993 messages::actionParameterMissing(asyncResp->res, "ComputerSystem.Reset", 994 "ResetType"); 995 return; 996 } 997 998 // Hypervisor object only support On operation 999 if (resetType != "On") 1000 { 1001 messages::propertyValueNotInList(asyncResp->res, *resetType, 1002 "ResetType"); 1003 return; 1004 } 1005 1006 std::string command = "xyz.openbmc_project.State.Host.Transition.On"; 1007 1008 sdbusplus::asio::setProperty( 1009 *crow::connections::systemBus, "xyz.openbmc_project.State.Hypervisor", 1010 "/xyz/openbmc_project/state/hypervisor0", 1011 "xyz.openbmc_project.State.Host", "RequestedHostTransition", command, 1012 [asyncResp, resetType](const boost::system::error_code& ec) { 1013 if (ec) 1014 { 1015 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); 1016 if (ec.value() == boost::asio::error::invalid_argument) 1017 { 1018 messages::actionParameterNotSupported(asyncResp->res, 1019 *resetType, "Reset"); 1020 return; 1021 } 1022 1023 if (ec.value() == boost::asio::error::host_unreachable) 1024 { 1025 messages::resourceNotFound(asyncResp->res, "Actions", "Reset"); 1026 return; 1027 } 1028 1029 messages::internalError(asyncResp->res); 1030 return; 1031 } 1032 messages::success(asyncResp->res); 1033 }); 1034 } 1035 1036 inline void requestRoutesHypervisorSystems(App& app) 1037 { 1038 /** 1039 * HypervisorInterfaceCollection class to handle the GET and PATCH on 1040 * Hypervisor Interface 1041 */ 1042 1043 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/") 1044 .privileges(redfish::privileges::getEthernetInterfaceCollection) 1045 .methods(boost::beast::http::verb::get)(std::bind_front( 1046 handleHypervisorEthernetInterfaceCollectionGet, std::ref(app))); 1047 1048 BMCWEB_ROUTE(app, 1049 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 1050 .privileges(redfish::privileges::getEthernetInterface) 1051 .methods(boost::beast::http::verb::get)(std::bind_front( 1052 handleHypervisorEthernetInterfaceGet, std::ref(app))); 1053 1054 BMCWEB_ROUTE(app, 1055 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 1056 .privileges(redfish::privileges::patchEthernetInterface) 1057 .methods(boost::beast::http::verb::patch)(std::bind_front( 1058 handleHypervisorEthernetInterfacePatch, std::ref(app))); 1059 1060 BMCWEB_ROUTE(app, 1061 "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset/") 1062 .privileges(redfish::privileges::postComputerSystem) 1063 .methods(boost::beast::http::verb::post)( 1064 std::bind_front(handleHypervisorSystemResetPost, std::ref(app))); 1065 } 1066 } // namespace redfish 1067