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