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