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<bmcweb::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  * @brief Populate Actions if any are valid for hypervisor object
98  *
99  * The hypervisor state object is optional so this function will only set the
100  * Action if the object is found
101  *
102  * @param[in] aResp     Shared pointer for completing asynchronous calls.
103  *
104  * @return None.
105  */
106 inline void
107     getHypervisorActions(const std::shared_ptr<bmcweb::AsyncResp>& aResp)
108 {
109     BMCWEB_LOG_DEBUG << "Get hypervisor actions.";
110     crow::connections::systemBus->async_method_call(
111         [aResp](
112             const boost::system::error_code ec,
113             const std::vector<std::pair<std::string, std::vector<std::string>>>&
114                 objInfo) {
115             if (ec)
116             {
117                 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
118                 // This is an optional D-Bus object so just return if
119                 // error occurs
120                 return;
121             }
122 
123             if (objInfo.size() == 0)
124             {
125                 // As noted above, this is an optional interface so just return
126                 // if there is no instance found
127                 return;
128             }
129 
130             if (objInfo.size() > 1)
131             {
132                 // More then one hypervisor object is not supported and is an
133                 // error
134                 messages::internalError(aResp->res);
135                 return;
136             }
137 
138             // Object present so system support limited ComputerSystem Action
139             aResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"] = {
140                 {"target",
141                  "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset"},
142                 {"@Redfish.ActionInfo",
143                  "/redfish/v1/Systems/hypervisor/ResetActionInfo"}};
144         },
145         "xyz.openbmc_project.ObjectMapper",
146         "/xyz/openbmc_project/object_mapper",
147         "xyz.openbmc_project.ObjectMapper", "GetObject",
148         "/xyz/openbmc_project/state/hypervisor0",
149         std::array<const char*, 1>{"xyz.openbmc_project.State.Host"});
150 }
151 
152 /**
153  * Hypervisor Systems derived class for delivering Computer Systems Schema.
154  */
155 class HypervisorSystem : public Node
156 {
157   public:
158     /*
159      * Default Constructor
160      */
161     HypervisorSystem(App& app) : Node(app, "/redfish/v1/Systems/hypervisor/")
162     {
163         entityPrivileges = {
164             {boost::beast::http::verb::get, {{"Login"}}},
165             {boost::beast::http::verb::head, {{"Login"}}},
166             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}};
167     }
168 
169   private:
170     /**
171      * Functions triggers appropriate requests on DBus
172      */
173     void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
174                const crow::Request&, const std::vector<std::string>&) override
175     {
176         crow::connections::systemBus->async_method_call(
177             [asyncResp](const boost::system::error_code ec,
178                         const std::variant<std::string>& /*hostName*/) {
179                 if (ec)
180                 {
181                     messages::resourceNotFound(asyncResp->res, "System",
182                                                "hypervisor");
183                     return;
184                 }
185                 BMCWEB_LOG_DEBUG << "Hypervisor is available";
186 
187                 asyncResp->res.jsonValue["@odata.type"] =
188                     "#ComputerSystem.v1_6_0.ComputerSystem";
189                 asyncResp->res.jsonValue["@odata.id"] =
190                     "/redfish/v1/Systems/hypervisor";
191                 asyncResp->res.jsonValue["Description"] = "Hypervisor";
192                 asyncResp->res.jsonValue["Name"] = "Hypervisor";
193                 asyncResp->res.jsonValue["Id"] = "hypervisor";
194                 asyncResp->res.jsonValue["Links"]["ManagedBy"] = {
195                     {{"@odata.id", "/redfish/v1/Managers/bmc"}}};
196                 asyncResp->res.jsonValue["EthernetInterfaces"] = {
197                     {"@odata.id", "/redfish/v1/Systems/hypervisor/"
198                                   "EthernetInterfaces"}};
199                 getHypervisorState(asyncResp);
200                 getHypervisorActions(asyncResp);
201                 // TODO: Add "SystemType" : "hypervisor"
202             },
203             "xyz.openbmc_project.Settings",
204             "/xyz/openbmc_project/network/hypervisor",
205             "org.freedesktop.DBus.Properties", "Get",
206             "xyz.openbmc_project.Network.SystemConfiguration", "HostName");
207     }
208 };
209 
210 /**
211  * HypervisorInterfaceCollection class to handle the GET and PATCH on Hypervisor
212  * Interface
213  */
214 class HypervisorInterfaceCollection : public Node
215 {
216   public:
217     HypervisorInterfaceCollection(App& app) :
218         Node(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/")
219     {
220         entityPrivileges = {
221             {boost::beast::http::verb::get, {{"Login"}}},
222             {boost::beast::http::verb::head, {{"Login"}}},
223             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}};
224     }
225 
226   private:
227     /**
228      * Functions triggers appropriate requests on DBus
229      */
230     void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
231                const crow::Request&, const std::vector<std::string>&) override
232     {
233         const std::array<const char*, 1> interfaces = {
234             "xyz.openbmc_project.Network.EthernetInterface"};
235 
236         crow::connections::systemBus->async_method_call(
237             [asyncResp](const boost::system::error_code error,
238                         const std::vector<std::string>& ifaceList) {
239                 if (error)
240                 {
241                     messages::resourceNotFound(asyncResp->res, "System",
242                                                "hypervisor");
243                     return;
244                 }
245                 asyncResp->res.jsonValue["@odata.type"] =
246                     "#EthernetInterfaceCollection."
247                     "EthernetInterfaceCollection";
248                 asyncResp->res.jsonValue["@odata.id"] =
249                     "/redfish/v1/Systems/hypervisor/EthernetInterfaces";
250                 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet "
251                                                    "Interface Collection";
252                 asyncResp->res.jsonValue["Description"] =
253                     "Collection of Virtual Management "
254                     "Interfaces for the hypervisor";
255 
256                 nlohmann::json& ifaceArray =
257                     asyncResp->res.jsonValue["Members"];
258                 ifaceArray = nlohmann::json::array();
259                 for (const std::string& iface : ifaceList)
260                 {
261                     sdbusplus::message::object_path path(iface);
262                     std::string name = path.filename();
263                     if (name.empty())
264                     {
265                         continue;
266                     }
267 
268                     ifaceArray.push_back(
269                         {{"@odata.id", "/redfish/v1/Systems/hypervisor/"
270                                        "EthernetInterfaces/" +
271                                            name}});
272                 }
273                 asyncResp->res.jsonValue["Members@odata.count"] =
274                     ifaceArray.size();
275             },
276             "xyz.openbmc_project.ObjectMapper",
277             "/xyz/openbmc_project/object_mapper",
278             "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
279             "/xyz/openbmc_project/network/hypervisor", 0, interfaces);
280     }
281 };
282 
283 inline bool extractHypervisorInterfaceData(
284     const std::string& ethIfaceId, const GetManagedObjects& dbusData,
285     EthernetInterfaceData& ethData,
286     boost::container::flat_set<IPv4AddressData>& ipv4Config)
287 {
288     bool idFound = false;
289     for (const auto& objpath : dbusData)
290     {
291         for (const auto& ifacePair : objpath.second)
292         {
293             if (objpath.first ==
294                 "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId)
295             {
296                 idFound = true;
297                 if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress")
298                 {
299                     for (const auto& propertyPair : ifacePair.second)
300                     {
301                         if (propertyPair.first == "MACAddress")
302                         {
303                             const std::string* mac =
304                                 std::get_if<std::string>(&propertyPair.second);
305                             if (mac != nullptr)
306                             {
307                                 ethData.mac_address = *mac;
308                             }
309                         }
310                     }
311                 }
312                 else if (ifacePair.first ==
313                          "xyz.openbmc_project.Network.EthernetInterface")
314                 {
315                     for (const auto& propertyPair : ifacePair.second)
316                     {
317                         if (propertyPair.first == "DHCPEnabled")
318                         {
319                             const std::string* dhcp =
320                                 std::get_if<std::string>(&propertyPair.second);
321                             if (dhcp != nullptr)
322                             {
323                                 ethData.DHCPEnabled = *dhcp;
324                                 break; // Interested on only "DHCPEnabled".
325                                        // Stop parsing since we got the
326                                        // "DHCPEnabled" value.
327                             }
328                         }
329                     }
330                 }
331             }
332             if (objpath.first == "/xyz/openbmc_project/network/hypervisor/" +
333                                      ethIfaceId + "/ipv4/addr0")
334             {
335                 std::pair<boost::container::flat_set<IPv4AddressData>::iterator,
336                           bool>
337                     it = ipv4Config.insert(IPv4AddressData{});
338                 IPv4AddressData& ipv4Address = *it.first;
339                 if (ifacePair.first == "xyz.openbmc_project.Object.Enable")
340                 {
341                     for (auto& property : ifacePair.second)
342                     {
343                         if (property.first == "Enabled")
344                         {
345                             const bool* intfEnable =
346                                 std::get_if<bool>(&property.second);
347                             if (intfEnable != nullptr)
348                             {
349                                 ipv4Address.isActive = *intfEnable;
350                                 break;
351                             }
352                         }
353                     }
354                 }
355                 if (ifacePair.first == "xyz.openbmc_project.Network.IP")
356                 {
357                     for (auto& property : ifacePair.second)
358                     {
359                         if (property.first == "Address")
360                         {
361                             const std::string* address =
362                                 std::get_if<std::string>(&property.second);
363                             if (address != nullptr)
364                             {
365                                 ipv4Address.address = *address;
366                             }
367                         }
368                         else if (property.first == "Origin")
369                         {
370                             const std::string* origin =
371                                 std::get_if<std::string>(&property.second);
372                             if (origin != nullptr)
373                             {
374                                 ipv4Address.origin =
375                                     translateAddressOriginDbusToRedfish(*origin,
376                                                                         true);
377                             }
378                         }
379                         else if (property.first == "PrefixLength")
380                         {
381                             const uint8_t* mask =
382                                 std::get_if<uint8_t>(&property.second);
383                             if (mask != nullptr)
384                             {
385                                 // convert it to the string
386                                 ipv4Address.netmask = getNetmask(*mask);
387                             }
388                         }
389                         else
390                         {
391                             BMCWEB_LOG_ERROR
392                                 << "Got extra property: " << property.first
393                                 << " on the " << objpath.first.str << " object";
394                         }
395                     }
396                 }
397             }
398             if (objpath.first == "/xyz/openbmc_project/network/hypervisor")
399             {
400                 // System configuration shows up in the global namespace, so no
401                 // need to check eth number
402                 if (ifacePair.first ==
403                     "xyz.openbmc_project.Network.SystemConfiguration")
404                 {
405                     for (const auto& propertyPair : ifacePair.second)
406                     {
407                         if (propertyPair.first == "HostName")
408                         {
409                             const std::string* hostName =
410                                 std::get_if<std::string>(&propertyPair.second);
411                             if (hostName != nullptr)
412                             {
413                                 ethData.hostname = *hostName;
414                             }
415                         }
416                         else if (propertyPair.first == "DefaultGateway")
417                         {
418                             const std::string* defaultGateway =
419                                 std::get_if<std::string>(&propertyPair.second);
420                             if (defaultGateway != nullptr)
421                             {
422                                 ethData.default_gateway = *defaultGateway;
423                             }
424                         }
425                     }
426                 }
427             }
428         }
429     }
430     return idFound;
431 }
432 /**
433  * Function that retrieves all properties for given Hypervisor Ethernet
434  * Interface Object from Settings Manager
435  * @param ethIfaceId Hypervisor ethernet interface id to query on DBus
436  * @param callback a function that shall be called to convert Dbus output
437  * into JSON
438  */
439 template <typename CallbackFunc>
440 void getHypervisorIfaceData(const std::string& ethIfaceId,
441                             CallbackFunc&& callback)
442 {
443     crow::connections::systemBus->async_method_call(
444         [ethIfaceId{std::string{ethIfaceId}},
445          callback{std::move(callback)}](const boost::system::error_code error,
446                                         const GetManagedObjects& resp) {
447             EthernetInterfaceData ethData{};
448             boost::container::flat_set<IPv4AddressData> ipv4Data;
449             if (error)
450             {
451                 callback(false, ethData, ipv4Data);
452                 return;
453             }
454 
455             bool found = extractHypervisorInterfaceData(ethIfaceId, resp,
456                                                         ethData, ipv4Data);
457             if (!found)
458             {
459                 BMCWEB_LOG_INFO << "Hypervisor Interface not found";
460             }
461             callback(found, ethData, ipv4Data);
462         },
463         "xyz.openbmc_project.Settings", "/",
464         "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
465 }
466 
467 /**
468  * @brief Sets the Hypervisor Interface IPAddress DBUS
469  *
470  * @param[in] aResp          Shared pointer for generating response message.
471  * @param[in] ipv4Address    Address from the incoming request
472  * @param[in] ethIfaceId     Hypervisor Interface Id
473  *
474  * @return None.
475  */
476 inline void
477     setHypervisorIPv4Address(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
478                              const std::string& ethIfaceId,
479                              const std::string& ipv4Address)
480 {
481     BMCWEB_LOG_DEBUG << "Setting the Hypervisor IPaddress : " << ipv4Address
482                      << " on Iface: " << ethIfaceId;
483     crow::connections::systemBus->async_method_call(
484         [aResp](const boost::system::error_code ec) {
485             if (ec)
486             {
487                 BMCWEB_LOG_ERROR << "DBUS response error " << ec;
488                 return;
489             }
490             BMCWEB_LOG_DEBUG << "Hypervisor IPaddress is Set";
491         },
492         "xyz.openbmc_project.Settings",
493         "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0",
494         "org.freedesktop.DBus.Properties", "Set",
495         "xyz.openbmc_project.Network.IP", "Address",
496         std::variant<std::string>(ipv4Address));
497 }
498 
499 /**
500  * @brief Sets the Hypervisor Interface SubnetMask DBUS
501  *
502  * @param[in] aResp     Shared pointer for generating response message.
503  * @param[in] subnet    SubnetMask from the incoming request
504  * @param[in] ethIfaceId Hypervisor Interface Id
505  *
506  * @return None.
507  */
508 inline void
509     setHypervisorIPv4Subnet(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
510                             const std::string& ethIfaceId, const uint8_t subnet)
511 {
512     BMCWEB_LOG_DEBUG << "Setting the Hypervisor subnet : " << subnet
513                      << " on Iface: " << ethIfaceId;
514 
515     crow::connections::systemBus->async_method_call(
516         [aResp](const boost::system::error_code ec) {
517             if (ec)
518             {
519                 BMCWEB_LOG_ERROR << "DBUS response error " << ec;
520                 return;
521             }
522             BMCWEB_LOG_DEBUG << "SubnetMask is Set";
523         },
524         "xyz.openbmc_project.Settings",
525         "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0",
526         "org.freedesktop.DBus.Properties", "Set",
527         "xyz.openbmc_project.Network.IP", "PrefixLength",
528         std::variant<uint8_t>(subnet));
529 }
530 
531 /**
532  * @brief Sets the Hypervisor Interface Gateway DBUS
533  *
534  * @param[in] aResp          Shared pointer for generating response message.
535  * @param[in] gateway        Gateway from the incoming request
536  * @param[in] ethIfaceId     Hypervisor Interface Id
537  *
538  * @return None.
539  */
540 inline void
541     setHypervisorIPv4Gateway(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
542                              const std::string& gateway)
543 {
544     BMCWEB_LOG_DEBUG
545         << "Setting the DefaultGateway to the last configured gateway";
546 
547     crow::connections::systemBus->async_method_call(
548         [aResp](const boost::system::error_code ec) {
549             if (ec)
550             {
551                 BMCWEB_LOG_ERROR << "DBUS response error " << ec;
552                 return;
553             }
554             BMCWEB_LOG_DEBUG << "Default Gateway is Set";
555         },
556         "xyz.openbmc_project.Settings",
557         "/xyz/openbmc_project/network/hypervisor",
558         "org.freedesktop.DBus.Properties", "Set",
559         "xyz.openbmc_project.Network.SystemConfiguration", "DefaultGateway",
560         std::variant<std::string>(gateway));
561 }
562 
563 /**
564  * @brief Creates a static IPv4 entry
565  *
566  * @param[in] ifaceId      Id of interface upon which to create the IPv4 entry
567  * @param[in] prefixLength IPv4 prefix syntax for the subnet mask
568  * @param[in] gateway      IPv4 address of this interfaces gateway
569  * @param[in] address      IPv4 address to assign to this interface
570  * @param[io] asyncResp    Response object that will be returned to client
571  *
572  * @return None
573  */
574 inline void
575     createHypervisorIPv4(const std::string& ifaceId, uint8_t prefixLength,
576                          const std::string& gateway, const std::string& address,
577                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
578 {
579     setHypervisorIPv4Address(asyncResp, ifaceId, address);
580     setHypervisorIPv4Gateway(asyncResp, gateway);
581     setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength);
582 }
583 
584 /**
585  * @brief Deletes given IPv4 interface
586  *
587  * @param[in] ifaceId     Id of interface whose IP should be deleted
588  * @param[io] asyncResp   Response object that will be returned to client
589  *
590  * @return None
591  */
592 inline void
593     deleteHypervisorIPv4(const std::string& ifaceId,
594                          const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
595 {
596     std::string address = "0.0.0.0";
597     std::string gateway = "0.0.0.0";
598     const uint8_t prefixLength = 0;
599     setHypervisorIPv4Address(asyncResp, ifaceId, address);
600     setHypervisorIPv4Gateway(asyncResp, gateway);
601     setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength);
602 }
603 
604 /**
605  * HypervisorInterface derived class for delivering Ethernet Schema
606  */
607 class HypervisorInterface : public Node
608 {
609   public:
610     /*
611      * Default Constructor
612      */
613     HypervisorInterface(App& app) :
614         Node(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/",
615              std::string())
616     {
617         entityPrivileges = {
618             {boost::beast::http::verb::get, {{"Login"}}},
619             {boost::beast::http::verb::head, {{"Login"}}},
620             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}}};
621     }
622 
623   private:
624     void parseInterfaceData(
625         nlohmann::json& jsonResponse, const std::string& ifaceId,
626         const EthernetInterfaceData& ethData,
627         const boost::container::flat_set<IPv4AddressData>& ipv4Data)
628     {
629         jsonResponse["Id"] = ifaceId;
630         jsonResponse["@odata.id"] =
631             "/redfish/v1/Systems/hypervisor/EthernetInterfaces/" + ifaceId;
632         jsonResponse["InterfaceEnabled"] = true;
633         jsonResponse["MACAddress"] = ethData.mac_address;
634 
635         jsonResponse["HostName"] = ethData.hostname;
636         jsonResponse["DHCPv4"]["DHCPEnabled"] =
637             translateDHCPEnabledToBool(ethData.DHCPEnabled, true);
638 
639         nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"];
640         nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"];
641         ipv4Array = nlohmann::json::array();
642         ipv4StaticArray = nlohmann::json::array();
643         for (auto& ipv4Config : ipv4Data)
644         {
645             if (ipv4Config.isActive)
646             {
647 
648                 ipv4Array.push_back({{"AddressOrigin", ipv4Config.origin},
649                                      {"SubnetMask", ipv4Config.netmask},
650                                      {"Address", ipv4Config.address},
651                                      {"Gateway", ethData.default_gateway}});
652                 if (ipv4Config.origin == "Static")
653                 {
654                     ipv4StaticArray.push_back(
655                         {{"AddressOrigin", ipv4Config.origin},
656                          {"SubnetMask", ipv4Config.netmask},
657                          {"Address", ipv4Config.address},
658                          {"Gateway", ethData.default_gateway}});
659                 }
660             }
661         }
662     }
663 
664     void handleHypervisorIPv4StaticPatch(
665         const std::string& ifaceId, const nlohmann::json& input,
666         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
667     {
668         if ((!input.is_array()) || input.empty())
669         {
670             messages::propertyValueTypeError(asyncResp->res, input.dump(),
671                                              "IPv4StaticAddresses");
672             return;
673         }
674 
675         // Hypervisor considers the first IP address in the array list
676         // as the Hypervisor's virtual management interface supports single IPv4
677         // address
678         const nlohmann::json& thisJson = input[0];
679 
680         // For the error string
681         std::string pathString = "IPv4StaticAddresses/1";
682 
683         if (!thisJson.is_null() && !thisJson.empty())
684         {
685             std::optional<std::string> address;
686             std::optional<std::string> subnetMask;
687             std::optional<std::string> gateway;
688             nlohmann::json thisJsonCopy = thisJson;
689             if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address",
690                                      address, "SubnetMask", subnetMask,
691                                      "Gateway", gateway))
692             {
693                 messages::propertyValueFormatError(
694                     asyncResp->res,
695                     thisJson.dump(2, ' ', true,
696                                   nlohmann::json::error_handler_t::replace),
697                     pathString);
698                 return;
699             }
700 
701             uint8_t prefixLength = 0;
702             bool errorInEntry = false;
703             if (address)
704             {
705                 if (!ipv4VerifyIpAndGetBitcount(*address))
706                 {
707                     messages::propertyValueFormatError(asyncResp->res, *address,
708                                                        pathString + "/Address");
709                     errorInEntry = true;
710                 }
711             }
712             else
713             {
714                 messages::propertyMissing(asyncResp->res,
715                                           pathString + "/Address");
716                 errorInEntry = true;
717             }
718 
719             if (subnetMask)
720             {
721                 if (!ipv4VerifyIpAndGetBitcount(*subnetMask, &prefixLength))
722                 {
723                     messages::propertyValueFormatError(
724                         asyncResp->res, *subnetMask,
725                         pathString + "/SubnetMask");
726                     errorInEntry = true;
727                 }
728             }
729             else
730             {
731                 messages::propertyMissing(asyncResp->res,
732                                           pathString + "/SubnetMask");
733                 errorInEntry = true;
734             }
735 
736             if (gateway)
737             {
738                 if (!ipv4VerifyIpAndGetBitcount(*gateway))
739                 {
740                     messages::propertyValueFormatError(asyncResp->res, *gateway,
741                                                        pathString + "/Gateway");
742                     errorInEntry = true;
743                 }
744             }
745             else
746             {
747                 messages::propertyMissing(asyncResp->res,
748                                           pathString + "/Gateway");
749                 errorInEntry = true;
750             }
751 
752             if (errorInEntry)
753             {
754                 return;
755             }
756 
757             BMCWEB_LOG_DEBUG << "Calling createHypervisorIPv4 on : " << ifaceId
758                              << "," << *address;
759             createHypervisorIPv4(ifaceId, prefixLength, *gateway, *address,
760                                  asyncResp);
761             // Set the DHCPEnabled to false since the Static IPv4 is set
762             setDHCPEnabled(ifaceId, false, asyncResp);
763         }
764         else
765         {
766             if (thisJson.is_null())
767             {
768                 deleteHypervisorIPv4(ifaceId, asyncResp);
769             }
770         }
771     }
772 
773     bool isHostnameValid(const std::string& hostName)
774     {
775         // As per RFC 1123
776         // Allow up to 255 characters
777         if (hostName.length() > 255)
778         {
779             return false;
780         }
781         // Validate the regex
782         const std::regex pattern(
783             "^[a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9]$");
784 
785         return std::regex_match(hostName, pattern);
786     }
787 
788     void
789         handleHostnamePatch(const std::string& hostName,
790                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
791     {
792         if (!isHostnameValid(hostName))
793         {
794             messages::propertyValueFormatError(asyncResp->res, hostName,
795                                                "HostName");
796             return;
797         }
798 
799         asyncResp->res.jsonValue["HostName"] = hostName;
800         crow::connections::systemBus->async_method_call(
801             [asyncResp](const boost::system::error_code ec) {
802                 if (ec)
803                 {
804                     messages::internalError(asyncResp->res);
805                 }
806             },
807             "xyz.openbmc_project.Settings",
808             "/xyz/openbmc_project/network/hypervisor",
809             "org.freedesktop.DBus.Properties", "Set",
810             "xyz.openbmc_project.Network.SystemConfiguration", "HostName",
811             std::variant<std::string>(hostName));
812     }
813 
814     void setIPv4InterfaceEnabled(
815         const std::string& ifaceId, const bool& isActive,
816         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
817     {
818         crow::connections::systemBus->async_method_call(
819             [asyncResp](const boost::system::error_code ec) {
820                 if (ec)
821                 {
822                     BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
823                     messages::internalError(asyncResp->res);
824                     return;
825                 }
826             },
827             "xyz.openbmc_project.Settings",
828             "/xyz/openbmc_project/network/hypervisor/" + ifaceId +
829                 "/ipv4/addr0",
830             "org.freedesktop.DBus.Properties", "Set",
831             "xyz.openbmc_project.Object.Enable", "Enabled",
832             std::variant<bool>(isActive));
833     }
834 
835     void setDHCPEnabled(const std::string& ifaceId, const bool& ipv4DHCPEnabled,
836                         const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
837     {
838         const std::string dhcp =
839             getDhcpEnabledEnumeration(ipv4DHCPEnabled, false);
840         crow::connections::systemBus->async_method_call(
841             [asyncResp](const boost::system::error_code ec) {
842                 if (ec)
843                 {
844                     BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
845                     messages::internalError(asyncResp->res);
846                     return;
847                 }
848             },
849             "xyz.openbmc_project.Settings",
850             "/xyz/openbmc_project/network/hypervisor/" + ifaceId,
851             "org.freedesktop.DBus.Properties", "Set",
852             "xyz.openbmc_project.Network.EthernetInterface", "DHCPEnabled",
853             std::variant<std::string>{dhcp});
854 
855         // Set the IPv4 address origin to the DHCP / Static as per the new value
856         // of the DHCPEnabled property
857         std::string origin;
858         if (ipv4DHCPEnabled == false)
859         {
860             origin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static";
861         }
862         else
863         {
864             // DHCPEnabled is set to true. Delete the current IPv4 settings
865             // to receive the new values from DHCP server.
866             deleteHypervisorIPv4(ifaceId, asyncResp);
867             origin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP";
868         }
869         crow::connections::systemBus->async_method_call(
870             [asyncResp](const boost::system::error_code ec) {
871                 if (ec)
872                 {
873                     BMCWEB_LOG_ERROR << "DBUS response error " << ec;
874                     messages::internalError(asyncResp->res);
875                     return;
876                 }
877                 BMCWEB_LOG_DEBUG << "Hypervisor IPaddress Origin is Set";
878             },
879             "xyz.openbmc_project.Settings",
880             "/xyz/openbmc_project/network/hypervisor/" + ifaceId +
881                 "/ipv4/addr0",
882             "org.freedesktop.DBus.Properties", "Set",
883             "xyz.openbmc_project.Network.IP", "Origin",
884             std::variant<std::string>(origin));
885     }
886 
887     /**
888      * Functions triggers appropriate requests on DBus
889      */
890     void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
891                const crow::Request&,
892                const std::vector<std::string>& params) override
893     {
894         if (params.size() != 1)
895         {
896             messages::internalError(asyncResp->res);
897             return;
898         }
899 
900         getHypervisorIfaceData(
901             params[0],
902             [this, asyncResp, ifaceId{std::string(params[0])}](
903                 const bool& success, const EthernetInterfaceData& ethData,
904                 const boost::container::flat_set<IPv4AddressData>& ipv4Data) {
905                 if (!success)
906                 {
907                     messages::resourceNotFound(asyncResp->res,
908                                                "EthernetInterface", ifaceId);
909                     return;
910                 }
911                 asyncResp->res.jsonValue["@odata.type"] =
912                     "#EthernetInterface.v1_5_1.EthernetInterface";
913                 asyncResp->res.jsonValue["Name"] =
914                     "Hypervisor Ethernet Interface";
915                 asyncResp->res.jsonValue["Description"] =
916                     "Hypervisor's Virtual Management Ethernet Interface";
917                 parseInterfaceData(asyncResp->res.jsonValue, ifaceId, ethData,
918                                    ipv4Data);
919             });
920     }
921 
922     void doPatch(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
923                  const crow::Request& req,
924                  const std::vector<std::string>& params) override
925     {
926 
927         if (params.size() != 1)
928         {
929             messages::internalError(asyncResp->res);
930             return;
931         }
932 
933         const std::string& ifaceId = params[0];
934         std::optional<std::string> hostName;
935         std::optional<std::vector<nlohmann::json>> ipv4StaticAddresses;
936         std::optional<nlohmann::json> ipv4Addresses;
937         std::optional<nlohmann::json> dhcpv4;
938         std::optional<bool> ipv4DHCPEnabled;
939 
940         if (!json_util::readJson(req, asyncResp->res, "HostName", hostName,
941                                  "IPv4StaticAddresses", ipv4StaticAddresses,
942                                  "IPv4Addresses", ipv4Addresses, "DHCPv4",
943                                  dhcpv4))
944         {
945             return;
946         }
947 
948         if (ipv4Addresses)
949         {
950             messages::propertyNotWritable(asyncResp->res, "IPv4Addresses");
951         }
952 
953         if (dhcpv4)
954         {
955             if (!json_util::readJson(*dhcpv4, asyncResp->res, "DHCPEnabled",
956                                      ipv4DHCPEnabled))
957             {
958                 return;
959             }
960         }
961 
962         getHypervisorIfaceData(
963             ifaceId,
964             [this, asyncResp, ifaceId, hostName = std::move(hostName),
965              ipv4StaticAddresses = std::move(ipv4StaticAddresses),
966              ipv4DHCPEnabled, dhcpv4 = std::move(dhcpv4)](
967                 const bool& success, const EthernetInterfaceData& ethData,
968                 const boost::container::flat_set<IPv4AddressData>&) {
969                 if (!success)
970                 {
971                     messages::resourceNotFound(asyncResp->res,
972                                                "EthernetInterface", ifaceId);
973                     return;
974                 }
975 
976                 if (ipv4StaticAddresses)
977                 {
978                     const nlohmann::json& ipv4Static = *ipv4StaticAddresses;
979                     if (ipv4Static.begin() == ipv4Static.end())
980                     {
981                         messages::propertyValueTypeError(
982                             asyncResp->res,
983                             ipv4Static.dump(
984                                 2, ' ', true,
985                                 nlohmann::json::error_handler_t::replace),
986                             "IPv4StaticAddresses");
987                         return;
988                     }
989 
990                     // One and only one hypervisor instance supported
991                     if (ipv4Static.size() != 1)
992                     {
993                         messages::propertyValueFormatError(
994                             asyncResp->res,
995                             ipv4Static.dump(
996                                 2, ' ', true,
997                                 nlohmann::json::error_handler_t::replace),
998                             "IPv4StaticAddresses");
999                         return;
1000                     }
1001 
1002                     const nlohmann::json& ipv4Json = ipv4Static[0];
1003                     // Check if the param is 'null'. If its null, it means that
1004                     // user wants to delete the IP address. Deleting the IP
1005                     // address is allowed only if its statically configured.
1006                     // Deleting the address originated from DHCP is not allowed.
1007                     if ((ipv4Json.is_null()) &&
1008                         (translateDHCPEnabledToBool(ethData.DHCPEnabled, true)))
1009                     {
1010                         BMCWEB_LOG_INFO
1011                             << "Ignoring the delete on ipv4StaticAddresses "
1012                                "as the interface is DHCP enabled";
1013                     }
1014                     else
1015                     {
1016                         handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static,
1017                                                         asyncResp);
1018                     }
1019                 }
1020 
1021                 if (hostName)
1022                 {
1023                     handleHostnamePatch(*hostName, asyncResp);
1024                 }
1025 
1026                 if (dhcpv4)
1027                 {
1028                     setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp);
1029                 }
1030 
1031                 // Set this interface to disabled/inactive. This will be set to
1032                 // enabled/active by the pldm once the hypervisor consumes the
1033                 // updated settings from the user.
1034                 setIPv4InterfaceEnabled(ifaceId, false, asyncResp);
1035             });
1036         asyncResp->res.result(boost::beast::http::status::accepted);
1037     }
1038 };
1039 
1040 /**
1041  * HypervisorResetActionInfo derived class for delivering Computer Systems
1042  * ResetType AllowableValues using ResetInfo schema.
1043  */
1044 class HypervisorResetActionInfo : public Node
1045 {
1046   public:
1047     /*
1048      * Default Constructor
1049      */
1050     HypervisorResetActionInfo(App& app) :
1051         Node(app, "/redfish/v1/Systems/hypervisor/ResetActionInfo/")
1052     {
1053         entityPrivileges = {
1054             {boost::beast::http::verb::get, {{"Login"}}},
1055             {boost::beast::http::verb::head, {{"Login"}}},
1056             {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1057             {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1058             {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1059             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1060     }
1061 
1062   private:
1063     /**
1064      * Functions triggers appropriate requests on DBus
1065      */
1066     void doGet(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1067                const crow::Request&, const std::vector<std::string>&) override
1068     {
1069         // Only return action info if hypervisor D-Bus object present
1070         crow::connections::systemBus->async_method_call(
1071             [asyncResp](const boost::system::error_code ec,
1072                         const std::vector<std::pair<
1073                             std::string, std::vector<std::string>>>& objInfo) {
1074                 if (ec)
1075                 {
1076                     BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1077 
1078                     // No hypervisor objects found by mapper
1079                     if (ec.value() == boost::system::errc::io_error)
1080                     {
1081                         messages::resourceNotFound(asyncResp->res, "hypervisor",
1082                                                    "ResetActionInfo");
1083                         return;
1084                     }
1085 
1086                     messages::internalError(asyncResp->res);
1087                     return;
1088                 }
1089 
1090                 // One and only one hypervisor instance supported
1091                 if (objInfo.size() != 1)
1092                 {
1093                     messages::internalError(asyncResp->res);
1094                     return;
1095                 }
1096 
1097                 // The hypervisor object only support the ability to turn On
1098                 // The system object Action should be utilized for other
1099                 // operations
1100                 asyncResp->res.jsonValue = {
1101                     {"@odata.type", "#ActionInfo.v1_1_2.ActionInfo"},
1102                     {"@odata.id",
1103                      "/redfish/v1/Systems/hypervisor/ResetActionInfo"},
1104                     {"Name", "Reset Action Info"},
1105                     {"Id", "ResetActionInfo"},
1106                     {"Parameters",
1107                      {{{"Name", "ResetType"},
1108                        {"Required", true},
1109                        {"DataType", "String"},
1110                        {"AllowableValues", {"On"}}}}}};
1111             },
1112             "xyz.openbmc_project.ObjectMapper",
1113             "/xyz/openbmc_project/object_mapper",
1114             "xyz.openbmc_project.ObjectMapper", "GetObject",
1115             "/xyz/openbmc_project/state/hypervisor0",
1116             std::array<const char*, 1>{"xyz.openbmc_project.State.Host"});
1117     }
1118 };
1119 
1120 /**
1121  * HypervisorActionsReset class supports the POST method for Reset action.
1122  * The class sends data directly to D-Bus.
1123  */
1124 class HypervisorActionsReset : public Node
1125 {
1126   public:
1127     HypervisorActionsReset(App& app) :
1128         Node(app,
1129              "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset/")
1130     {
1131         entityPrivileges = {
1132             {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1133     }
1134 
1135   private:
1136     /**
1137      * Function handles POST method request.
1138      * Analyzes POST body message before sends Reset request data to D-Bus.
1139      */
1140     void doPost(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1141                 const crow::Request& req,
1142                 const std::vector<std::string>&) override
1143     {
1144 
1145         std::optional<std::string> resetType;
1146         if (!json_util::readJson(req, asyncResp->res, "ResetType", resetType))
1147         {
1148             // readJson adds appropriate error to response
1149             return;
1150         }
1151 
1152         if (!resetType)
1153         {
1154             messages::actionParameterMissing(
1155                 asyncResp->res, "ComputerSystem.Reset", "ResetType");
1156             return;
1157         }
1158 
1159         // Hypervisor object only support On operation
1160         if (resetType != "On")
1161         {
1162             messages::propertyValueNotInList(asyncResp->res, *resetType,
1163                                              "ResetType");
1164             return;
1165         }
1166 
1167         std::string command = "xyz.openbmc_project.State.Host.Transition.On";
1168 
1169         crow::connections::systemBus->async_method_call(
1170             [asyncResp, resetType](const boost::system::error_code ec) {
1171                 if (ec)
1172                 {
1173                     BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec;
1174                     if (ec.value() == boost::asio::error::invalid_argument)
1175                     {
1176                         messages::actionParameterNotSupported(
1177                             asyncResp->res, *resetType, "Reset");
1178                         return;
1179                     }
1180 
1181                     if (ec.value() == boost::asio::error::host_unreachable)
1182                     {
1183                         messages::resourceNotFound(asyncResp->res, "Actions",
1184                                                    "Reset");
1185                         return;
1186                     }
1187 
1188                     messages::internalError(asyncResp->res);
1189                     return;
1190                 }
1191                 messages::success(asyncResp->res);
1192             },
1193             "xyz.openbmc_project.State.Hypervisor",
1194             "/xyz/openbmc_project/state/hypervisor0",
1195             "org.freedesktop.DBus.Properties", "Set",
1196             "xyz.openbmc_project.State.Host", "RequestedHostTransition",
1197             std::variant<std::string>{std::move(command)});
1198     }
1199 };
1200 } // namespace redfish
1201