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