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