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