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