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