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 == "xyz.openbmc_project.Network.MACAddress") 182 { 183 for (const auto& propertyPair : ifacePair.second) 184 { 185 if (propertyPair.first == "MACAddress") 186 { 187 const std::string* mac = 188 std::get_if<std::string>(&propertyPair.second); 189 if (mac != nullptr) 190 { 191 ethData.macAddress = *mac; 192 } 193 } 194 } 195 } 196 else if (ifacePair.first == 197 "xyz.openbmc_project.Network.EthernetInterface") 198 { 199 for (const auto& propertyPair : ifacePair.second) 200 { 201 if (propertyPair.first == "DHCPEnabled") 202 { 203 const std::string* dhcp = 204 std::get_if<std::string>(&propertyPair.second); 205 if (dhcp != nullptr) 206 { 207 ethData.dhcpEnabled = *dhcp; 208 break; // Interested on only "DHCPEnabled". 209 // Stop parsing since we got the 210 // "DHCPEnabled" value. 211 } 212 } 213 } 214 } 215 } 216 if (objpath.first == "/xyz/openbmc_project/network/hypervisor/" + 217 ethIfaceId + "/ipv4/addr0") 218 { 219 IPv4AddressData& ipv4Address = ipv4Config.emplace_back(); 220 if (ifacePair.first == "xyz.openbmc_project.Object.Enable") 221 { 222 for (const auto& property : ifacePair.second) 223 { 224 if (property.first == "Enabled") 225 { 226 const bool* intfEnable = 227 std::get_if<bool>(&property.second); 228 if (intfEnable != nullptr) 229 { 230 ipv4Address.isActive = *intfEnable; 231 break; 232 } 233 } 234 } 235 } 236 if (ifacePair.first == "xyz.openbmc_project.Network.IP") 237 { 238 for (const auto& property : ifacePair.second) 239 { 240 if (property.first == "Address") 241 { 242 const std::string* address = 243 std::get_if<std::string>(&property.second); 244 if (address != nullptr) 245 { 246 ipv4Address.address = *address; 247 } 248 } 249 else if (property.first == "Origin") 250 { 251 const std::string* origin = 252 std::get_if<std::string>(&property.second); 253 if (origin != nullptr) 254 { 255 ipv4Address.origin = 256 translateAddressOriginDbusToRedfish(*origin, 257 true); 258 } 259 } 260 else if (property.first == "PrefixLength") 261 { 262 const uint8_t* mask = 263 std::get_if<uint8_t>(&property.second); 264 if (mask != nullptr) 265 { 266 // convert it to the string 267 ipv4Address.netmask = getNetmask(*mask); 268 } 269 } 270 else if (property.first == "Type" || 271 property.first == "Gateway") 272 { 273 // Type & Gateway is not used 274 continue; 275 } 276 else 277 { 278 BMCWEB_LOG_ERROR( 279 "Got extra property: {} on the {} object", 280 property.first, objpath.first.str); 281 } 282 } 283 } 284 } 285 if (objpath.first == "/xyz/openbmc_project/network/hypervisor") 286 { 287 // System configuration shows up in the global namespace, so no 288 // need to check eth number 289 if (ifacePair.first == 290 "xyz.openbmc_project.Network.SystemConfiguration") 291 { 292 for (const auto& propertyPair : ifacePair.second) 293 { 294 if (propertyPair.first == "HostName") 295 { 296 const std::string* hostName = 297 std::get_if<std::string>(&propertyPair.second); 298 if (hostName != nullptr) 299 { 300 ethData.hostName = *hostName; 301 } 302 } 303 else if (propertyPair.first == "DefaultGateway") 304 { 305 const std::string* defaultGateway = 306 std::get_if<std::string>(&propertyPair.second); 307 if (defaultGateway != nullptr) 308 { 309 ethData.defaultGateway = *defaultGateway; 310 } 311 } 312 } 313 } 314 } 315 } 316 } 317 return idFound; 318 } 319 /** 320 * Function that retrieves all properties for given Hypervisor Ethernet 321 * Interface Object from Settings Manager 322 * @param ethIfaceId Hypervisor ethernet interface id to query on DBus 323 * @param callback a function that shall be called to convert Dbus output 324 * into JSON 325 */ 326 template <typename CallbackFunc> 327 void getHypervisorIfaceData(const std::string& ethIfaceId, 328 CallbackFunc&& callback) 329 { 330 sdbusplus::message::object_path path("/"); 331 dbus::utility::getManagedObjects( 332 "xyz.openbmc_project.Settings", path, 333 [ethIfaceId{std::string{ethIfaceId}}, 334 callback = std::forward<CallbackFunc>(callback)]( 335 const boost::system::error_code& ec, 336 const dbus::utility::ManagedObjectType& resp) mutable { 337 EthernetInterfaceData ethData{}; 338 std::vector<IPv4AddressData> ipv4Data; 339 if (ec) 340 { 341 callback(false, ethData, ipv4Data); 342 return; 343 } 344 345 bool found = extractHypervisorInterfaceData(ethIfaceId, resp, 346 ethData, ipv4Data); 347 if (!found) 348 { 349 BMCWEB_LOG_INFO("Hypervisor Interface not found"); 350 } 351 callback(found, ethData, ipv4Data); 352 }); 353 } 354 355 /** 356 * @brief Sets the Hypervisor Interface IPAddress DBUS 357 * 358 * @param[in] asyncResp Shared pointer for generating response message. 359 * @param[in] ipv4Address Address from the incoming request 360 * @param[in] ethIfaceId Hypervisor Interface Id 361 * 362 * @return None. 363 */ 364 inline void setHypervisorIPv4Address( 365 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 366 const std::string& ethIfaceId, const std::string& ipv4Address) 367 { 368 BMCWEB_LOG_DEBUG("Setting the Hypervisor IPaddress : {} on Iface: {}", 369 ipv4Address, ethIfaceId); 370 371 setDbusProperty( 372 asyncResp, "IPv4StaticAddresses/1/Address", 373 "xyz.openbmc_project.Settings", 374 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 375 "xyz.openbmc_project.Network.IP", "Address", ipv4Address); 376 } 377 378 /** 379 * @brief Sets the Hypervisor Interface SubnetMask DBUS 380 * 381 * @param[in] asyncResp Shared pointer for generating response message. 382 * @param[in] subnet SubnetMask from the incoming request 383 * @param[in] ethIfaceId Hypervisor Interface Id 384 * 385 * @return None. 386 */ 387 inline void 388 setHypervisorIPv4Subnet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 389 const std::string& ethIfaceId, const uint8_t subnet) 390 { 391 BMCWEB_LOG_DEBUG("Setting the Hypervisor subnet : {} on Iface: {}", subnet, 392 ethIfaceId); 393 394 setDbusProperty( 395 asyncResp, "IPv4StaticAddresses/1/SubnetMask", 396 "xyz.openbmc_project.Settings", 397 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 398 "xyz.openbmc_project.Network.IP", "PrefixLength", subnet); 399 } 400 401 /** 402 * @brief Sets the Hypervisor Interface Gateway DBUS 403 * 404 * @param[in] asyncResp Shared pointer for generating response message. 405 * @param[in] gateway Gateway from the incoming request 406 * @param[in] ethIfaceId Hypervisor Interface Id 407 * 408 * @return None. 409 */ 410 inline void setHypervisorIPv4Gateway( 411 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 412 const std::string& gateway) 413 { 414 BMCWEB_LOG_DEBUG( 415 "Setting the DefaultGateway to the last configured gateway"); 416 417 setDbusProperty(asyncResp, "IPv4StaticAddresses/1/Gateway", 418 "xyz.openbmc_project.Settings", 419 sdbusplus::message::object_path( 420 "/xyz/openbmc_project/network/hypervisor"), 421 "xyz.openbmc_project.Network.SystemConfiguration", 422 "DefaultGateway", gateway); 423 } 424 425 /** 426 * @brief Creates a static IPv4 entry 427 * 428 * @param[in] ifaceId Id of interface upon which to create the IPv4 entry 429 * @param[in] prefixLength IPv4 prefix syntax for the subnet mask 430 * @param[in] gateway IPv4 address of this interfaces gateway 431 * @param[in] address IPv4 address to assign to this interface 432 * @param[io] asyncResp Response object that will be returned to client 433 * 434 * @return None 435 */ 436 inline void 437 createHypervisorIPv4(const std::string& ifaceId, uint8_t prefixLength, 438 const std::string& gateway, const std::string& address, 439 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 440 { 441 setHypervisorIPv4Address(asyncResp, ifaceId, address); 442 setHypervisorIPv4Gateway(asyncResp, gateway); 443 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 444 } 445 446 /** 447 * @brief Deletes given IPv4 interface 448 * 449 * @param[in] ifaceId Id of interface whose IP should be deleted 450 * @param[io] asyncResp Response object that will be returned to client 451 * 452 * @return None 453 */ 454 inline void 455 deleteHypervisorIPv4(const std::string& ifaceId, 456 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 457 { 458 std::string address = "0.0.0.0"; 459 std::string gateway = "0.0.0.0"; 460 const uint8_t prefixLength = 0; 461 setHypervisorIPv4Address(asyncResp, ifaceId, address); 462 setHypervisorIPv4Gateway(asyncResp, gateway); 463 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 464 } 465 466 inline void parseInterfaceData(nlohmann::json& jsonResponse, 467 const std::string& ifaceId, 468 const EthernetInterfaceData& ethData, 469 const std::vector<IPv4AddressData>& ipv4Data) 470 { 471 jsonResponse["Id"] = ifaceId; 472 jsonResponse["@odata.id"] = boost::urls::format( 473 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/{}", ifaceId); 474 jsonResponse["InterfaceEnabled"] = true; 475 jsonResponse["MACAddress"] = ethData.macAddress; 476 477 jsonResponse["HostName"] = ethData.hostName; 478 jsonResponse["DHCPv4"]["DHCPEnabled"] = 479 translateDhcpEnabledToBool(ethData.dhcpEnabled, true); 480 481 nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"]; 482 nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"]; 483 ipv4Array = nlohmann::json::array(); 484 ipv4StaticArray = nlohmann::json::array(); 485 for (const auto& ipv4Config : ipv4Data) 486 { 487 if (ipv4Config.isActive) 488 { 489 nlohmann::json::object_t ipv4; 490 ipv4["AddressOrigin"] = ipv4Config.origin; 491 ipv4["SubnetMask"] = ipv4Config.netmask; 492 ipv4["Address"] = ipv4Config.address; 493 ipv4["Gateway"] = ethData.defaultGateway; 494 495 if (ipv4Config.origin == "Static") 496 { 497 ipv4StaticArray.push_back(ipv4); 498 } 499 ipv4Array.emplace_back(std::move(ipv4)); 500 } 501 } 502 } 503 504 inline void setDHCPEnabled(const std::string& ifaceId, bool ipv4DHCPEnabled, 505 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 506 { 507 const std::string dhcp = getDhcpEnabledEnumeration(ipv4DHCPEnabled, false); 508 509 setDbusProperty( 510 asyncResp, "DHCPv4/DHCPEnabled", "xyz.openbmc_project.Settings", 511 sdbusplus::message::object_path( 512 "/xyz/openbmc_project/network/hypervisor") / 513 ifaceId, 514 "xyz.openbmc_project.Network.EthernetInterface", "DHCPEnabled", dhcp); 515 516 // Set the IPv4 address origin to the DHCP / Static as per the new value 517 // of the DHCPEnabled property 518 std::string origin; 519 if (!ipv4DHCPEnabled) 520 { 521 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 522 } 523 else 524 { 525 // DHCPEnabled is set to true. Delete the current IPv4 settings 526 // to receive the new values from DHCP server. 527 deleteHypervisorIPv4(ifaceId, asyncResp); 528 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 529 } 530 531 setDbusProperty( 532 asyncResp, "IPv4StaticAddresses/1/AddressOrigin", 533 "xyz.openbmc_project.Settings", 534 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", 535 "xyz.openbmc_project.Network.IP", "Origin", origin); 536 } 537 538 inline void handleHypervisorIPv4StaticPatch( 539 const std::string& ifaceId, 540 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>& input, 541 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 542 { 543 // Hypervisor considers the first IP address in the array list 544 // as the Hypervisor's virtual management interface supports single IPv4 545 // address 546 std::variant<nlohmann::json::object_t, std::nullptr_t>& thisJson = input[0]; 547 nlohmann::json::object_t* obj = 548 std::get_if<nlohmann::json::object_t>(&thisJson); 549 if (obj == nullptr) 550 { 551 deleteHypervisorIPv4(ifaceId, asyncResp); 552 return; 553 } 554 if (obj->empty()) 555 { 556 return; 557 } 558 // For the error string 559 std::string pathString = "IPv4StaticAddresses/1"; 560 std::string address; 561 std::string subnetMask; 562 std::string gateway; 563 if (!json_util::readJsonObject(*obj, asyncResp->res, "Address", address, 564 "SubnetMask", subnetMask, "Gateway", 565 gateway)) 566 { 567 return; 568 } 569 570 uint8_t prefixLength = 0; 571 if (!ip_util::ipv4VerifyIpAndGetBitcount(address)) 572 { 573 messages::propertyValueFormatError(asyncResp->res, address, 574 pathString + "/Address"); 575 return; 576 } 577 578 if (!ip_util::ipv4VerifyIpAndGetBitcount(subnetMask, &prefixLength)) 579 { 580 messages::propertyValueFormatError(asyncResp->res, subnetMask, 581 pathString + "/SubnetMask"); 582 return; 583 } 584 585 if (!ip_util::ipv4VerifyIpAndGetBitcount(gateway)) 586 { 587 messages::propertyValueFormatError(asyncResp->res, gateway, 588 pathString + "/Gateway"); 589 return; 590 } 591 592 BMCWEB_LOG_DEBUG("Calling createHypervisorIPv4 on : {},{}", ifaceId, 593 address); 594 createHypervisorIPv4(ifaceId, prefixLength, gateway, address, asyncResp); 595 // Set the DHCPEnabled to false since the Static IPv4 is set 596 setDHCPEnabled(ifaceId, false, asyncResp); 597 } 598 599 inline void handleHypervisorHostnamePatch( 600 const std::string& hostName, 601 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 602 { 603 if (!isHostnameValid(hostName)) 604 { 605 messages::propertyValueFormatError(asyncResp->res, hostName, 606 "HostName"); 607 return; 608 } 609 610 asyncResp->res.jsonValue["HostName"] = hostName; 611 setDbusProperty(asyncResp, "HostName", "xyz.openbmc_project.Settings", 612 sdbusplus::message::object_path( 613 "/xyz/openbmc_project/network/hypervisor"), 614 "xyz.openbmc_project.Network.SystemConfiguration", 615 "HostName", hostName); 616 } 617 618 inline void 619 setIPv4InterfaceEnabled(const std::string& ifaceId, bool isActive, 620 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 621 { 622 setDbusProperty( 623 asyncResp, "InterfaceEnabled", "xyz.openbmc_project.Settings", 624 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", 625 "xyz.openbmc_project.Object.Enable", "Enabled", isActive); 626 } 627 628 inline void handleHypervisorEthernetInterfaceCollectionGet( 629 App& app, const crow::Request& req, 630 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 631 { 632 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 633 { 634 return; 635 } 636 constexpr std::array<std::string_view, 1> interfaces = { 637 "xyz.openbmc_project.Network.EthernetInterface"}; 638 639 dbus::utility::getSubTreePaths( 640 "/xyz/openbmc_project/network/hypervisor", 0, interfaces, 641 [asyncResp]( 642 const boost::system::error_code& ec, 643 const dbus::utility::MapperGetSubTreePathsResponse& ifaceList) { 644 if (ec) 645 { 646 messages::resourceNotFound(asyncResp->res, "System", 647 "hypervisor"); 648 return; 649 } 650 asyncResp->res.jsonValue["@odata.type"] = 651 "#EthernetInterfaceCollection." 652 "EthernetInterfaceCollection"; 653 asyncResp->res.jsonValue["@odata.id"] = 654 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 655 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet " 656 "Interface Collection"; 657 asyncResp->res.jsonValue["Description"] = 658 "Collection of Virtual Management " 659 "Interfaces for the hypervisor"; 660 661 nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; 662 ifaceArray = nlohmann::json::array(); 663 for (const std::string& iface : ifaceList) 664 { 665 sdbusplus::message::object_path path(iface); 666 std::string name = path.filename(); 667 if (name.empty()) 668 { 669 continue; 670 } 671 nlohmann::json::object_t ethIface; 672 ethIface["@odata.id"] = boost::urls::format( 673 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/{}", 674 name); 675 ifaceArray.emplace_back(std::move(ethIface)); 676 } 677 asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size(); 678 }); 679 } 680 681 inline void handleHypervisorEthernetInterfaceGet( 682 App& app, const crow::Request& req, 683 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 684 { 685 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 686 { 687 return; 688 } 689 getHypervisorIfaceData( 690 id, [asyncResp, ifaceId{std::string(id)}]( 691 bool success, const EthernetInterfaceData& ethData, 692 const std::vector<IPv4AddressData>& ipv4Data) { 693 if (!success) 694 { 695 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 696 ifaceId); 697 return; 698 } 699 asyncResp->res.jsonValue["@odata.type"] = 700 "#EthernetInterface.v1_9_0.EthernetInterface"; 701 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet Interface"; 702 asyncResp->res.jsonValue["Description"] = 703 "Hypervisor's Virtual Management Ethernet Interface"; 704 parseInterfaceData(asyncResp->res.jsonValue, ifaceId, ethData, 705 ipv4Data); 706 }); 707 } 708 709 inline void handleHypervisorSystemGet( 710 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 711 { 712 asyncResp->res.jsonValue["@odata.type"] = 713 "#ComputerSystem.v1_6_0.ComputerSystem"; 714 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems/hypervisor"; 715 asyncResp->res.jsonValue["Description"] = "Hypervisor"; 716 asyncResp->res.jsonValue["Name"] = "Hypervisor"; 717 asyncResp->res.jsonValue["Id"] = "hypervisor"; 718 asyncResp->res.jsonValue["SystemType"] = computer_system::SystemType::OS; 719 nlohmann::json::array_t managedBy; 720 nlohmann::json::object_t manager; 721 manager["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}", 722 BMCWEB_REDFISH_MANAGER_URI_NAME); 723 managedBy.emplace_back(std::move(manager)); 724 asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); 725 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = 726 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 727 getHypervisorState(asyncResp); 728 getHypervisorActions(asyncResp); 729 // TODO: Add "SystemType" : "hypervisor" 730 } 731 732 inline void handleHypervisorEthernetInterfacePatch( 733 App& app, const crow::Request& req, 734 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 735 const std::string& ifaceId) 736 { 737 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 738 { 739 return; 740 } 741 std::optional<std::string> hostName; 742 std::optional< 743 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>> 744 ipv4StaticAddresses; 745 std::optional<std::vector<nlohmann::json::object_t>> ipv4Addresses; 746 std::optional<bool> ipv4DHCPEnabled; 747 748 if (!json_util::readJsonPatch( 749 req, asyncResp->res, "HostName", hostName, "IPv4StaticAddresses", 750 ipv4StaticAddresses, "IPv4Addresses", ipv4Addresses, 751 "DHCPv4/DHCPEnabled", ipv4DHCPEnabled)) 752 { 753 return; 754 } 755 756 if (ipv4Addresses) 757 { 758 messages::propertyNotWritable(asyncResp->res, "IPv4Addresses"); 759 return; 760 } 761 762 getHypervisorIfaceData( 763 ifaceId, 764 [asyncResp, ifaceId, hostName = std::move(hostName), 765 ipv4StaticAddresses = std::move(ipv4StaticAddresses), 766 ipv4DHCPEnabled](bool success, const EthernetInterfaceData& ethData, 767 const std::vector<IPv4AddressData>&) mutable { 768 if (!success) 769 { 770 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 771 ifaceId); 772 return; 773 } 774 775 if (ipv4StaticAddresses) 776 { 777 std::vector<std::variant<nlohmann::json::object_t, 778 std::nullptr_t>>& ipv4Static = 779 *ipv4StaticAddresses; 780 if (ipv4Static.begin() == ipv4Static.end()) 781 { 782 messages::propertyValueTypeError(asyncResp->res, 783 std::vector<std::string>(), 784 "IPv4StaticAddresses"); 785 return; 786 } 787 788 // One and only one hypervisor instance supported 789 if (ipv4Static.size() != 1) 790 { 791 messages::propertyValueFormatError(asyncResp->res, "[]", 792 "IPv4StaticAddresses"); 793 return; 794 } 795 796 std::variant<nlohmann::json::object_t, std::nullptr_t>& 797 ipv4Json = ipv4Static[0]; 798 // Check if the param is 'null'. If its null, it means 799 // that user wants to delete the IP address. Deleting 800 // the IP address is allowed only if its statically 801 // configured. Deleting the address originated from DHCP 802 // is not allowed. 803 if (std::holds_alternative<std::nullptr_t>(ipv4Json) && 804 translateDhcpEnabledToBool(ethData.dhcpEnabled, true)) 805 { 806 BMCWEB_LOG_INFO( 807 "Ignoring the delete on ipv4StaticAddresses " 808 "as the interface is DHCP enabled"); 809 } 810 else 811 { 812 handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, 813 asyncResp); 814 } 815 } 816 817 if (hostName) 818 { 819 handleHypervisorHostnamePatch(*hostName, asyncResp); 820 } 821 822 if (ipv4DHCPEnabled) 823 { 824 setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); 825 } 826 827 // Set this interface to disabled/inactive. This will be set 828 // to enabled/active by the pldm once the hypervisor 829 // consumes the updated settings from the user. 830 setIPv4InterfaceEnabled(ifaceId, false, asyncResp); 831 }); 832 asyncResp->res.result(boost::beast::http::status::accepted); 833 } 834 835 inline void handleHypervisorResetActionGet( 836 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 837 { 838 // Only return action info if hypervisor D-Bus object present 839 constexpr std::array<std::string_view, 1> interfaces = { 840 "xyz.openbmc_project.State.Host"}; 841 dbus::utility::getDbusObject( 842 "/xyz/openbmc_project/state/hypervisor0", interfaces, 843 [asyncResp]( 844 const boost::system::error_code& ec, 845 const std::vector<std::pair<std::string, std::vector<std::string>>>& 846 objInfo) { 847 if (ec) 848 { 849 BMCWEB_LOG_DEBUG("DBUS response error {}", ec); 850 851 // No hypervisor objects found by mapper 852 if (ec.value() == boost::system::errc::io_error) 853 { 854 messages::resourceNotFound(asyncResp->res, "hypervisor", 855 "ResetActionInfo"); 856 return; 857 } 858 859 messages::internalError(asyncResp->res); 860 return; 861 } 862 863 // One and only one hypervisor instance supported 864 if (objInfo.size() != 1) 865 { 866 messages::internalError(asyncResp->res); 867 return; 868 } 869 870 // The hypervisor object only support the ability to 871 // turn On The system object Action should be utilized 872 // for other operations 873 874 asyncResp->res.jsonValue["@odata.type"] = 875 "#ActionInfo.v1_1_2.ActionInfo"; 876 asyncResp->res.jsonValue["@odata.id"] = 877 "/redfish/v1/Systems/hypervisor/ResetActionInfo"; 878 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 879 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 880 nlohmann::json::array_t parameters; 881 nlohmann::json::object_t parameter; 882 parameter["Name"] = "ResetType"; 883 parameter["Required"] = true; 884 parameter["DataType"] = action_info::ParameterTypes::String; 885 nlohmann::json::array_t allowed; 886 allowed.emplace_back("On"); 887 parameter["AllowableValues"] = std::move(allowed); 888 parameters.emplace_back(std::move(parameter)); 889 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 890 }); 891 } 892 893 inline void handleHypervisorSystemResetPost( 894 const crow::Request& req, 895 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 896 { 897 std::optional<std::string> resetType; 898 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType)) 899 { 900 // readJson adds appropriate error to response 901 return; 902 } 903 904 if (!resetType) 905 { 906 messages::actionParameterMissing(asyncResp->res, "ComputerSystem.Reset", 907 "ResetType"); 908 return; 909 } 910 911 // Hypervisor object only support On operation 912 if (resetType != "On") 913 { 914 messages::propertyValueNotInList(asyncResp->res, *resetType, 915 "ResetType"); 916 return; 917 } 918 919 std::string command = "xyz.openbmc_project.State.Host.Transition.On"; 920 921 setDbusPropertyAction( 922 asyncResp, "xyz.openbmc_project.State.Hypervisor", 923 sdbusplus::message::object_path( 924 "/xyz/openbmc_project/state/hypervisor0"), 925 "xyz.openbmc_project.State.Host", "RequestedHostTransition", 926 "ResetType", "ComputerSystem.Reset", command); 927 } 928 929 inline void requestRoutesHypervisorSystems(App& app) 930 { 931 /** 932 * HypervisorInterfaceCollection class to handle the GET and PATCH on 933 * Hypervisor Interface 934 */ 935 936 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/") 937 .privileges(redfish::privileges::getEthernetInterfaceCollection) 938 .methods(boost::beast::http::verb::get)(std::bind_front( 939 handleHypervisorEthernetInterfaceCollectionGet, std::ref(app))); 940 941 BMCWEB_ROUTE(app, 942 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 943 .privileges(redfish::privileges::getEthernetInterface) 944 .methods(boost::beast::http::verb::get)(std::bind_front( 945 handleHypervisorEthernetInterfaceGet, std::ref(app))); 946 947 BMCWEB_ROUTE(app, 948 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 949 .privileges(redfish::privileges::patchEthernetInterface) 950 .methods(boost::beast::http::verb::patch)(std::bind_front( 951 handleHypervisorEthernetInterfacePatch, std::ref(app))); 952 } 953 } // namespace redfish 954