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