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