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