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