xref: /openbmc/bmcweb/features/redfish/lib/hypervisor_system.hpp (revision 71f52d96b51bda2a2f00374237f368e980396692)
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