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 "query.hpp" 9 #include "registries/privilege_registry.hpp" 10 #include "utils/ip_utils.hpp" 11 #include "utils/json_utils.hpp" 12 13 #include <boost/container/flat_set.hpp> 14 #include <sdbusplus/asio/property.hpp> 15 16 #include <array> 17 #include <optional> 18 #include <string_view> 19 #include <utility> 20 21 namespace redfish 22 { 23 24 /** 25 * @brief Retrieves hypervisor state properties over dbus 26 * 27 * The hypervisor state object is optional so this function will only set the 28 * state variables if the object is found 29 * 30 * @param[in] aResp Shared pointer for completing asynchronous calls. 31 * 32 * @return None. 33 */ 34 inline void getHypervisorState(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 35 { 36 BMCWEB_LOG_DEBUG << "Get hypervisor state information."; 37 sdbusplus::asio::getProperty<std::string>( 38 *crow::connections::systemBus, "xyz.openbmc_project.State.Hypervisor", 39 "/xyz/openbmc_project/state/hypervisor0", 40 "xyz.openbmc_project.State.Host", "CurrentHostState", 41 [aResp](const boost::system::error_code& ec, 42 const std::string& hostState) { 43 if (ec) 44 { 45 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 46 // This is an optional D-Bus object so just return if 47 // error occurs 48 return; 49 } 50 51 BMCWEB_LOG_DEBUG << "Hypervisor state: " << hostState; 52 // Verify Host State 53 if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") 54 { 55 aResp->res.jsonValue["PowerState"] = "On"; 56 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 57 } 58 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 59 "Quiesced") 60 { 61 aResp->res.jsonValue["PowerState"] = "On"; 62 aResp->res.jsonValue["Status"]["State"] = "Quiesced"; 63 } 64 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 65 "Standby") 66 { 67 aResp->res.jsonValue["PowerState"] = "On"; 68 aResp->res.jsonValue["Status"]["State"] = "StandbyOffline"; 69 } 70 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 71 "TransitioningToRunning") 72 { 73 aResp->res.jsonValue["PowerState"] = "PoweringOn"; 74 aResp->res.jsonValue["Status"]["State"] = "Starting"; 75 } 76 else if (hostState == "xyz.openbmc_project.State.Host.HostState." 77 "TransitioningToOff") 78 { 79 aResp->res.jsonValue["PowerState"] = "PoweringOff"; 80 aResp->res.jsonValue["Status"]["State"] = "Enabled"; 81 } 82 else if (hostState == "xyz.openbmc_project.State.Host.HostState.Off") 83 { 84 aResp->res.jsonValue["PowerState"] = "Off"; 85 aResp->res.jsonValue["Status"]["State"] = "Disabled"; 86 } 87 else 88 { 89 messages::internalError(aResp->res); 90 return; 91 } 92 }); 93 } 94 95 /** 96 * @brief Populate Actions if any are valid for hypervisor object 97 * 98 * The hypervisor state object is optional so this function will only set the 99 * Action if the object is found 100 * 101 * @param[in] aResp Shared pointer for completing asynchronous calls. 102 * 103 * @return None. 104 */ 105 inline void 106 getHypervisorActions(const std::shared_ptr<bmcweb::AsyncResp>& aResp) 107 { 108 BMCWEB_LOG_DEBUG << "Get hypervisor actions."; 109 constexpr std::array<std::string_view, 1> interfaces = { 110 "xyz.openbmc_project.State.Host"}; 111 dbus::utility::getDbusObject( 112 "/xyz/openbmc_project/state/hypervisor0", interfaces, 113 [aResp]( 114 const boost::system::error_code& ec, 115 const std::vector<std::pair<std::string, std::vector<std::string>>>& 116 objInfo) { 117 if (ec) 118 { 119 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 120 // This is an optional D-Bus object so just return if 121 // error occurs 122 return; 123 } 124 125 if (objInfo.empty()) 126 { 127 // As noted above, this is an optional interface so just return 128 // if there is no instance found 129 return; 130 } 131 132 if (objInfo.size() > 1) 133 { 134 // More then one hypervisor object is not supported and is an 135 // error 136 messages::internalError(aResp->res); 137 return; 138 } 139 140 // Object present so system support limited ComputerSystem Action 141 nlohmann::json& reset = 142 aResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]; 143 reset["target"] = 144 "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset"; 145 reset["@Redfish.ActionInfo"] = 146 "/redfish/v1/Systems/hypervisor/ResetActionInfo"; 147 }); 148 } 149 150 inline bool extractHypervisorInterfaceData( 151 const std::string& ethIfaceId, 152 const dbus::utility::ManagedObjectType& dbusData, 153 EthernetInterfaceData& ethData, 154 boost::container::flat_set<IPv4AddressData>& ipv4Config) 155 { 156 bool idFound = false; 157 for (const auto& objpath : dbusData) 158 { 159 for (const auto& ifacePair : objpath.second) 160 { 161 if (objpath.first == 162 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId) 163 { 164 idFound = true; 165 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress") 166 { 167 for (const auto& propertyPair : ifacePair.second) 168 { 169 if (propertyPair.first == "MACAddress") 170 { 171 const std::string* mac = 172 std::get_if<std::string>(&propertyPair.second); 173 if (mac != nullptr) 174 { 175 ethData.macAddress = *mac; 176 } 177 } 178 } 179 } 180 else if (ifacePair.first == 181 "xyz.openbmc_project.Network.EthernetInterface") 182 { 183 for (const auto& propertyPair : ifacePair.second) 184 { 185 if (propertyPair.first == "DHCPEnabled") 186 { 187 const std::string* dhcp = 188 std::get_if<std::string>(&propertyPair.second); 189 if (dhcp != nullptr) 190 { 191 ethData.dhcpEnabled = *dhcp; 192 break; // Interested on only "DHCPEnabled". 193 // Stop parsing since we got the 194 // "DHCPEnabled" value. 195 } 196 } 197 } 198 } 199 } 200 if (objpath.first == "/xyz/openbmc_project/network/hypervisor/" + 201 ethIfaceId + "/ipv4/addr0") 202 { 203 std::pair<boost::container::flat_set<IPv4AddressData>::iterator, 204 bool> 205 it = ipv4Config.insert(IPv4AddressData{}); 206 IPv4AddressData& ipv4Address = *it.first; 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: " << property.first 267 << " on the " << objpath.first.str << " object"; 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 crow::connections::systemBus->async_method_call( 318 [ethIfaceId{std::string{ethIfaceId}}, 319 callback{std::forward<CallbackFunc>(callback)}]( 320 const boost::system::error_code& error, 321 const dbus::utility::ManagedObjectType& resp) { 322 EthernetInterfaceData ethData{}; 323 boost::container::flat_set<IPv4AddressData> ipv4Data; 324 if (error) 325 { 326 callback(false, ethData, ipv4Data); 327 return; 328 } 329 330 bool found = 331 extractHypervisorInterfaceData(ethIfaceId, resp, ethData, ipv4Data); 332 if (!found) 333 { 334 BMCWEB_LOG_INFO << "Hypervisor Interface not found"; 335 } 336 callback(found, ethData, ipv4Data); 337 }, 338 "xyz.openbmc_project.Settings", "/", 339 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 340 } 341 342 /** 343 * @brief Sets the Hypervisor Interface IPAddress DBUS 344 * 345 * @param[in] aResp 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 352 setHypervisorIPv4Address(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 353 const std::string& ethIfaceId, 354 const std::string& ipv4Address) 355 { 356 BMCWEB_LOG_DEBUG << "Setting the Hypervisor IPaddress : " << ipv4Address 357 << " on Iface: " << ethIfaceId; 358 crow::connections::systemBus->async_method_call( 359 [aResp](const boost::system::error_code& ec) { 360 if (ec) 361 { 362 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 363 return; 364 } 365 BMCWEB_LOG_DEBUG << "Hypervisor IPaddress is Set"; 366 }, 367 "xyz.openbmc_project.Settings", 368 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 369 "org.freedesktop.DBus.Properties", "Set", 370 "xyz.openbmc_project.Network.IP", "Address", 371 dbus::utility::DbusVariantType(ipv4Address)); 372 } 373 374 /** 375 * @brief Sets the Hypervisor Interface SubnetMask DBUS 376 * 377 * @param[in] aResp Shared pointer for generating response message. 378 * @param[in] subnet SubnetMask from the incoming request 379 * @param[in] ethIfaceId Hypervisor Interface Id 380 * 381 * @return None. 382 */ 383 inline void 384 setHypervisorIPv4Subnet(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 385 const std::string& ethIfaceId, const uint8_t subnet) 386 { 387 BMCWEB_LOG_DEBUG << "Setting the Hypervisor subnet : " << subnet 388 << " on Iface: " << ethIfaceId; 389 390 crow::connections::systemBus->async_method_call( 391 [aResp](const boost::system::error_code& ec) { 392 if (ec) 393 { 394 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 395 return; 396 } 397 BMCWEB_LOG_DEBUG << "SubnetMask is Set"; 398 }, 399 "xyz.openbmc_project.Settings", 400 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 401 "org.freedesktop.DBus.Properties", "Set", 402 "xyz.openbmc_project.Network.IP", "PrefixLength", 403 dbus::utility::DbusVariantType(subnet)); 404 } 405 406 /** 407 * @brief Sets the Hypervisor Interface Gateway DBUS 408 * 409 * @param[in] aResp Shared pointer for generating response message. 410 * @param[in] gateway Gateway from the incoming request 411 * @param[in] ethIfaceId Hypervisor Interface Id 412 * 413 * @return None. 414 */ 415 inline void 416 setHypervisorIPv4Gateway(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 417 const std::string& gateway) 418 { 419 BMCWEB_LOG_DEBUG 420 << "Setting the DefaultGateway to the last configured gateway"; 421 422 crow::connections::systemBus->async_method_call( 423 [aResp](const boost::system::error_code& ec) { 424 if (ec) 425 { 426 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 427 return; 428 } 429 BMCWEB_LOG_DEBUG << "Default Gateway is Set"; 430 }, 431 "xyz.openbmc_project.Settings", 432 "/xyz/openbmc_project/network/hypervisor", 433 "org.freedesktop.DBus.Properties", "Set", 434 "xyz.openbmc_project.Network.SystemConfiguration", "DefaultGateway", 435 dbus::utility::DbusVariantType(gateway)); 436 } 437 438 /** 439 * @brief Creates a static IPv4 entry 440 * 441 * @param[in] ifaceId Id of interface upon which to create the IPv4 entry 442 * @param[in] prefixLength IPv4 prefix syntax for the subnet mask 443 * @param[in] gateway IPv4 address of this interfaces gateway 444 * @param[in] address IPv4 address to assign to this interface 445 * @param[io] asyncResp Response object that will be returned to client 446 * 447 * @return None 448 */ 449 inline void 450 createHypervisorIPv4(const std::string& ifaceId, uint8_t prefixLength, 451 const std::string& gateway, const std::string& address, 452 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 453 { 454 setHypervisorIPv4Address(asyncResp, ifaceId, address); 455 setHypervisorIPv4Gateway(asyncResp, gateway); 456 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 457 } 458 459 /** 460 * @brief Deletes given IPv4 interface 461 * 462 * @param[in] ifaceId Id of interface whose IP should be deleted 463 * @param[io] asyncResp Response object that will be returned to client 464 * 465 * @return None 466 */ 467 inline void 468 deleteHypervisorIPv4(const std::string& ifaceId, 469 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 470 { 471 std::string address = "0.0.0.0"; 472 std::string gateway = "0.0.0.0"; 473 const uint8_t prefixLength = 0; 474 setHypervisorIPv4Address(asyncResp, ifaceId, address); 475 setHypervisorIPv4Gateway(asyncResp, gateway); 476 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 477 } 478 479 inline void parseInterfaceData( 480 nlohmann::json& jsonResponse, const std::string& ifaceId, 481 const EthernetInterfaceData& ethData, 482 const boost::container::flat_set<IPv4AddressData>& ipv4Data) 483 { 484 jsonResponse["Id"] = ifaceId; 485 jsonResponse["@odata.id"] = 486 crow::utility::urlFromPieces("redfish", "v1", "Systems", "hypervisor", 487 "EthernetInterfaces", ifaceId); 488 jsonResponse["InterfaceEnabled"] = true; 489 jsonResponse["MACAddress"] = ethData.macAddress; 490 491 jsonResponse["HostName"] = ethData.hostName; 492 jsonResponse["DHCPv4"]["DHCPEnabled"] = 493 translateDhcpEnabledToBool(ethData.dhcpEnabled, true); 494 495 nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"]; 496 nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"]; 497 ipv4Array = nlohmann::json::array(); 498 ipv4StaticArray = nlohmann::json::array(); 499 for (const auto& ipv4Config : ipv4Data) 500 { 501 if (ipv4Config.isActive) 502 { 503 nlohmann::json::object_t ipv4; 504 ipv4["AddressOrigin"] = ipv4Config.origin; 505 ipv4["SubnetMask"] = ipv4Config.netmask; 506 ipv4["Address"] = ipv4Config.address; 507 ipv4["Gateway"] = ethData.defaultGateway; 508 509 if (ipv4Config.origin == "Static") 510 { 511 ipv4StaticArray.push_back(ipv4); 512 } 513 ipv4Array.push_back(std::move(ipv4)); 514 } 515 } 516 } 517 518 inline void setDHCPEnabled(const std::string& ifaceId, 519 const bool& ipv4DHCPEnabled, 520 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 521 { 522 const std::string dhcp = getDhcpEnabledEnumeration(ipv4DHCPEnabled, false); 523 crow::connections::systemBus->async_method_call( 524 [asyncResp](const boost::system::error_code& ec) { 525 if (ec) 526 { 527 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 528 messages::internalError(asyncResp->res); 529 return; 530 } 531 }, 532 "xyz.openbmc_project.Settings", 533 "/xyz/openbmc_project/network/hypervisor/" + ifaceId, 534 "org.freedesktop.DBus.Properties", "Set", 535 "xyz.openbmc_project.Network.EthernetInterface", "DHCPEnabled", 536 dbus::utility::DbusVariantType{dhcp}); 537 538 // Set the IPv4 address origin to the DHCP / Static as per the new value 539 // of the DHCPEnabled property 540 std::string origin; 541 if (!ipv4DHCPEnabled) 542 { 543 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 544 } 545 else 546 { 547 // DHCPEnabled is set to true. Delete the current IPv4 settings 548 // to receive the new values from DHCP server. 549 deleteHypervisorIPv4(ifaceId, asyncResp); 550 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 551 } 552 crow::connections::systemBus->async_method_call( 553 [asyncResp](const boost::system::error_code& ec) { 554 if (ec) 555 { 556 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 557 messages::internalError(asyncResp->res); 558 return; 559 } 560 BMCWEB_LOG_DEBUG << "Hypervisor IPaddress Origin is Set"; 561 }, 562 "xyz.openbmc_project.Settings", 563 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", 564 "org.freedesktop.DBus.Properties", "Set", 565 "xyz.openbmc_project.Network.IP", "Origin", 566 dbus::utility::DbusVariantType(origin)); 567 } 568 569 inline void handleHypervisorIPv4StaticPatch( 570 const std::string& ifaceId, const nlohmann::json& input, 571 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 572 { 573 if ((!input.is_array()) || input.empty()) 574 { 575 messages::propertyValueTypeError(asyncResp->res, input.dump(), 576 "IPv4StaticAddresses"); 577 return; 578 } 579 580 // Hypervisor considers the first IP address in the array list 581 // as the Hypervisor's virtual management interface supports single IPv4 582 // address 583 const nlohmann::json& thisJson = input[0]; 584 585 if (!thisJson.is_null() && !thisJson.empty()) 586 { 587 // For the error string 588 std::string pathString = "IPv4StaticAddresses/1"; 589 std::optional<std::string> address; 590 std::optional<std::string> subnetMask; 591 std::optional<std::string> gateway; 592 nlohmann::json thisJsonCopy = thisJson; 593 if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address", 594 address, "SubnetMask", subnetMask, "Gateway", 595 gateway)) 596 { 597 messages::propertyValueFormatError( 598 asyncResp->res, 599 thisJson.dump(2, ' ', true, 600 nlohmann::json::error_handler_t::replace), 601 pathString); 602 return; 603 } 604 605 uint8_t prefixLength = 0; 606 bool errorInEntry = false; 607 if (address) 608 { 609 if (!ip_util::ipv4VerifyIpAndGetBitcount(*address)) 610 { 611 messages::propertyValueFormatError(asyncResp->res, *address, 612 pathString + "/Address"); 613 errorInEntry = true; 614 } 615 } 616 else 617 { 618 messages::propertyMissing(asyncResp->res, pathString + "/Address"); 619 errorInEntry = true; 620 } 621 622 if (subnetMask) 623 { 624 if (!ip_util::ipv4VerifyIpAndGetBitcount(*subnetMask, 625 &prefixLength)) 626 { 627 messages::propertyValueFormatError(asyncResp->res, *subnetMask, 628 pathString + "/SubnetMask"); 629 errorInEntry = true; 630 } 631 } 632 else 633 { 634 messages::propertyMissing(asyncResp->res, 635 pathString + "/SubnetMask"); 636 errorInEntry = true; 637 } 638 639 if (gateway) 640 { 641 if (!ip_util::ipv4VerifyIpAndGetBitcount(*gateway)) 642 { 643 messages::propertyValueFormatError(asyncResp->res, *gateway, 644 pathString + "/Gateway"); 645 errorInEntry = true; 646 } 647 } 648 else 649 { 650 messages::propertyMissing(asyncResp->res, pathString + "/Gateway"); 651 errorInEntry = true; 652 } 653 654 if (errorInEntry) 655 { 656 return; 657 } 658 659 BMCWEB_LOG_DEBUG << "Calling createHypervisorIPv4 on : " << ifaceId 660 << "," << *address; 661 createHypervisorIPv4(ifaceId, prefixLength, *gateway, *address, 662 asyncResp); 663 // Set the DHCPEnabled to false since the Static IPv4 is set 664 setDHCPEnabled(ifaceId, false, asyncResp); 665 } 666 else 667 { 668 if (thisJson.is_null()) 669 { 670 deleteHypervisorIPv4(ifaceId, asyncResp); 671 } 672 } 673 } 674 675 inline void handleHypervisorHostnamePatch( 676 const std::string& hostName, 677 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 678 { 679 if (!isHostnameValid(hostName)) 680 { 681 messages::propertyValueFormatError(asyncResp->res, hostName, 682 "HostName"); 683 return; 684 } 685 686 asyncResp->res.jsonValue["HostName"] = hostName; 687 crow::connections::systemBus->async_method_call( 688 [asyncResp](const boost::system::error_code& ec) { 689 if (ec) 690 { 691 messages::internalError(asyncResp->res); 692 } 693 }, 694 "xyz.openbmc_project.Settings", 695 "/xyz/openbmc_project/network/hypervisor", 696 "org.freedesktop.DBus.Properties", "Set", 697 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 698 dbus::utility::DbusVariantType(hostName)); 699 } 700 701 inline void 702 setIPv4InterfaceEnabled(const std::string& ifaceId, const bool& isActive, 703 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 704 { 705 crow::connections::systemBus->async_method_call( 706 [asyncResp](const boost::system::error_code& ec) { 707 if (ec) 708 { 709 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 710 messages::internalError(asyncResp->res); 711 return; 712 } 713 }, 714 "xyz.openbmc_project.Settings", 715 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", 716 "org.freedesktop.DBus.Properties", "Set", 717 "xyz.openbmc_project.Object.Enable", "Enabled", 718 dbus::utility::DbusVariantType(isActive)); 719 } 720 721 inline void handleHypervisorEthernetInterfaceCollectionGet( 722 App& app, const crow::Request& req, 723 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 724 { 725 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 726 { 727 return; 728 } 729 constexpr std::array<std::string_view, 1> interfaces = { 730 "xyz.openbmc_project.Network.EthernetInterface"}; 731 732 dbus::utility::getSubTreePaths( 733 "/xyz/openbmc_project/network/hypervisor", 0, interfaces, 734 [asyncResp]( 735 const boost::system::error_code& error, 736 const dbus::utility::MapperGetSubTreePathsResponse& ifaceList) { 737 if (error) 738 { 739 messages::resourceNotFound(asyncResp->res, "System", "hypervisor"); 740 return; 741 } 742 asyncResp->res.jsonValue["@odata.type"] = 743 "#EthernetInterfaceCollection." 744 "EthernetInterfaceCollection"; 745 asyncResp->res.jsonValue["@odata.id"] = 746 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 747 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet " 748 "Interface Collection"; 749 asyncResp->res.jsonValue["Description"] = 750 "Collection of Virtual Management " 751 "Interfaces for the hypervisor"; 752 753 nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; 754 ifaceArray = nlohmann::json::array(); 755 for (const std::string& iface : ifaceList) 756 { 757 sdbusplus::message::object_path path(iface); 758 std::string name = path.filename(); 759 if (name.empty()) 760 { 761 continue; 762 } 763 nlohmann::json::object_t ethIface; 764 ethIface["@odata.id"] = crow::utility::urlFromPieces( 765 "redfish", "v1", "Systems", "hypervisor", "EthernetInterfaces", 766 name); 767 ifaceArray.push_back(std::move(ethIface)); 768 } 769 asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size(); 770 }); 771 } 772 773 inline void handleHypervisorEthernetInterfaceGet( 774 App& app, const crow::Request& req, 775 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) 776 { 777 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 778 { 779 return; 780 } 781 getHypervisorIfaceData( 782 id, [asyncResp, ifaceId{std::string(id)}]( 783 const bool& success, const EthernetInterfaceData& ethData, 784 const boost::container::flat_set<IPv4AddressData>& ipv4Data) { 785 if (!success) 786 { 787 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 788 ifaceId); 789 return; 790 } 791 asyncResp->res.jsonValue["@odata.type"] = 792 "#EthernetInterface.v1_6_0.EthernetInterface"; 793 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet Interface"; 794 asyncResp->res.jsonValue["Description"] = 795 "Hypervisor's Virtual Management Ethernet Interface"; 796 parseInterfaceData(asyncResp->res.jsonValue, ifaceId, ethData, 797 ipv4Data); 798 }); 799 } 800 801 inline void handleHypervisorSystemGet( 802 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 803 { 804 sdbusplus::asio::getProperty<std::string>( 805 *crow::connections::systemBus, "xyz.openbmc_project.Settings", 806 "/xyz/openbmc_project/network/hypervisor", 807 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 808 [asyncResp](const boost::system::error_code& ec, 809 const std::string& /*hostName*/) { 810 if (ec) 811 { 812 messages::resourceNotFound(asyncResp->res, "System", "hypervisor"); 813 return; 814 } 815 BMCWEB_LOG_DEBUG << "Hypervisor is available"; 816 817 asyncResp->res.jsonValue["@odata.type"] = 818 "#ComputerSystem.v1_6_0.ComputerSystem"; 819 asyncResp->res.jsonValue["@odata.id"] = 820 "/redfish/v1/Systems/hypervisor"; 821 asyncResp->res.jsonValue["Description"] = "Hypervisor"; 822 asyncResp->res.jsonValue["Name"] = "Hypervisor"; 823 asyncResp->res.jsonValue["Id"] = "hypervisor"; 824 asyncResp->res.jsonValue["SystemType"] = "OS"; 825 nlohmann::json::array_t managedBy; 826 nlohmann::json::object_t manager; 827 manager["@odata.id"] = "/redfish/v1/Managers/bmc"; 828 managedBy.push_back(std::move(manager)); 829 asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); 830 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = 831 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 832 getHypervisorState(asyncResp); 833 getHypervisorActions(asyncResp); 834 // TODO: Add "SystemType" : "hypervisor" 835 }); 836 } 837 838 inline void handleHypervisorEthernetInterfacePatch( 839 App& app, const crow::Request& req, 840 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 841 const std::string& ifaceId) 842 { 843 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 844 { 845 return; 846 } 847 std::optional<std::string> hostName; 848 std::optional<std::vector<nlohmann::json>> ipv4StaticAddresses; 849 std::optional<nlohmann::json> ipv4Addresses; 850 std::optional<nlohmann::json> dhcpv4; 851 std::optional<bool> ipv4DHCPEnabled; 852 853 if (!json_util::readJsonPatch(req, asyncResp->res, "HostName", hostName, 854 "IPv4StaticAddresses", ipv4StaticAddresses, 855 "IPv4Addresses", ipv4Addresses, "DHCPv4", 856 dhcpv4)) 857 { 858 return; 859 } 860 861 if (ipv4Addresses) 862 { 863 messages::propertyNotWritable(asyncResp->res, "IPv4Addresses"); 864 return; 865 } 866 867 if (dhcpv4) 868 { 869 if (!json_util::readJson(*dhcpv4, asyncResp->res, "DHCPEnabled", 870 ipv4DHCPEnabled)) 871 { 872 return; 873 } 874 } 875 876 getHypervisorIfaceData( 877 ifaceId, 878 [asyncResp, ifaceId, hostName = std::move(hostName), 879 ipv4StaticAddresses = std::move(ipv4StaticAddresses), ipv4DHCPEnabled, 880 dhcpv4 = std::move(dhcpv4)]( 881 const bool& success, const EthernetInterfaceData& ethData, 882 const boost::container::flat_set<IPv4AddressData>&) { 883 if (!success) 884 { 885 messages::resourceNotFound(asyncResp->res, "EthernetInterface", 886 ifaceId); 887 return; 888 } 889 890 if (ipv4StaticAddresses) 891 { 892 const nlohmann::json& ipv4Static = *ipv4StaticAddresses; 893 if (ipv4Static.begin() == ipv4Static.end()) 894 { 895 messages::propertyValueTypeError( 896 asyncResp->res, 897 ipv4Static.dump(2, ' ', true, 898 nlohmann::json::error_handler_t::replace), 899 "IPv4StaticAddresses"); 900 return; 901 } 902 903 // One and only one hypervisor instance supported 904 if (ipv4Static.size() != 1) 905 { 906 messages::propertyValueFormatError( 907 asyncResp->res, 908 ipv4Static.dump(2, ' ', true, 909 nlohmann::json::error_handler_t::replace), 910 "IPv4StaticAddresses"); 911 return; 912 } 913 914 const nlohmann::json& ipv4Json = ipv4Static[0]; 915 // Check if the param is 'null'. If its null, it means 916 // that user wants to delete the IP address. Deleting 917 // the IP address is allowed only if its statically 918 // configured. Deleting the address originated from DHCP 919 // is not allowed. 920 if ((ipv4Json.is_null()) && 921 (translateDhcpEnabledToBool(ethData.dhcpEnabled, true))) 922 { 923 BMCWEB_LOG_INFO << "Ignoring the delete on ipv4StaticAddresses " 924 "as the interface is DHCP enabled"; 925 } 926 else 927 { 928 handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, asyncResp); 929 } 930 } 931 932 if (hostName) 933 { 934 handleHypervisorHostnamePatch(*hostName, asyncResp); 935 } 936 937 if (dhcpv4) 938 { 939 setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); 940 } 941 942 // Set this interface to disabled/inactive. This will be set 943 // to enabled/active by the pldm once the hypervisor 944 // consumes the updated settings from the user. 945 setIPv4InterfaceEnabled(ifaceId, false, asyncResp); 946 }); 947 asyncResp->res.result(boost::beast::http::status::accepted); 948 } 949 950 inline void handleHypervisorResetActionGet( 951 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 952 { 953 // Only return action info if hypervisor D-Bus object present 954 constexpr std::array<std::string_view, 1> interfaces = { 955 "xyz.openbmc_project.State.Host"}; 956 dbus::utility::getDbusObject( 957 "/xyz/openbmc_project/state/hypervisor0", interfaces, 958 [asyncResp]( 959 const boost::system::error_code& ec, 960 const std::vector<std::pair<std::string, std::vector<std::string>>>& 961 objInfo) { 962 if (ec) 963 { 964 BMCWEB_LOG_DEBUG << "DBUS response error " << ec; 965 966 // No hypervisor objects found by mapper 967 if (ec.value() == boost::system::errc::io_error) 968 { 969 messages::resourceNotFound(asyncResp->res, "hypervisor", 970 "ResetActionInfo"); 971 return; 972 } 973 974 messages::internalError(asyncResp->res); 975 return; 976 } 977 978 // One and only one hypervisor instance supported 979 if (objInfo.size() != 1) 980 { 981 messages::internalError(asyncResp->res); 982 return; 983 } 984 985 // The hypervisor object only support the ability to 986 // turn On The system object Action should be utilized 987 // for other operations 988 989 asyncResp->res.jsonValue["@odata.type"] = 990 "#ActionInfo.v1_1_2.ActionInfo"; 991 asyncResp->res.jsonValue["@odata.id"] = 992 "/redfish/v1/Systems/hypervisor/ResetActionInfo"; 993 asyncResp->res.jsonValue["Name"] = "Reset Action Info"; 994 asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; 995 nlohmann::json::array_t parameters; 996 nlohmann::json::object_t parameter; 997 parameter["Name"] = "ResetType"; 998 parameter["Required"] = true; 999 parameter["DataType"] = "String"; 1000 nlohmann::json::array_t allowed; 1001 allowed.push_back("On"); 1002 parameter["AllowableValues"] = std::move(allowed); 1003 parameters.push_back(std::move(parameter)); 1004 asyncResp->res.jsonValue["Parameters"] = std::move(parameters); 1005 }); 1006 } 1007 1008 inline void handleHypervisorSystemResetPost( 1009 App& app, const crow::Request& req, 1010 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 1011 { 1012 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 1013 { 1014 return; 1015 } 1016 std::optional<std::string> resetType; 1017 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType)) 1018 { 1019 // readJson adds appropriate error to response 1020 return; 1021 } 1022 1023 if (!resetType) 1024 { 1025 messages::actionParameterMissing(asyncResp->res, "ComputerSystem.Reset", 1026 "ResetType"); 1027 return; 1028 } 1029 1030 // Hypervisor object only support On operation 1031 if (resetType != "On") 1032 { 1033 messages::propertyValueNotInList(asyncResp->res, *resetType, 1034 "ResetType"); 1035 return; 1036 } 1037 1038 std::string command = "xyz.openbmc_project.State.Host.Transition.On"; 1039 1040 crow::connections::systemBus->async_method_call( 1041 [asyncResp, resetType](const boost::system::error_code& ec) { 1042 if (ec) 1043 { 1044 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 1045 if (ec.value() == boost::asio::error::invalid_argument) 1046 { 1047 messages::actionParameterNotSupported(asyncResp->res, 1048 *resetType, "Reset"); 1049 return; 1050 } 1051 1052 if (ec.value() == boost::asio::error::host_unreachable) 1053 { 1054 messages::resourceNotFound(asyncResp->res, "Actions", "Reset"); 1055 return; 1056 } 1057 1058 messages::internalError(asyncResp->res); 1059 return; 1060 } 1061 messages::success(asyncResp->res); 1062 }, 1063 "xyz.openbmc_project.State.Hypervisor", 1064 "/xyz/openbmc_project/state/hypervisor0", 1065 "org.freedesktop.DBus.Properties", "Set", 1066 "xyz.openbmc_project.State.Host", "RequestedHostTransition", 1067 dbus::utility::DbusVariantType{std::move(command)}); 1068 } 1069 1070 inline void requestRoutesHypervisorSystems(App& app) 1071 { 1072 /** 1073 * HypervisorInterfaceCollection class to handle the GET and PATCH on 1074 * Hypervisor Interface 1075 */ 1076 1077 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/") 1078 .privileges(redfish::privileges::getEthernetInterfaceCollection) 1079 .methods(boost::beast::http::verb::get)(std::bind_front( 1080 handleHypervisorEthernetInterfaceCollectionGet, std::ref(app))); 1081 1082 BMCWEB_ROUTE(app, 1083 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 1084 .privileges(redfish::privileges::getEthernetInterface) 1085 .methods(boost::beast::http::verb::get)(std::bind_front( 1086 handleHypervisorEthernetInterfaceGet, std::ref(app))); 1087 1088 BMCWEB_ROUTE(app, 1089 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") 1090 .privileges(redfish::privileges::patchEthernetInterface) 1091 .methods(boost::beast::http::verb::patch)(std::bind_front( 1092 handleHypervisorEthernetInterfacePatch, std::ref(app))); 1093 1094 BMCWEB_ROUTE(app, 1095 "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset/") 1096 .privileges(redfish::privileges::postComputerSystem) 1097 .methods(boost::beast::http::verb::post)( 1098 std::bind_front(handleHypervisorSystemResetPost, std::ref(app))); 1099 } 1100 } // namespace redfish 1101