1 #pragma once 2 3 #include <boost/container/flat_map.hpp> 4 #include <boost/container/flat_set.hpp> 5 #include <dbus_singleton.hpp> 6 #include <error_messages.hpp> 7 #include <node.hpp> 8 #include <utils/json_utils.hpp> 9 10 #include <optional> 11 #include <utility> 12 #include <variant> 13 14 namespace redfish 15 { 16 17 /** 18 * Hypervisor Systems derived class for delivering Computer Systems Schema. 19 */ 20 class HypervisorSystem : public Node 21 { 22 public: 23 /* 24 * Default Constructor 25 */ 26 HypervisorSystem(App& app) : Node(app, "/redfish/v1/Systems/hypervisor/") 27 { 28 entityPrivileges = { 29 {boost::beast::http::verb::get, {{"Login"}}}, 30 {boost::beast::http::verb::head, {{"Login"}}}, 31 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}}; 32 } 33 34 private: 35 /** 36 * Functions triggers appropriate requests on DBus 37 */ 38 void doGet(crow::Response& res, const crow::Request&, 39 const std::vector<std::string>&) override 40 { 41 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 42 crow::connections::systemBus->async_method_call( 43 [asyncResp](const boost::system::error_code ec, 44 const std::variant<std::string>& /*hostName*/) { 45 if (ec) 46 { 47 messages::resourceNotFound(asyncResp->res, "System", 48 "hypervisor"); 49 return; 50 } 51 BMCWEB_LOG_DEBUG << "Hypervisor is available"; 52 53 asyncResp->res.jsonValue["@odata.type"] = 54 "#ComputerSystem.v1_6_0.ComputerSystem"; 55 asyncResp->res.jsonValue["@odata.id"] = 56 "/redfish/v1/Systems/hypervisor"; 57 asyncResp->res.jsonValue["Description"] = "Hypervisor"; 58 asyncResp->res.jsonValue["Name"] = "Hypervisor"; 59 asyncResp->res.jsonValue["Id"] = "hypervisor"; 60 asyncResp->res.jsonValue["Links"]["ManagedBy"] = { 61 {{"@odata.id", "/redfish/v1/Managers/bmc"}}}; 62 asyncResp->res.jsonValue["EthernetInterfaces"] = { 63 {"@odata.id", "/redfish/v1/Systems/hypervisor/" 64 "EthernetInterfaces"}}; 65 // TODO: Add "SystemType" : "hypervisor" 66 }, 67 "xyz.openbmc_project.Settings", 68 "/xyz/openbmc_project/network/hypervisor", 69 "org.freedesktop.DBus.Properties", "Get", 70 "xyz.openbmc_project.Network.SystemConfiguration", "HostName"); 71 } 72 }; 73 74 /** 75 * HypervisorInterfaceCollection class to handle the GET and PATCH on Hypervisor 76 * Interface 77 */ 78 class HypervisorInterfaceCollection : public Node 79 { 80 public: 81 HypervisorInterfaceCollection(App& app) : 82 Node(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/") 83 { 84 entityPrivileges = { 85 {boost::beast::http::verb::get, {{"Login"}}}, 86 {boost::beast::http::verb::head, {{"Login"}}}, 87 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}}; 88 } 89 90 private: 91 /** 92 * Functions triggers appropriate requests on DBus 93 */ 94 void doGet(crow::Response& res, const crow::Request&, 95 const std::vector<std::string>&) override 96 { 97 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 98 const std::array<const char*, 1> interfaces = { 99 "xyz.openbmc_project.Network.EthernetInterface"}; 100 101 crow::connections::systemBus->async_method_call( 102 [asyncResp](const boost::system::error_code error, 103 const std::vector<std::string>& ifaceList) { 104 if (error) 105 { 106 messages::resourceNotFound(asyncResp->res, "System", 107 "hypervisor"); 108 return; 109 } 110 asyncResp->res.jsonValue["@odata.type"] = 111 "#EthernetInterfaceCollection." 112 "EthernetInterfaceCollection"; 113 asyncResp->res.jsonValue["@odata.id"] = 114 "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; 115 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet " 116 "Interface Collection"; 117 asyncResp->res.jsonValue["Description"] = 118 "Collection of Virtual Management " 119 "Interfaces for the hypervisor"; 120 121 nlohmann::json& ifaceArray = 122 asyncResp->res.jsonValue["Members"]; 123 ifaceArray = nlohmann::json::array(); 124 for (const std::string& iface : ifaceList) 125 { 126 sdbusplus::message::object_path path(iface); 127 std::string name = path.filename(); 128 if (name.empty()) 129 { 130 continue; 131 } 132 133 ifaceArray.push_back( 134 {{"@odata.id", "/redfish/v1/Systems/hypervisor/" 135 "EthernetInterfaces/" + 136 name}}); 137 } 138 asyncResp->res.jsonValue["Members@odata.count"] = 139 ifaceArray.size(); 140 }, 141 "xyz.openbmc_project.ObjectMapper", 142 "/xyz/openbmc_project/object_mapper", 143 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", 144 "/xyz/openbmc_project/network/hypervisor", 0, interfaces); 145 } 146 }; 147 148 inline bool extractHypervisorInterfaceData( 149 const std::string& ethIfaceId, const GetManagedObjects& dbusData, 150 EthernetInterfaceData& ethData, 151 boost::container::flat_set<IPv4AddressData>& ipv4Config) 152 { 153 bool idFound = false; 154 for (const auto& objpath : dbusData) 155 { 156 for (const auto& ifacePair : objpath.second) 157 { 158 if (objpath.first == 159 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId) 160 { 161 idFound = true; 162 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress") 163 { 164 for (const auto& propertyPair : ifacePair.second) 165 { 166 if (propertyPair.first == "MACAddress") 167 { 168 const std::string* mac = 169 std::get_if<std::string>(&propertyPair.second); 170 if (mac != nullptr) 171 { 172 ethData.mac_address = *mac; 173 } 174 } 175 } 176 } 177 else if (ifacePair.first == 178 "xyz.openbmc_project.Network.EthernetInterface") 179 { 180 for (const auto& propertyPair : ifacePair.second) 181 { 182 if (propertyPair.first == "DHCPEnabled") 183 { 184 const std::string* dhcp = 185 std::get_if<std::string>(&propertyPair.second); 186 if (dhcp != nullptr) 187 { 188 ethData.DHCPEnabled = *dhcp; 189 break; // Interested on only "DHCPEnabled". 190 // Stop parsing since we got the 191 // "DHCPEnabled" value. 192 } 193 } 194 } 195 } 196 } 197 if (objpath.first == "/xyz/openbmc_project/network/hypervisor/" + 198 ethIfaceId + "/ipv4/addr0") 199 { 200 std::pair<boost::container::flat_set<IPv4AddressData>::iterator, 201 bool> 202 it = ipv4Config.insert(IPv4AddressData{}); 203 IPv4AddressData& ipv4Address = *it.first; 204 if (ifacePair.first == "xyz.openbmc_project.Object.Enable") 205 { 206 for (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 (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 255 { 256 BMCWEB_LOG_ERROR 257 << "Got extra property: " << property.first 258 << " on the " << objpath.first.str << " object"; 259 } 260 } 261 } 262 } 263 if (objpath.first == "/xyz/openbmc_project/network/hypervisor") 264 { 265 // System configuration shows up in the global namespace, so no 266 // need to check eth number 267 if (ifacePair.first == 268 "xyz.openbmc_project.Network.SystemConfiguration") 269 { 270 for (const auto& propertyPair : ifacePair.second) 271 { 272 if (propertyPair.first == "HostName") 273 { 274 const std::string* hostName = 275 std::get_if<std::string>(&propertyPair.second); 276 if (hostName != nullptr) 277 { 278 ethData.hostname = *hostName; 279 } 280 } 281 else if (propertyPair.first == "DefaultGateway") 282 { 283 const std::string* defaultGateway = 284 std::get_if<std::string>(&propertyPair.second); 285 if (defaultGateway != nullptr) 286 { 287 ethData.default_gateway = *defaultGateway; 288 } 289 } 290 } 291 } 292 } 293 } 294 } 295 return idFound; 296 } 297 /** 298 * Function that retrieves all properties for given Hypervisor Ethernet 299 * Interface Object from Settings Manager 300 * @param ethIfaceId Hypervisor ethernet interface id to query on DBus 301 * @param callback a function that shall be called to convert Dbus output 302 * into JSON 303 */ 304 template <typename CallbackFunc> 305 void getHypervisorIfaceData(const std::string& ethIfaceId, 306 CallbackFunc&& callback) 307 { 308 crow::connections::systemBus->async_method_call( 309 [ethIfaceId{std::string{ethIfaceId}}, 310 callback{std::move(callback)}](const boost::system::error_code error, 311 const GetManagedObjects& resp) { 312 EthernetInterfaceData ethData{}; 313 boost::container::flat_set<IPv4AddressData> ipv4Data; 314 if (error) 315 { 316 callback(false, ethData, ipv4Data); 317 return; 318 } 319 320 bool found = extractHypervisorInterfaceData(ethIfaceId, resp, 321 ethData, ipv4Data); 322 if (!found) 323 { 324 BMCWEB_LOG_INFO << "Hypervisor Interface not found"; 325 } 326 callback(found, ethData, ipv4Data); 327 }, 328 "xyz.openbmc_project.Settings", "/", 329 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 330 } 331 332 /** 333 * @brief Sets the Hypervisor Interface IPAddress DBUS 334 * 335 * @param[in] aResp Shared pointer for generating response message. 336 * @param[in] ipv4Address Address from the incoming request 337 * @param[in] ethIfaceId Hypervisor Interface Id 338 * 339 * @return None. 340 */ 341 inline void setHypervisorIPv4Address(const std::shared_ptr<AsyncResp>& aResp, 342 const std::string& ethIfaceId, 343 const std::string& ipv4Address) 344 { 345 BMCWEB_LOG_DEBUG << "Setting the Hypervisor IPaddress : " << ipv4Address 346 << " on Iface: " << ethIfaceId; 347 crow::connections::systemBus->async_method_call( 348 [aResp](const boost::system::error_code ec) { 349 if (ec) 350 { 351 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 352 return; 353 } 354 BMCWEB_LOG_DEBUG << "Hypervisor IPaddress is Set"; 355 }, 356 "xyz.openbmc_project.Settings", 357 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 358 "org.freedesktop.DBus.Properties", "Set", 359 "xyz.openbmc_project.Network.IP", "Address", 360 std::variant<std::string>(ipv4Address)); 361 } 362 363 /** 364 * @brief Sets the Hypervisor Interface SubnetMask DBUS 365 * 366 * @param[in] aResp Shared pointer for generating response message. 367 * @param[in] subnet SubnetMask from the incoming request 368 * @param[in] ethIfaceId Hypervisor Interface Id 369 * 370 * @return None. 371 */ 372 inline void setHypervisorIPv4Subnet(const std::shared_ptr<AsyncResp>& aResp, 373 const std::string& ethIfaceId, 374 const uint8_t subnet) 375 { 376 BMCWEB_LOG_DEBUG << "Setting the Hypervisor subnet : " << subnet 377 << " on Iface: " << ethIfaceId; 378 379 crow::connections::systemBus->async_method_call( 380 [aResp](const boost::system::error_code ec) { 381 if (ec) 382 { 383 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 384 return; 385 } 386 BMCWEB_LOG_DEBUG << "SubnetMask is Set"; 387 }, 388 "xyz.openbmc_project.Settings", 389 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", 390 "org.freedesktop.DBus.Properties", "Set", 391 "xyz.openbmc_project.Network.IP", "PrefixLength", 392 std::variant<uint8_t>(subnet)); 393 } 394 395 /** 396 * @brief Sets the Hypervisor Interface Gateway DBUS 397 * 398 * @param[in] aResp Shared pointer for generating response message. 399 * @param[in] gateway Gateway from the incoming request 400 * @param[in] ethIfaceId Hypervisor Interface Id 401 * 402 * @return None. 403 */ 404 inline void setHypervisorIPv4Gateway(const std::shared_ptr<AsyncResp>& aResp, 405 const std::string& gateway) 406 { 407 BMCWEB_LOG_DEBUG 408 << "Setting the DefaultGateway to the last configured gateway"; 409 410 crow::connections::systemBus->async_method_call( 411 [aResp](const boost::system::error_code ec) { 412 if (ec) 413 { 414 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 415 return; 416 } 417 BMCWEB_LOG_DEBUG << "Default Gateway is Set"; 418 }, 419 "xyz.openbmc_project.Settings", 420 "/xyz/openbmc_project/network/hypervisor", 421 "org.freedesktop.DBus.Properties", "Set", 422 "xyz.openbmc_project.Network.SystemConfiguration", "DefaultGateway", 423 std::variant<std::string>(gateway)); 424 } 425 426 /** 427 * @brief Creates a static IPv4 entry 428 * 429 * @param[in] ifaceId Id of interface upon which to create the IPv4 entry 430 * @param[in] prefixLength IPv4 prefix syntax for the subnet mask 431 * @param[in] gateway IPv4 address of this interfaces gateway 432 * @param[in] address IPv4 address to assign to this interface 433 * @param[io] asyncResp Response object that will be returned to client 434 * 435 * @return None 436 */ 437 inline void createHypervisorIPv4(const std::string& ifaceId, 438 uint8_t prefixLength, 439 const std::string& gateway, 440 const std::string& address, 441 const std::shared_ptr<AsyncResp>& asyncResp) 442 { 443 setHypervisorIPv4Address(asyncResp, ifaceId, address); 444 setHypervisorIPv4Gateway(asyncResp, gateway); 445 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 446 } 447 448 /** 449 * @brief Deletes given IPv4 interface 450 * 451 * @param[in] ifaceId Id of interface whose IP should be deleted 452 * @param[io] asyncResp Response object that will be returned to client 453 * 454 * @return None 455 */ 456 inline void deleteHypervisorIPv4(const std::string& ifaceId, 457 const std::shared_ptr<AsyncResp>& asyncResp) 458 { 459 std::string address = "0.0.0.0"; 460 std::string gateway = "0.0.0.0"; 461 const uint8_t prefixLength = 0; 462 setHypervisorIPv4Address(asyncResp, ifaceId, address); 463 setHypervisorIPv4Gateway(asyncResp, gateway); 464 setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); 465 } 466 467 /** 468 * HypervisorInterface derived class for delivering Ethernet Schema 469 */ 470 class HypervisorInterface : public Node 471 { 472 public: 473 /* 474 * Default Constructor 475 */ 476 HypervisorInterface(App& app) : 477 Node(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/", 478 std::string()) 479 { 480 entityPrivileges = { 481 {boost::beast::http::verb::get, {{"Login"}}}, 482 {boost::beast::http::verb::head, {{"Login"}}}, 483 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}}; 484 } 485 486 private: 487 void parseInterfaceData( 488 nlohmann::json& jsonResponse, const std::string& ifaceId, 489 const EthernetInterfaceData& ethData, 490 const boost::container::flat_set<IPv4AddressData>& ipv4Data) 491 { 492 jsonResponse["Id"] = ifaceId; 493 jsonResponse["@odata.id"] = 494 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/" + ifaceId; 495 jsonResponse["InterfaceEnabled"] = true; 496 jsonResponse["MACAddress"] = ethData.mac_address; 497 498 jsonResponse["HostName"] = ethData.hostname; 499 jsonResponse["DHCPv4"]["DHCPEnabled"] = 500 translateDHCPEnabledToBool(ethData.DHCPEnabled, true); 501 502 nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"]; 503 nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"]; 504 ipv4Array = nlohmann::json::array(); 505 ipv4StaticArray = nlohmann::json::array(); 506 for (auto& ipv4Config : ipv4Data) 507 { 508 if (ipv4Config.isActive) 509 { 510 511 ipv4Array.push_back({{"AddressOrigin", ipv4Config.origin}, 512 {"SubnetMask", ipv4Config.netmask}, 513 {"Address", ipv4Config.address}, 514 {"Gateway", ethData.default_gateway}}); 515 if (ipv4Config.origin == "Static") 516 { 517 ipv4StaticArray.push_back( 518 {{"AddressOrigin", ipv4Config.origin}, 519 {"SubnetMask", ipv4Config.netmask}, 520 {"Address", ipv4Config.address}, 521 {"Gateway", ethData.default_gateway}}); 522 } 523 } 524 } 525 } 526 527 void handleHypervisorIPv4StaticPatch( 528 const std::string& ifaceId, const nlohmann::json& input, 529 const std::shared_ptr<AsyncResp>& asyncResp) 530 { 531 if ((!input.is_array()) || input.empty()) 532 { 533 messages::propertyValueTypeError(asyncResp->res, input.dump(), 534 "IPv4StaticAddresses"); 535 return; 536 } 537 538 // Hypervisor considers the first IP address in the array list 539 // as the Hypervisor's virtual management interface supports single IPv4 540 // address 541 const nlohmann::json& thisJson = input[0]; 542 543 // For the error string 544 std::string pathString = "IPv4StaticAddresses/1"; 545 546 if (!thisJson.is_null() && !thisJson.empty()) 547 { 548 std::optional<std::string> address; 549 std::optional<std::string> subnetMask; 550 std::optional<std::string> gateway; 551 nlohmann::json thisJsonCopy = thisJson; 552 if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address", 553 address, "SubnetMask", subnetMask, 554 "Gateway", gateway)) 555 { 556 messages::propertyValueFormatError(asyncResp->res, 557 thisJson.dump(), pathString); 558 return; 559 } 560 561 uint8_t prefixLength = 0; 562 bool errorInEntry = false; 563 if (address) 564 { 565 if (!ipv4VerifyIpAndGetBitcount(*address)) 566 { 567 messages::propertyValueFormatError(asyncResp->res, *address, 568 pathString + "/Address"); 569 errorInEntry = true; 570 } 571 } 572 else 573 { 574 messages::propertyMissing(asyncResp->res, 575 pathString + "/Address"); 576 errorInEntry = true; 577 } 578 579 if (subnetMask) 580 { 581 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength)) 582 { 583 messages::propertyValueFormatError( 584 asyncResp->res, *subnetMask, 585 pathString + "/SubnetMask"); 586 errorInEntry = true; 587 } 588 } 589 else 590 { 591 messages::propertyMissing(asyncResp->res, 592 pathString + "/SubnetMask"); 593 errorInEntry = true; 594 } 595 596 if (gateway) 597 { 598 if (!ipv4VerifyIpAndGetBitcount(*gateway)) 599 { 600 messages::propertyValueFormatError(asyncResp->res, *gateway, 601 pathString + "/Gateway"); 602 errorInEntry = true; 603 } 604 } 605 else 606 { 607 messages::propertyMissing(asyncResp->res, 608 pathString + "/Gateway"); 609 errorInEntry = true; 610 } 611 612 if (errorInEntry) 613 { 614 return; 615 } 616 617 BMCWEB_LOG_DEBUG << "Calling createHypervisorIPv4 on : " << ifaceId 618 << "," << *address; 619 createHypervisorIPv4(ifaceId, prefixLength, *gateway, *address, 620 asyncResp); 621 // Set the DHCPEnabled to false since the Static IPv4 is set 622 setDHCPEnabled(ifaceId, false, asyncResp); 623 } 624 else 625 { 626 if (thisJson.is_null()) 627 { 628 deleteHypervisorIPv4(ifaceId, asyncResp); 629 } 630 } 631 } 632 633 bool isHostnameValid(const std::string& hostName) 634 { 635 // As per RFC 1123 636 // Allow up to 255 characters 637 if (hostName.length() > 255) 638 { 639 return false; 640 } 641 // Validate the regex 642 const std::regex pattern( 643 "^[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]$"); 644 645 return std::regex_match(hostName, pattern); 646 } 647 648 void handleHostnamePatch(const std::string& hostName, 649 const std::shared_ptr<AsyncResp>& asyncResp) 650 { 651 if (!isHostnameValid(hostName)) 652 { 653 messages::propertyValueFormatError(asyncResp->res, hostName, 654 "HostName"); 655 return; 656 } 657 658 asyncResp->res.jsonValue["HostName"] = hostName; 659 crow::connections::systemBus->async_method_call( 660 [asyncResp](const boost::system::error_code ec) { 661 if (ec) 662 { 663 messages::internalError(asyncResp->res); 664 } 665 }, 666 "xyz.openbmc_project.Settings", 667 "/xyz/openbmc_project/network/hypervisor", 668 "org.freedesktop.DBus.Properties", "Set", 669 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 670 std::variant<std::string>(hostName)); 671 } 672 673 void setIPv4InterfaceEnabled(const std::string& ifaceId, 674 const bool& isActive, 675 const std::shared_ptr<AsyncResp>& asyncResp) 676 { 677 crow::connections::systemBus->async_method_call( 678 [asyncResp](const boost::system::error_code ec) { 679 if (ec) 680 { 681 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 682 messages::internalError(asyncResp->res); 683 return; 684 } 685 }, 686 "xyz.openbmc_project.Settings", 687 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + 688 "/ipv4/addr0", 689 "org.freedesktop.DBus.Properties", "Set", 690 "xyz.openbmc_project.Object.Enable", "Enabled", 691 std::variant<bool>(isActive)); 692 } 693 694 void setDHCPEnabled(const std::string& ifaceId, const bool& ipv4DHCPEnabled, 695 const std::shared_ptr<AsyncResp>& asyncResp) 696 { 697 const std::string dhcp = 698 getDhcpEnabledEnumeration(ipv4DHCPEnabled, false); 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, 710 "org.freedesktop.DBus.Properties", "Set", 711 "xyz.openbmc_project.Network.EthernetInterface", "DHCPEnabled", 712 std::variant<std::string>{dhcp}); 713 714 // Set the IPv4 address origin to the DHCP / Static as per the new value 715 // of the DHCPEnabled property 716 std::string origin; 717 if (ipv4DHCPEnabled == false) 718 { 719 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 720 } 721 else 722 { 723 // DHCPEnabled is set to true. Delete the current IPv4 settings 724 // to receive the new values from DHCP server. 725 deleteHypervisorIPv4(ifaceId, asyncResp); 726 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 727 } 728 crow::connections::systemBus->async_method_call( 729 [asyncResp](const boost::system::error_code ec) { 730 if (ec) 731 { 732 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 733 messages::internalError(asyncResp->res); 734 return; 735 } 736 BMCWEB_LOG_DEBUG << "Hypervisor IPaddress Origin is Set"; 737 }, 738 "xyz.openbmc_project.Settings", 739 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + 740 "/ipv4/addr0", 741 "org.freedesktop.DBus.Properties", "Set", 742 "xyz.openbmc_project.Network.IP", "Origin", 743 std::variant<std::string>(origin)); 744 } 745 746 /** 747 * Functions triggers appropriate requests on DBus 748 */ 749 void doGet(crow::Response& res, const crow::Request&, 750 const std::vector<std::string>& params) override 751 { 752 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 753 if (params.size() != 1) 754 { 755 messages::internalError(asyncResp->res); 756 return; 757 } 758 759 getHypervisorIfaceData( 760 params[0], 761 [this, asyncResp, ifaceId{std::string(params[0])}]( 762 const bool& success, const EthernetInterfaceData& ethData, 763 const boost::container::flat_set<IPv4AddressData>& ipv4Data) { 764 if (!success) 765 { 766 messages::resourceNotFound(asyncResp->res, 767 "EthernetInterface", ifaceId); 768 return; 769 } 770 asyncResp->res.jsonValue["@odata.type"] = 771 "#EthernetInterface.v1_5_1.EthernetInterface"; 772 asyncResp->res.jsonValue["Name"] = 773 "Hypervisor Ethernet Interface"; 774 asyncResp->res.jsonValue["Description"] = 775 "Hypervisor's Virtual Management Ethernet Interface"; 776 parseInterfaceData(asyncResp->res.jsonValue, ifaceId, ethData, 777 ipv4Data); 778 }); 779 } 780 781 void doPatch(crow::Response& res, const crow::Request& req, 782 const std::vector<std::string>& params) override 783 { 784 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 785 if (params.size() != 1) 786 { 787 messages::internalError(asyncResp->res); 788 return; 789 } 790 791 const std::string& ifaceId = params[0]; 792 std::optional<std::string> hostName; 793 std::optional<nlohmann::json> ipv4StaticAddresses; 794 std::optional<nlohmann::json> ipv4Addresses; 795 std::optional<nlohmann::json> dhcpv4; 796 std::optional<bool> ipv4DHCPEnabled; 797 798 if (!json_util::readJson(req, res, "HostName", hostName, 799 "IPv4StaticAddresses", ipv4StaticAddresses, 800 "IPv4Addresses", ipv4Addresses, "DHCPv4", 801 dhcpv4)) 802 { 803 return; 804 } 805 806 if (ipv4Addresses) 807 { 808 messages::propertyNotWritable(asyncResp->res, "IPv4Addresses"); 809 } 810 811 if (dhcpv4) 812 { 813 if (!json_util::readJson(*dhcpv4, res, "DHCPEnabled", 814 ipv4DHCPEnabled)) 815 { 816 return; 817 } 818 } 819 820 getHypervisorIfaceData( 821 ifaceId, 822 [this, asyncResp, ifaceId, hostName = std::move(hostName), 823 ipv4StaticAddresses = std::move(ipv4StaticAddresses), 824 ipv4DHCPEnabled, dhcpv4 = std::move(dhcpv4)]( 825 const bool& success, const EthernetInterfaceData& ethData, 826 const boost::container::flat_set<IPv4AddressData>&) { 827 if (!success) 828 { 829 messages::resourceNotFound(asyncResp->res, 830 "EthernetInterface", ifaceId); 831 return; 832 } 833 834 if (ipv4StaticAddresses) 835 { 836 const nlohmann::json& ipv4Static = *ipv4StaticAddresses; 837 const nlohmann::json& ipv4Json = ipv4Static[0]; 838 // Check if the param is 'null'. If its null, it means that 839 // user wants to delete the IP address. Deleting the IP 840 // address is allowed only if its statically configured. 841 // Deleting the address originated from DHCP is not allowed. 842 if ((ipv4Json.is_null()) && 843 (translateDHCPEnabledToBool(ethData.DHCPEnabled, true))) 844 { 845 BMCWEB_LOG_INFO 846 << "Ignoring the delete on ipv4StaticAddresses " 847 "as the interface is DHCP enabled"; 848 } 849 else 850 { 851 handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, 852 asyncResp); 853 } 854 } 855 856 if (hostName) 857 { 858 handleHostnamePatch(*hostName, asyncResp); 859 } 860 861 if (dhcpv4) 862 { 863 setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); 864 } 865 866 // Set this interface to disabled/inactive. This will be set to 867 // enabled/active by the pldm once the hypervisor consumes the 868 // updated settings from the user. 869 setIPv4InterfaceEnabled(ifaceId, false, asyncResp); 870 }); 871 res.result(boost::beast::http::status::accepted); 872 } 873 }; 874 } // namespace redfish 875