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