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) mutable { 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 355 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings", 356 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + 357 "/ipv4/addr0", 358 "xyz.openbmc_project.Network.IP", "Address", 359 "IPv4StaticAddresses/1/Address", ipv4Address); 360 } 361 362 /** 363 * @brief Sets the Hypervisor Interface SubnetMask DBUS 364 * 365 * @param[in] asyncResp Shared pointer for generating response message. 366 * @param[in] subnet SubnetMask from the incoming request 367 * @param[in] ethIfaceId Hypervisor Interface Id 368 * 369 * @return None. 370 */ 371 inline void 372 setHypervisorIPv4Subnet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 373 const std::string& ethIfaceId, const uint8_t subnet) 374 { 375 BMCWEB_LOG_DEBUG("Setting the Hypervisor subnet : {} on Iface: {}", subnet, 376 ethIfaceId); 377 378 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings", 379 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + 380 "/ipv4/addr0", 381 "xyz.openbmc_project.Network.IP", "PrefixLength", 382 "IPv4StaticAddresses/1/SubnetMask", subnet); 383 } 384 385 /** 386 * @brief Sets the Hypervisor Interface Gateway DBUS 387 * 388 * @param[in] asyncResp Shared pointer for generating response message. 389 * @param[in] gateway Gateway from the incoming request 390 * @param[in] ethIfaceId Hypervisor Interface Id 391 * 392 * @return None. 393 */ 394 inline void setHypervisorIPv4Gateway( 395 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 396 const std::string& gateway) 397 { 398 BMCWEB_LOG_DEBUG( 399 "Setting the DefaultGateway to the last configured gateway"); 400 401 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings", 402 sdbusplus::message::object_path( 403 "/xyz/openbmc_project/network/hypervisor"), 404 "xyz.openbmc_project.Network.SystemConfiguration", 405 "DefaultGateway", "IPv4StaticAddresses/1/Gateway", gateway); 406 } 407 408 /** 409 * @brief Creates a static IPv4 entry 410 * 411 * @param[in] ifaceId Id of interface upon which to create the IPv4 entry 412 * @param[in] prefixLength IPv4 prefix syntax for the subnet mask 413 * @param[in] gateway IPv4 address of this interfaces gateway 414 * @param[in] address IPv4 address to assign to this interface 415 * @param[io] asyncResp Response object that will be returned to client 416 * 417 * @return None 418 */ 419 inline void 420 createHypervisorIPv4(const std::string& ifaceId, uint8_t prefixLength, 421 const std::string& gateway, const std::string& address, 422 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 423 { 424 setHypervisorIPv4Address(asyncResp, ifaceId, address); 425 setHypervisorIPv4Gateway(asyncResp, gateway); 426 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 427 } 428 429 /** 430 * @brief Deletes given IPv4 interface 431 * 432 * @param[in] ifaceId Id of interface whose IP should be deleted 433 * @param[io] asyncResp Response object that will be returned to client 434 * 435 * @return None 436 */ 437 inline void 438 deleteHypervisorIPv4(const std::string& ifaceId, 439 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 440 { 441 std::string address = "0.0.0.0"; 442 std::string gateway = "0.0.0.0"; 443 const uint8_t prefixLength = 0; 444 setHypervisorIPv4Address(asyncResp, ifaceId, address); 445 setHypervisorIPv4Gateway(asyncResp, gateway); 446 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 447 } 448 449 inline void parseInterfaceData(nlohmann::json& jsonResponse, 450 const std::string& ifaceId, 451 const EthernetInterfaceData& ethData, 452 const std::vector<IPv4AddressData>& ipv4Data) 453 { 454 jsonResponse["Id"] = ifaceId; 455 jsonResponse["@odata.id"] = boost::urls::format( 456 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/{}", ifaceId); 457 jsonResponse["InterfaceEnabled"] = true; 458 jsonResponse["MACAddress"] = ethData.macAddress; 459 460 jsonResponse["HostName"] = ethData.hostName; 461 jsonResponse["DHCPv4"]["DHCPEnabled"] = 462 translateDhcpEnabledToBool(ethData.dhcpEnabled, true); 463 464 nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"]; 465 nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"]; 466 ipv4Array = nlohmann::json::array(); 467 ipv4StaticArray = nlohmann::json::array(); 468 for (const auto& ipv4Config : ipv4Data) 469 { 470 if (ipv4Config.isActive) 471 { 472 nlohmann::json::object_t ipv4; 473 ipv4["AddressOrigin"] = ipv4Config.origin; 474 ipv4["SubnetMask"] = ipv4Config.netmask; 475 ipv4["Address"] = ipv4Config.address; 476 ipv4["Gateway"] = ethData.defaultGateway; 477 478 if (ipv4Config.origin == "Static") 479 { 480 ipv4StaticArray.push_back(ipv4); 481 } 482 ipv4Array.emplace_back(std::move(ipv4)); 483 } 484 } 485 } 486 487 inline void setDHCPEnabled(const std::string& ifaceId, bool ipv4DHCPEnabled, 488 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 489 { 490 const std::string dhcp = getDhcpEnabledEnumeration(ipv4DHCPEnabled, false); 491 492 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings", 493 sdbusplus::message::object_path( 494 "/xyz/openbmc_project/network/hypervisor") / 495 ifaceId, 496 "xyz.openbmc_project.Network.EthernetInterface", 497 "DHCPEnabled", "DHCPv4/DHCPEnabled", dhcp); 498 499 // Set the IPv4 address origin to the DHCP / Static as per the new value 500 // of the DHCPEnabled property 501 std::string origin; 502 if (!ipv4DHCPEnabled) 503 { 504 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 505 } 506 else 507 { 508 // DHCPEnabled is set to true. Delete the current IPv4 settings 509 // to receive the new values from DHCP server. 510 deleteHypervisorIPv4(ifaceId, asyncResp); 511 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 512 } 513 514 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings", 515 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + 516 "/ipv4/addr0", 517 "xyz.openbmc_project.Network.IP", "Origin", 518 "IPv4StaticAddresses/1/AddressOrigin", origin); 519 } 520 521 inline void handleHypervisorIPv4StaticPatch( 522 const std::string& ifaceId, 523 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>& input, 524 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 525 { 526 // Hypervisor considers the first IP address in the array list 527 // as the Hypervisor's virtual management interface supports single IPv4 528 // address 529 std::variant<nlohmann::json::object_t, std::nullptr_t>& thisJson = input[0]; 530 nlohmann::json::object_t* obj = 531 std::get_if<nlohmann::json::object_t>(&thisJson); 532 if (obj == nullptr) 533 { 534 deleteHypervisorIPv4(ifaceId, asyncResp); 535 return; 536 } 537 if (obj->empty()) 538 { 539 return; 540 } 541 // For the error string 542 std::string pathString = "IPv4StaticAddresses/1"; 543 std::string address; 544 std::string subnetMask; 545 std::string gateway; 546 if (!json_util::readJsonObject(*obj, asyncResp->res, "Address", address, 547 "SubnetMask", subnetMask, "Gateway", 548 gateway)) 549 { 550 return; 551 } 552 553 uint8_t prefixLength = 0; 554 if (!ip_util::ipv4VerifyIpAndGetBitcount(address)) 555 { 556 messages::propertyValueFormatError(asyncResp->res, address, 557 pathString + "/Address"); 558 return; 559 } 560 561 if (!ip_util::ipv4VerifyIpAndGetBitcount(subnetMask, &prefixLength)) 562 { 563 messages::propertyValueFormatError(asyncResp->res, subnetMask, 564 pathString + "/SubnetMask"); 565 return; 566 } 567 568 if (!ip_util::ipv4VerifyIpAndGetBitcount(gateway)) 569 { 570 messages::propertyValueFormatError(asyncResp->res, gateway, 571 pathString + "/Gateway"); 572 return; 573 } 574 575 BMCWEB_LOG_DEBUG("Calling createHypervisorIPv4 on : {},{}", ifaceId, 576 address); 577 createHypervisorIPv4(ifaceId, prefixLength, gateway, address, asyncResp); 578 // Set the DHCPEnabled to false since the Static IPv4 is set 579 setDHCPEnabled(ifaceId, false, asyncResp); 580 } 581 582 inline void handleHypervisorHostnamePatch( 583 const std::string& hostName, 584 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 585 { 586 if (!isHostnameValid(hostName)) 587 { 588 messages::propertyValueFormatError(asyncResp->res, hostName, 589 "HostName"); 590 return; 591 } 592 593 asyncResp->res.jsonValue["HostName"] = hostName; 594 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings", 595 sdbusplus::message::object_path( 596 "/xyz/openbmc_project/network/hypervisor"), 597 "xyz.openbmc_project.Network.SystemConfiguration", 598 "HostName", "HostName", hostName); 599 } 600 601 inline void 602 setIPv4InterfaceEnabled(const std::string& ifaceId, bool isActive, 603 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 604 { 605 setDbusProperty(asyncResp, "xyz.openbmc_project.Settings", 606 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + 607 "/ipv4/addr0", 608 "xyz.openbmc_project.Object.Enable", "Enabled", 609 "InterfaceEnabled", isActive); 610 } 611 612 inline void handleHypervisorEthernetInterfaceCollectionGet( 613 App& app, const crow::Request& req, 614 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 615 { 616 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 617 { 618 return; 619 } 620 constexpr std::array<std::string_view, 1> interfaces = { 621 "xyz.openbmc_project.Network.EthernetInterface"}; 622 623 dbus::utility::getSubTreePaths( 624 "/xyz/openbmc_project/network/hypervisor", 0, interfaces, 625 [asyncResp]( 626 const boost::system::error_code& ec, 627 const dbus::utility::MapperGetSubTreePathsResponse& ifaceList) { 628 if (ec) 629 { 630 messages::resourceNotFound(asyncResp->res, "System", "hypervisor"); 631 return; 632 } 633 asyncResp->res.jsonValue["@odata.type"] = 634 "#EthernetInterfaceCollection." 635 "EthernetInterfaceCollection"; 636 asyncResp->res.jsonValue["@odata.id"] = 637 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 638 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet " 639 "Interface Collection"; 640 asyncResp->res.jsonValue["Description"] = 641 "Collection of Virtual Management " 642 "Interfaces for the hypervisor"; 643 644 nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; 645 ifaceArray = nlohmann::json::array(); 646 for (const std::string& iface : ifaceList) 647 { 648 sdbusplus::message::object_path path(iface); 649 std::string name = path.filename(); 650 if (name.empty()) 651 { 652 continue; 653 } 654 nlohmann::json::object_t ethIface; 655 ethIface["@odata.id"] = boost::urls::format( 656 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/{}", name); 657 ifaceArray.emplace_back(std::move(ethIface)); 658 } 659 asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size(); 660 }); 661 } 662 663 inline void handleHypervisorEthernetInterfaceGet( 664 App& app, const crow::Request& req, 665 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 666 { 667 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 668 { 669 return; 670 } 671 getHypervisorIfaceData( 672 id, [asyncResp, ifaceId{std::string(id)}]( 673 bool success, const EthernetInterfaceData& ethData, 674 const std::vector<IPv4AddressData>& ipv4Data) { 675 if (!success) 676 { 677 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 678 ifaceId); 679 return; 680 } 681 asyncResp->res.jsonValue["@odata.type"] = 682 "#EthernetInterface.v1_9_0.EthernetInterface"; 683 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet Interface"; 684 asyncResp->res.jsonValue["Description"] = 685 "Hypervisor's Virtual Management Ethernet Interface"; 686 parseInterfaceData(asyncResp->res.jsonValue, ifaceId, ethData, 687 ipv4Data); 688 }); 689 } 690 691 inline void handleHypervisorSystemGet( 692 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 693 { 694 sdbusplus::asio::getProperty<std::string>( 695 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 696 "/xyz/openbmc_project/network/hypervisor", 697 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 698 [asyncResp](const boost::system::error_code& ec, 699 const std::string& /*hostName*/) { 700 if (ec) 701 { 702 messages::resourceNotFound(asyncResp->res, "System", "hypervisor"); 703 return; 704 } 705 BMCWEB_LOG_DEBUG("Hypervisor is available"); 706 707 asyncResp->res.jsonValue["@odata.type"] = 708 "#ComputerSystem.v1_6_0.ComputerSystem"; 709 asyncResp->res.jsonValue["@odata.id"] = 710 "/redfish/v1/Systems/hypervisor"; 711 asyncResp->res.jsonValue["Description"] = "Hypervisor"; 712 asyncResp->res.jsonValue["Name"] = "Hypervisor"; 713 asyncResp->res.jsonValue["Id"] = "hypervisor"; 714 asyncResp->res.jsonValue["SystemType"] = "OS"; 715 nlohmann::json::array_t managedBy; 716 nlohmann::json::object_t manager; 717 manager["@odata.id"] = "/redfish/v1/Managers/bmc"; 718 managedBy.emplace_back(std::move(manager)); 719 asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); 720 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = 721 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 722 getHypervisorState(asyncResp); 723 getHypervisorActions(asyncResp); 724 // TODO: Add "SystemType" : "hypervisor" 725 }); 726 } 727 728 inline void handleHypervisorEthernetInterfacePatch( 729 App& app, const crow::Request& req, 730 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 731 const std::string& ifaceId) 732 { 733 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 734 { 735 return; 736 } 737 std::optional<std::string> hostName; 738 std::optional< 739 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>> 740 ipv4StaticAddresses; 741 std::optional<std::vector<nlohmann::json::object_t>> ipv4Addresses; 742 std::optional<bool> ipv4DHCPEnabled; 743 744 if (!json_util::readJsonPatch(req, asyncResp->res, "HostName", hostName, 745 "IPv4StaticAddresses", ipv4StaticAddresses, 746 "IPv4Addresses", ipv4Addresses, 747 "DHCPv4/DHCPEnabled", ipv4DHCPEnabled)) 748 { 749 return; 750 } 751 752 if (ipv4Addresses) 753 { 754 messages::propertyNotWritable(asyncResp->res, "IPv4Addresses"); 755 return; 756 } 757 758 getHypervisorIfaceData( 759 ifaceId, 760 [asyncResp, ifaceId, hostName = std::move(hostName), 761 ipv4StaticAddresses = std::move(ipv4StaticAddresses), 762 ipv4DHCPEnabled](bool success, const EthernetInterfaceData& ethData, 763 const std::vector<IPv4AddressData>&) mutable { 764 if (!success) 765 { 766 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 767 ifaceId); 768 return; 769 } 770 771 if (ipv4StaticAddresses) 772 { 773 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>& 774 ipv4Static = *ipv4StaticAddresses; 775 if (ipv4Static.begin() == ipv4Static.end()) 776 { 777 messages::propertyValueTypeError(asyncResp->res, 778 std::vector<std::string>(), 779 "IPv4StaticAddresses"); 780 return; 781 } 782 783 // One and only one hypervisor instance supported 784 if (ipv4Static.size() != 1) 785 { 786 messages::propertyValueFormatError(asyncResp->res, "[]", 787 "IPv4StaticAddresses"); 788 return; 789 } 790 791 std::variant<nlohmann::json::object_t, std::nullptr_t>& ipv4Json = 792 ipv4Static[0]; 793 // Check if the param is 'null'. If its null, it means 794 // that user wants to delete the IP address. Deleting 795 // the IP address is allowed only if its statically 796 // configured. Deleting the address originated from DHCP 797 // is not allowed. 798 if (std::holds_alternative<std::nullptr_t>(ipv4Json) && 799 translateDhcpEnabledToBool(ethData.dhcpEnabled, true)) 800 { 801 BMCWEB_LOG_INFO("Ignoring the delete on ipv4StaticAddresses " 802 "as the interface is DHCP enabled"); 803 } 804 else 805 { 806 handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, asyncResp); 807 } 808 } 809 810 if (hostName) 811 { 812 handleHypervisorHostnamePatch(*hostName, asyncResp); 813 } 814 815 if (ipv4DHCPEnabled) 816 { 817 setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); 818 } 819 820 // Set this interface to disabled/inactive. This will be set 821 // to enabled/active by the pldm once the hypervisor 822 // consumes the updated settings from the user. 823 setIPv4InterfaceEnabled(ifaceId, false, asyncResp); 824 }); 825 asyncResp->res.result(boost::beast::http::status::accepted); 826 } 827 828 inline void handleHypervisorResetActionGet( 829 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 830 { 831 // Only return action info if hypervisor D-Bus object present 832 constexpr std::array<std::string_view, 1> interfaces = { 833 "xyz.openbmc_project.State.Host"}; 834 dbus::utility::getDbusObject( 835 "/xyz/openbmc_project/state/hypervisor0", interfaces, 836 [asyncResp]( 837 const boost::system::error_code& ec, 838 const std::vector<std::pair<std::string, std::vector<std::string>>>& 839 objInfo) { 840 if (ec) 841 { 842 BMCWEB_LOG_DEBUG("DBUS response error {}", ec); 843 844 // No hypervisor objects found by mapper 845 if (ec.value() == boost::system::errc::io_error) 846 { 847 messages::resourceNotFound(asyncResp->res, "hypervisor", 848 "ResetActionInfo"); 849 return; 850 } 851 852 messages::internalError(asyncResp->res); 853 return; 854 } 855 856 // One and only one hypervisor instance supported 857 if (objInfo.size() != 1) 858 { 859 messages::internalError(asyncResp->res); 860 return; 861 } 862 863 // The hypervisor object only support the ability to 864 // turn On The system object Action should be utilized 865 // for other operations 866 867 asyncResp->res.jsonValue["@odata.type"] = 868 "#ActionInfo.v1_1_2.ActionInfo"; 869 asyncResp->res.jsonValue["@odata.id"] = 870 "/redfish/v1/Systems/hypervisor/ResetActionInfo"; 871 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 872 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 873 nlohmann::json::array_t parameters; 874 nlohmann::json::object_t parameter; 875 parameter["Name"] = "ResetType"; 876 parameter["Required"] = true; 877 parameter["DataType"] = "String"; 878 nlohmann::json::array_t allowed; 879 allowed.emplace_back("On"); 880 parameter["AllowableValues"] = std::move(allowed); 881 parameters.emplace_back(std::move(parameter)); 882 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 883 }); 884 } 885 886 inline void handleHypervisorSystemResetPost( 887 App& app, const crow::Request& req, 888 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 889 { 890 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 891 { 892 return; 893 } 894 std::optional<std::string> resetType; 895 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType)) 896 { 897 // readJson adds appropriate error to response 898 return; 899 } 900 901 if (!resetType) 902 { 903 messages::actionParameterMissing(asyncResp->res, "ComputerSystem.Reset", 904 "ResetType"); 905 return; 906 } 907 908 // Hypervisor object only support On operation 909 if (resetType != "On") 910 { 911 messages::propertyValueNotInList(asyncResp->res, *resetType, 912 "ResetType"); 913 return; 914 } 915 916 std::string command = "xyz.openbmc_project.State.Host.Transition.On"; 917 918 sdbusplus::asio::setProperty( 919 *crow::connections::systemBus, "xyz.openbmc_project.State.Hypervisor", 920 "/xyz/openbmc_project/state/hypervisor0", 921 "xyz.openbmc_project.State.Host", "RequestedHostTransition", command, 922 [asyncResp, resetType](const boost::system::error_code& ec) { 923 if (ec) 924 { 925 BMCWEB_LOG_ERROR("D-Bus responses error: {}", ec); 926 if (ec.value() == boost::asio::error::invalid_argument) 927 { 928 messages::actionParameterNotSupported(asyncResp->res, 929 *resetType, "Reset"); 930 return; 931 } 932 933 if (ec.value() == boost::asio::error::host_unreachable) 934 { 935 messages::resourceNotFound(asyncResp->res, "Actions", "Reset"); 936 return; 937 } 938 939 messages::internalError(asyncResp->res); 940 return; 941 } 942 messages::success(asyncResp->res); 943 }); 944 } 945 946 inline void requestRoutesHypervisorSystems(App& app) 947 { 948 /** 949 * HypervisorInterfaceCollection class to handle the GET and PATCH on 950 * Hypervisor Interface 951 */ 952 953 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/") 954 .privileges(redfish::privileges::getEthernetInterfaceCollection) 955 .methods(boost::beast::http::verb::get)(std::bind_front( 956 handleHypervisorEthernetInterfaceCollectionGet, std::ref(app))); 957 958 BMCWEB_ROUTE(app, 959 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 960 .privileges(redfish::privileges::getEthernetInterface) 961 .methods(boost::beast::http::verb::get)(std::bind_front( 962 handleHypervisorEthernetInterfaceGet, std::ref(app))); 963 964 BMCWEB_ROUTE(app, 965 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 966 .privileges(redfish::privileges::patchEthernetInterface) 967 .methods(boost::beast::http::verb::patch)(std::bind_front( 968 handleHypervisorEthernetInterfacePatch, std::ref(app))); 969 970 BMCWEB_ROUTE(app, 971 "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset/") 972 .privileges(redfish::privileges::postComputerSystem) 973 .methods(boost::beast::http::verb::post)( 974 std::bind_front(handleHypervisorSystemResetPost, std::ref(app))); 975 } 976 } // namespace redfish 977