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( //
547 *obj, asyncResp->res, //
548 "Address", address, //
549 "Gateway", gateway, //
550 "SubnetMask", subnetMask //
551 ))
552 {
553 return;
554 }
555
556 uint8_t prefixLength = 0;
557 if (!ip_util::ipv4VerifyIpAndGetBitcount(address))
558 {
559 messages::propertyValueFormatError(asyncResp->res, address,
560 pathString + "/Address");
561 return;
562 }
563
564 if (!ip_util::ipv4VerifyIpAndGetBitcount(subnetMask, &prefixLength))
565 {
566 messages::propertyValueFormatError(asyncResp->res, subnetMask,
567 pathString + "/SubnetMask");
568 return;
569 }
570
571 if (!ip_util::ipv4VerifyIpAndGetBitcount(gateway))
572 {
573 messages::propertyValueFormatError(asyncResp->res, gateway,
574 pathString + "/Gateway");
575 return;
576 }
577
578 BMCWEB_LOG_DEBUG("Calling createHypervisorIPv4 on : {},{}", ifaceId,
579 address);
580 createHypervisorIPv4(ifaceId, prefixLength, gateway, address, asyncResp);
581 // Set the DHCPEnabled to false since the Static IPv4 is set
582 setDHCPEnabled(ifaceId, false, asyncResp);
583 }
584
handleHypervisorHostnamePatch(const std::string & hostName,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)585 inline void handleHypervisorHostnamePatch(
586 const std::string& hostName,
587 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
588 {
589 if (!isHostnameValid(hostName))
590 {
591 messages::propertyValueFormatError(asyncResp->res, hostName,
592 "HostName");
593 return;
594 }
595
596 asyncResp->res.jsonValue["HostName"] = hostName;
597 setDbusProperty(asyncResp, "HostName", "xyz.openbmc_project.Settings",
598 sdbusplus::message::object_path(
599 "/xyz/openbmc_project/network/hypervisor"),
600 "xyz.openbmc_project.Network.SystemConfiguration",
601 "HostName", hostName);
602 }
603
604 inline void
setIPv4InterfaceEnabled(const std::string & ifaceId,bool isActive,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)605 setIPv4InterfaceEnabled(const std::string& ifaceId, bool isActive,
606 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
607 {
608 setDbusProperty(
609 asyncResp, "InterfaceEnabled", "xyz.openbmc_project.Settings",
610 "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0",
611 "xyz.openbmc_project.Object.Enable", "Enabled", isActive);
612 }
613
handleHypervisorEthernetInterfaceCollectionGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)614 inline void handleHypervisorEthernetInterfaceCollectionGet(
615 App& app, const crow::Request& req,
616 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
617 {
618 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
619 {
620 return;
621 }
622 constexpr std::array<std::string_view, 1> interfaces = {
623 "xyz.openbmc_project.Network.EthernetInterface"};
624
625 dbus::utility::getSubTreePaths(
626 "/xyz/openbmc_project/network/hypervisor", 0, interfaces,
627 [asyncResp](
628 const boost::system::error_code& ec,
629 const dbus::utility::MapperGetSubTreePathsResponse& ifaceList) {
630 if (ec)
631 {
632 messages::resourceNotFound(asyncResp->res, "System",
633 "hypervisor");
634 return;
635 }
636 asyncResp->res.jsonValue["@odata.type"] =
637 "#EthernetInterfaceCollection."
638 "EthernetInterfaceCollection";
639 asyncResp->res.jsonValue["@odata.id"] =
640 "/redfish/v1/Systems/hypervisor/EthernetInterfaces";
641 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet "
642 "Interface Collection";
643 asyncResp->res.jsonValue["Description"] =
644 "Collection of Virtual Management "
645 "Interfaces for the hypervisor";
646
647 nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"];
648 ifaceArray = nlohmann::json::array();
649 for (const std::string& iface : ifaceList)
650 {
651 sdbusplus::message::object_path path(iface);
652 std::string name = path.filename();
653 if (name.empty())
654 {
655 continue;
656 }
657 nlohmann::json::object_t ethIface;
658 ethIface["@odata.id"] = boost::urls::format(
659 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/{}",
660 name);
661 ifaceArray.emplace_back(std::move(ethIface));
662 }
663 asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size();
664 });
665 }
666
handleHypervisorEthernetInterfaceGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & id)667 inline void handleHypervisorEthernetInterfaceGet(
668 App& app, const crow::Request& req,
669 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id)
670 {
671 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
672 {
673 return;
674 }
675 getHypervisorIfaceData(
676 id, [asyncResp, ifaceId{std::string(id)}](
677 bool success, const EthernetInterfaceData& ethData,
678 const std::vector<IPv4AddressData>& ipv4Data) {
679 if (!success)
680 {
681 messages::resourceNotFound(asyncResp->res, "EthernetInterface",
682 ifaceId);
683 return;
684 }
685 asyncResp->res.jsonValue["@odata.type"] =
686 "#EthernetInterface.v1_9_0.EthernetInterface";
687 asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet Interface";
688 asyncResp->res.jsonValue["Description"] =
689 "Hypervisor's Virtual Management Ethernet Interface";
690 parseInterfaceData(asyncResp->res.jsonValue, ifaceId, ethData,
691 ipv4Data);
692 });
693 }
694
handleHypervisorSystemGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)695 inline void handleHypervisorSystemGet(
696 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
697 {
698 asyncResp->res.jsonValue["@odata.type"] =
699 "#ComputerSystem.v1_6_0.ComputerSystem";
700 asyncResp->res.jsonValue["@odata.id"] = "/redfish/v1/Systems/hypervisor";
701 asyncResp->res.jsonValue["Description"] = "Hypervisor";
702 asyncResp->res.jsonValue["Name"] = "Hypervisor";
703 asyncResp->res.jsonValue["Id"] = "hypervisor";
704 asyncResp->res.jsonValue["SystemType"] = computer_system::SystemType::OS;
705 nlohmann::json::array_t managedBy;
706 nlohmann::json::object_t manager;
707 manager["@odata.id"] = boost::urls::format("/redfish/v1/Managers/{}",
708 BMCWEB_REDFISH_MANAGER_URI_NAME);
709 managedBy.emplace_back(std::move(manager));
710 asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy);
711 asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] =
712 "/redfish/v1/Systems/hypervisor/EthernetInterfaces";
713 getHypervisorState(asyncResp);
714 getHypervisorActions(asyncResp);
715 // TODO: Add "SystemType" : "hypervisor"
716 }
717
handleHypervisorEthernetInterfacePatch(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & ifaceId)718 inline void handleHypervisorEthernetInterfacePatch(
719 App& app, const crow::Request& req,
720 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
721 const std::string& ifaceId)
722 {
723 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
724 {
725 return;
726 }
727 std::optional<std::string> hostName;
728 std::optional<
729 std::vector<std::variant<nlohmann::json::object_t, std::nullptr_t>>>
730 ipv4StaticAddresses;
731 std::optional<std::vector<nlohmann::json::object_t>> ipv4Addresses;
732 std::optional<bool> ipv4DHCPEnabled;
733
734 if (!json_util::readJsonPatch( //
735 req, asyncResp->res, //
736 "DHCPv4/DHCPEnabled", ipv4DHCPEnabled, //
737 "IPv4Addresses", ipv4Addresses, //
738 "IPv4StaticAddresses", ipv4StaticAddresses, //
739 "HostName", hostName //
740 ))
741 {
742 return;
743 }
744
745 if (ipv4Addresses)
746 {
747 messages::propertyNotWritable(asyncResp->res, "IPv4Addresses");
748 return;
749 }
750
751 getHypervisorIfaceData(
752 ifaceId,
753 [asyncResp, ifaceId, hostName = std::move(hostName),
754 ipv4StaticAddresses = std::move(ipv4StaticAddresses),
755 ipv4DHCPEnabled](bool success, const EthernetInterfaceData& ethData,
756 const std::vector<IPv4AddressData>&) mutable {
757 if (!success)
758 {
759 messages::resourceNotFound(asyncResp->res, "EthernetInterface",
760 ifaceId);
761 return;
762 }
763
764 if (ipv4StaticAddresses)
765 {
766 std::vector<std::variant<nlohmann::json::object_t,
767 std::nullptr_t>>& ipv4Static =
768 *ipv4StaticAddresses;
769 if (ipv4Static.begin() == ipv4Static.end())
770 {
771 messages::propertyValueTypeError(asyncResp->res,
772 std::vector<std::string>(),
773 "IPv4StaticAddresses");
774 return;
775 }
776
777 // One and only one hypervisor instance supported
778 if (ipv4Static.size() != 1)
779 {
780 messages::propertyValueFormatError(asyncResp->res, "[]",
781 "IPv4StaticAddresses");
782 return;
783 }
784
785 std::variant<nlohmann::json::object_t, std::nullptr_t>&
786 ipv4Json = ipv4Static[0];
787 // Check if the param is 'null'. If its null, it means
788 // that user wants to delete the IP address. Deleting
789 // the IP address is allowed only if its statically
790 // configured. Deleting the address originated from DHCP
791 // is not allowed.
792 if (std::holds_alternative<std::nullptr_t>(ipv4Json) &&
793 translateDhcpEnabledToBool(ethData.dhcpEnabled, true))
794 {
795 BMCWEB_LOG_INFO(
796 "Ignoring the delete on ipv4StaticAddresses "
797 "as the interface is DHCP enabled");
798 }
799 else
800 {
801 handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static,
802 asyncResp);
803 }
804 }
805
806 if (hostName)
807 {
808 handleHypervisorHostnamePatch(*hostName, asyncResp);
809 }
810
811 if (ipv4DHCPEnabled)
812 {
813 setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp);
814 }
815
816 // Set this interface to disabled/inactive. This will be set
817 // to enabled/active by the pldm once the hypervisor
818 // consumes the updated settings from the user.
819 setIPv4InterfaceEnabled(ifaceId, false, asyncResp);
820 });
821 asyncResp->res.result(boost::beast::http::status::accepted);
822 }
823
handleHypervisorResetActionGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)824 inline void handleHypervisorResetActionGet(
825 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
826 {
827 // Only return action info if hypervisor D-Bus object present
828 constexpr std::array<std::string_view, 1> interfaces = {
829 "xyz.openbmc_project.State.Host"};
830 dbus::utility::getDbusObject(
831 "/xyz/openbmc_project/state/hypervisor0", interfaces,
832 [asyncResp](
833 const boost::system::error_code& ec,
834 const std::vector<std::pair<std::string, std::vector<std::string>>>&
835 objInfo) {
836 if (ec)
837 {
838 BMCWEB_LOG_DEBUG("DBUS response error {}", ec);
839
840 // No hypervisor objects found by mapper
841 if (ec.value() == boost::system::errc::io_error)
842 {
843 messages::resourceNotFound(asyncResp->res, "hypervisor",
844 "ResetActionInfo");
845 return;
846 }
847
848 messages::internalError(asyncResp->res);
849 return;
850 }
851
852 // One and only one hypervisor instance supported
853 if (objInfo.size() != 1)
854 {
855 messages::internalError(asyncResp->res);
856 return;
857 }
858
859 // The hypervisor object only support the ability to
860 // turn On The system object Action should be utilized
861 // for other operations
862
863 asyncResp->res.jsonValue["@odata.type"] =
864 "#ActionInfo.v1_1_2.ActionInfo";
865 asyncResp->res.jsonValue["@odata.id"] =
866 "/redfish/v1/Systems/hypervisor/ResetActionInfo";
867 asyncResp->res.jsonValue["Name"] = "Reset Action Info";
868 asyncResp->res.jsonValue["Id"] = "ResetActionInfo";
869 nlohmann::json::array_t parameters;
870 nlohmann::json::object_t parameter;
871 parameter["Name"] = "ResetType";
872 parameter["Required"] = true;
873 parameter["DataType"] = action_info::ParameterTypes::String;
874 nlohmann::json::array_t allowed;
875 allowed.emplace_back("On");
876 parameter["AllowableValues"] = std::move(allowed);
877 parameters.emplace_back(std::move(parameter));
878 asyncResp->res.jsonValue["Parameters"] = std::move(parameters);
879 });
880 }
881
handleHypervisorSystemResetPost(const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)882 inline void handleHypervisorSystemResetPost(
883 const crow::Request& req,
884 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
885 {
886 std::optional<std::string> resetType;
887 if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType))
888 {
889 // readJson adds appropriate error to response
890 return;
891 }
892
893 if (!resetType)
894 {
895 messages::actionParameterMissing(asyncResp->res, "ComputerSystem.Reset",
896 "ResetType");
897 return;
898 }
899
900 // Hypervisor object only support On operation
901 if (resetType != "On")
902 {
903 messages::propertyValueNotInList(asyncResp->res, *resetType,
904 "ResetType");
905 return;
906 }
907
908 std::string command = "xyz.openbmc_project.State.Host.Transition.On";
909
910 setDbusPropertyAction(
911 asyncResp, "xyz.openbmc_project.State.Hypervisor",
912 sdbusplus::message::object_path(
913 "/xyz/openbmc_project/state/hypervisor0"),
914 "xyz.openbmc_project.State.Host", "RequestedHostTransition",
915 "ResetType", "ComputerSystem.Reset", command);
916 }
917
requestRoutesHypervisorSystems(App & app)918 inline void requestRoutesHypervisorSystems(App& app)
919 {
920 /**
921 * HypervisorInterfaceCollection class to handle the GET and PATCH on
922 * Hypervisor Interface
923 */
924
925 BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/")
926 .privileges(redfish::privileges::getEthernetInterfaceCollection)
927 .methods(boost::beast::http::verb::get)(std::bind_front(
928 handleHypervisorEthernetInterfaceCollectionGet, std::ref(app)));
929
930 BMCWEB_ROUTE(app,
931 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/")
932 .privileges(redfish::privileges::getEthernetInterface)
933 .methods(boost::beast::http::verb::get)(std::bind_front(
934 handleHypervisorEthernetInterfaceGet, std::ref(app)));
935
936 BMCWEB_ROUTE(app,
937 "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/")
938 .privileges(redfish::privileges::patchEthernetInterface)
939 .methods(boost::beast::http::verb::patch)(std::bind_front(
940 handleHypervisorEthernetInterfacePatch, std::ref(app)));
941 }
942 } // namespace redfish
943