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