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