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