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