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( 557 asyncResp->res, 558 thisJson.dump(2, ' ', true, 559 nlohmann::json::error_handler_t::replace), 560 pathString); 561 return; 562 } 563 564 uint8_t prefixLength = 0; 565 bool errorInEntry = false; 566 if (address) 567 { 568 if (!ipv4VerifyIpAndGetBitcount(*address)) 569 { 570 messages::propertyValueFormatError(asyncResp->res, *address, 571 pathString + "/Address"); 572 errorInEntry = true; 573 } 574 } 575 else 576 { 577 messages::propertyMissing(asyncResp->res, 578 pathString + "/Address"); 579 errorInEntry = true; 580 } 581 582 if (subnetMask) 583 { 584 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength)) 585 { 586 messages::propertyValueFormatError( 587 asyncResp->res, *subnetMask, 588 pathString + "/SubnetMask"); 589 errorInEntry = true; 590 } 591 } 592 else 593 { 594 messages::propertyMissing(asyncResp->res, 595 pathString + "/SubnetMask"); 596 errorInEntry = true; 597 } 598 599 if (gateway) 600 { 601 if (!ipv4VerifyIpAndGetBitcount(*gateway)) 602 { 603 messages::propertyValueFormatError(asyncResp->res, *gateway, 604 pathString + "/Gateway"); 605 errorInEntry = true; 606 } 607 } 608 else 609 { 610 messages::propertyMissing(asyncResp->res, 611 pathString + "/Gateway"); 612 errorInEntry = true; 613 } 614 615 if (errorInEntry) 616 { 617 return; 618 } 619 620 BMCWEB_LOG_DEBUG << "Calling createHypervisorIPv4 on : " << ifaceId 621 << "," << *address; 622 createHypervisorIPv4(ifaceId, prefixLength, *gateway, *address, 623 asyncResp); 624 // Set the DHCPEnabled to false since the Static IPv4 is set 625 setDHCPEnabled(ifaceId, false, asyncResp); 626 } 627 else 628 { 629 if (thisJson.is_null()) 630 { 631 deleteHypervisorIPv4(ifaceId, asyncResp); 632 } 633 } 634 } 635 636 bool isHostnameValid(const std::string& hostName) 637 { 638 // As per RFC 1123 639 // Allow up to 255 characters 640 if (hostName.length() > 255) 641 { 642 return false; 643 } 644 // Validate the regex 645 const std::regex pattern( 646 "^[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]$"); 647 648 return std::regex_match(hostName, pattern); 649 } 650 651 void handleHostnamePatch(const std::string& hostName, 652 const std::shared_ptr<AsyncResp>& asyncResp) 653 { 654 if (!isHostnameValid(hostName)) 655 { 656 messages::propertyValueFormatError(asyncResp->res, hostName, 657 "HostName"); 658 return; 659 } 660 661 asyncResp->res.jsonValue["HostName"] = hostName; 662 crow::connections::systemBus->async_method_call( 663 [asyncResp](const boost::system::error_code ec) { 664 if (ec) 665 { 666 messages::internalError(asyncResp->res); 667 } 668 }, 669 "xyz.openbmc_project.Settings", 670 "/xyz/openbmc_project/network/hypervisor", 671 "org.freedesktop.DBus.Properties", "Set", 672 "xyz.openbmc_project.Network.SystemConfiguration", "HostName", 673 std::variant<std::string>(hostName)); 674 } 675 676 void setIPv4InterfaceEnabled(const std::string& ifaceId, 677 const bool& isActive, 678 const std::shared_ptr<AsyncResp>& asyncResp) 679 { 680 crow::connections::systemBus->async_method_call( 681 [asyncResp](const boost::system::error_code ec) { 682 if (ec) 683 { 684 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 685 messages::internalError(asyncResp->res); 686 return; 687 } 688 }, 689 "xyz.openbmc_project.Settings", 690 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + 691 "/ipv4/addr0", 692 "org.freedesktop.DBus.Properties", "Set", 693 "xyz.openbmc_project.Object.Enable", "Enabled", 694 std::variant<bool>(isActive)); 695 } 696 697 void setDHCPEnabled(const std::string& ifaceId, const bool& ipv4DHCPEnabled, 698 const std::shared_ptr<AsyncResp>& asyncResp) 699 { 700 const std::string dhcp = 701 getDhcpEnabledEnumeration(ipv4DHCPEnabled, false); 702 crow::connections::systemBus->async_method_call( 703 [asyncResp](const boost::system::error_code ec) { 704 if (ec) 705 { 706 BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; 707 messages::internalError(asyncResp->res); 708 return; 709 } 710 }, 711 "xyz.openbmc_project.Settings", 712 "/xyz/openbmc_project/network/hypervisor/" + ifaceId, 713 "org.freedesktop.DBus.Properties", "Set", 714 "xyz.openbmc_project.Network.EthernetInterface", "DHCPEnabled", 715 std::variant<std::string>{dhcp}); 716 717 // Set the IPv4 address origin to the DHCP / Static as per the new value 718 // of the DHCPEnabled property 719 std::string origin; 720 if (ipv4DHCPEnabled == false) 721 { 722 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; 723 } 724 else 725 { 726 // DHCPEnabled is set to true. Delete the current IPv4 settings 727 // to receive the new values from DHCP server. 728 deleteHypervisorIPv4(ifaceId, asyncResp); 729 origin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; 730 } 731 crow::connections::systemBus->async_method_call( 732 [asyncResp](const boost::system::error_code ec) { 733 if (ec) 734 { 735 BMCWEB_LOG_ERROR << "DBUS response error " << ec; 736 messages::internalError(asyncResp->res); 737 return; 738 } 739 BMCWEB_LOG_DEBUG << "Hypervisor IPaddress Origin is Set"; 740 }, 741 "xyz.openbmc_project.Settings", 742 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + 743 "/ipv4/addr0", 744 "org.freedesktop.DBus.Properties", "Set", 745 "xyz.openbmc_project.Network.IP", "Origin", 746 std::variant<std::string>(origin)); 747 } 748 749 /** 750 * Functions triggers appropriate requests on DBus 751 */ 752 void doGet(crow::Response& res, const crow::Request&, 753 const std::vector<std::string>& params) override 754 { 755 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 756 if (params.size() != 1) 757 { 758 messages::internalError(asyncResp->res); 759 return; 760 } 761 762 getHypervisorIfaceData( 763 params[0], 764 [this, asyncResp, ifaceId{std::string(params[0])}]( 765 const bool& success, const EthernetInterfaceData& ethData, 766 const boost::container::flat_set<IPv4AddressData>& ipv4Data) { 767 if (!success) 768 { 769 messages::resourceNotFound(asyncResp->res, 770 "EthernetInterface", ifaceId); 771 return; 772 } 773 asyncResp->res.jsonValue["@odata.type"] = 774 "#EthernetInterface.v1_5_1.EthernetInterface"; 775 asyncResp->res.jsonValue["Name"] = 776 "Hypervisor Ethernet Interface"; 777 asyncResp->res.jsonValue["Description"] = 778 "Hypervisor's Virtual Management Ethernet Interface"; 779 parseInterfaceData(asyncResp->res.jsonValue, ifaceId, ethData, 780 ipv4Data); 781 }); 782 } 783 784 void doPatch(crow::Response& res, const crow::Request& req, 785 const std::vector<std::string>& params) override 786 { 787 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res); 788 if (params.size() != 1) 789 { 790 messages::internalError(asyncResp->res); 791 return; 792 } 793 794 const std::string& ifaceId = params[0]; 795 std::optional<std::string> hostName; 796 std::optional<nlohmann::json> ipv4StaticAddresses; 797 std::optional<nlohmann::json> ipv4Addresses; 798 std::optional<nlohmann::json> dhcpv4; 799 std::optional<bool> ipv4DHCPEnabled; 800 801 if (!json_util::readJson(req, res, "HostName", hostName, 802 "IPv4StaticAddresses", ipv4StaticAddresses, 803 "IPv4Addresses", ipv4Addresses, "DHCPv4", 804 dhcpv4)) 805 { 806 return; 807 } 808 809 if (ipv4Addresses) 810 { 811 messages::propertyNotWritable(asyncResp->res, "IPv4Addresses"); 812 } 813 814 if (dhcpv4) 815 { 816 if (!json_util::readJson(*dhcpv4, res, "DHCPEnabled", 817 ipv4DHCPEnabled)) 818 { 819 return; 820 } 821 } 822 823 getHypervisorIfaceData( 824 ifaceId, 825 [this, asyncResp, ifaceId, hostName = std::move(hostName), 826 ipv4StaticAddresses = std::move(ipv4StaticAddresses), 827 ipv4DHCPEnabled, dhcpv4 = std::move(dhcpv4)]( 828 const bool& success, const EthernetInterfaceData& ethData, 829 const boost::container::flat_set<IPv4AddressData>&) { 830 if (!success) 831 { 832 messages::resourceNotFound(asyncResp->res, 833 "EthernetInterface", ifaceId); 834 return; 835 } 836 837 if (ipv4StaticAddresses) 838 { 839 const nlohmann::json& ipv4Static = *ipv4StaticAddresses; 840 const nlohmann::json& ipv4Json = ipv4Static[0]; 841 // Check if the param is 'null'. If its null, it means that 842 // user wants to delete the IP address. Deleting the IP 843 // address is allowed only if its statically configured. 844 // Deleting the address originated from DHCP is not allowed. 845 if ((ipv4Json.is_null()) && 846 (translateDHCPEnabledToBool(ethData.DHCPEnabled, true))) 847 { 848 BMCWEB_LOG_INFO 849 << "Ignoring the delete on ipv4StaticAddresses " 850 "as the interface is DHCP enabled"; 851 } 852 else 853 { 854 handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, 855 asyncResp); 856 } 857 } 858 859 if (hostName) 860 { 861 handleHostnamePatch(*hostName, asyncResp); 862 } 863 864 if (dhcpv4) 865 { 866 setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); 867 } 868 869 // Set this interface to disabled/inactive. This will be set to 870 // enabled/active by the pldm once the hypervisor consumes the 871 // updated settings from the user. 872 setIPv4InterfaceEnabled(ifaceId, false, asyncResp); 873 }); 874 res.result(boost::beast::http::status::accepted); 875 } 876 }; 877 } // namespace redfish 878