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