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