| #pragma once |
| |
| #include "app.hpp" |
| #include "dbus_utility.hpp" |
| #include "error_messages.hpp" |
| #include "ethernet.hpp" |
| #include "managed_store.hpp" |
| #include "managed_store_types.hpp" |
| #include "query.hpp" |
| #include "registries/privilege_registry.hpp" |
| #include "utils/dbus_utils.hpp" |
| #include "utils/ip_utils.hpp" |
| #include "utils/json_utils.hpp" |
| |
| #include <boost/container/flat_set.hpp> |
| #include <sdbusplus/asio/property.hpp> |
| |
| #include <array> |
| #include <optional> |
| #include <string_view> |
| #include <utility> |
| |
| #ifdef UNIT_TEST_BUILD |
| #include "test/g3/mock_managed_store.hpp" // NOLINT |
| #endif |
| |
| namespace redfish |
| { |
| |
| /** |
| * @brief Retrieves hypervisor state properties over dbus |
| * |
| * The hypervisor state object is optional so this function will only set the |
| * state variables if the object is found |
| * |
| * @param[in] aResp Shared pointer for completing asynchronous calls. |
| * |
| * @return None. |
| */ |
| inline void getHypervisorState(const std::shared_ptr<bmcweb::AsyncResp>& aResp) |
| { |
| BMCWEB_LOG_DEBUG << "Get hypervisor state information."; |
| managedStore::ManagedObjectStoreContext requestContext(aResp); |
| dbus_utils::getProperty<std::string>( |
| "xyz.openbmc_project.State.Hypervisor", |
| "/xyz/openbmc_project/state/hypervisor0", |
| "xyz.openbmc_project.State.Host", "CurrentHostState", requestContext, |
| [aResp](const boost::system::error_code& ec, |
| const std::string& hostState) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; |
| // This is an optional D-Bus object so just return if |
| // error occurs |
| return; |
| } |
| |
| BMCWEB_LOG_DEBUG << "Hypervisor state: " << hostState; |
| // Verify Host State |
| if (hostState == "xyz.openbmc_project.State.Host.HostState.Running") |
| { |
| aResp->res.jsonValue["PowerState"] = "On"; |
| aResp->res.jsonValue["Status"]["State"] = "Enabled"; |
| } |
| else if (hostState == "xyz.openbmc_project.State.Host.HostState." |
| "Quiesced") |
| { |
| aResp->res.jsonValue["PowerState"] = "On"; |
| aResp->res.jsonValue["Status"]["State"] = "Quiesced"; |
| } |
| else if (hostState == "xyz.openbmc_project.State.Host.HostState." |
| "Standby") |
| { |
| aResp->res.jsonValue["PowerState"] = "On"; |
| aResp->res.jsonValue["Status"]["State"] = "StandbyOffline"; |
| } |
| else if (hostState == "xyz.openbmc_project.State.Host.HostState." |
| "TransitioningToRunning") |
| { |
| aResp->res.jsonValue["PowerState"] = "PoweringOn"; |
| aResp->res.jsonValue["Status"]["State"] = "Starting"; |
| } |
| else if (hostState == "xyz.openbmc_project.State.Host.HostState." |
| "TransitioningToOff") |
| { |
| aResp->res.jsonValue["PowerState"] = "PoweringOff"; |
| aResp->res.jsonValue["Status"]["State"] = "Enabled"; |
| } |
| else if (hostState == "xyz.openbmc_project.State.Host.HostState.Off") |
| { |
| aResp->res.jsonValue["PowerState"] = "Off"; |
| aResp->res.jsonValue["Status"]["State"] = "Disabled"; |
| } |
| else |
| { |
| messages::internalError(aResp->res); |
| return; |
| } |
| }); |
| } |
| |
| /** |
| * @brief Populate Actions if any are valid for hypervisor object |
| * |
| * The hypervisor state object is optional so this function will only set the |
| * Action if the object is found |
| * |
| * @param[in] aResp Shared pointer for completing asynchronous calls. |
| * |
| * @return None. |
| */ |
| inline void |
| getHypervisorActions(const std::shared_ptr<bmcweb::AsyncResp>& aResp) |
| { |
| BMCWEB_LOG_DEBUG << "Get hypervisor actions."; |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.State.Host"}; |
| managedStore::ManagedObjectStoreContext requestContext(aResp); |
| managedStore::GetManagedObjectStore()->getDbusObject( |
| "/xyz/openbmc_project/state/hypervisor0", interfaces, requestContext, |
| [aResp]( |
| const boost::system::error_code& ec, |
| const std::vector<std::pair<std::string, std::vector<std::string>>>& |
| objInfo) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; |
| // This is an optional D-Bus object so just return if |
| // error occurs |
| return; |
| } |
| |
| if (objInfo.empty()) |
| { |
| // As noted above, this is an optional interface so just return |
| // if there is no instance found |
| return; |
| } |
| |
| if (objInfo.size() > 1) |
| { |
| // More then one hypervisor object is not supported and is an |
| // error |
| messages::internalError(aResp->res); |
| return; |
| } |
| |
| // Object present so system support limited ComputerSystem Action |
| nlohmann::json& reset = |
| aResp->res.jsonValue["Actions"]["#ComputerSystem.Reset"]; |
| reset["target"] = |
| "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset"; |
| reset["@Redfish.ActionInfo"] = |
| "/redfish/v1/Systems/hypervisor/ResetActionInfo"; |
| }); |
| } |
| |
| inline bool extractHypervisorInterfaceData( |
| const std::string& ethIfaceId, |
| const dbus::utility::ManagedObjectType& dbusData, |
| EthernetInterfaceData& ethData, |
| boost::container::flat_set<IPv4AddressData>& ipv4Config) |
| { |
| bool idFound = false; |
| for (const auto& objpath : dbusData) |
| { |
| for (const auto& ifacePair : objpath.second) |
| { |
| if (objpath.first == |
| "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId) |
| { |
| idFound = true; |
| if (ifacePair.first == "xyz.openbmc_project.Network.MACAddress") |
| { |
| for (const auto& propertyPair : ifacePair.second) |
| { |
| if (propertyPair.first == "MACAddress") |
| { |
| const std::string* mac = |
| std::get_if<std::string>(&propertyPair.second); |
| if (mac != nullptr) |
| { |
| ethData.macAddress = *mac; |
| } |
| } |
| } |
| } |
| else if (ifacePair.first == |
| "xyz.openbmc_project.Network.EthernetInterface") |
| { |
| for (const auto& propertyPair : ifacePair.second) |
| { |
| if (propertyPair.first == "DHCPEnabled") |
| { |
| const std::string* dhcp = |
| std::get_if<std::string>(&propertyPair.second); |
| if (dhcp != nullptr) |
| { |
| ethData.dhcpEnabled = *dhcp; |
| break; // Interested on only "DHCPEnabled". |
| // Stop parsing since we got the |
| // "DHCPEnabled" value. |
| } |
| } |
| } |
| } |
| } |
| if (objpath.first == "/xyz/openbmc_project/network/hypervisor/" + |
| ethIfaceId + "/ipv4/addr0") |
| { |
| std::pair<boost::container::flat_set<IPv4AddressData>::iterator, |
| bool> |
| it = ipv4Config.insert(IPv4AddressData{}); |
| IPv4AddressData& ipv4Address = *it.first; |
| if (ifacePair.first == "xyz.openbmc_project.Object.Enable") |
| { |
| for (const auto& property : ifacePair.second) |
| { |
| if (property.first == "Enabled") |
| { |
| const bool* intfEnable = |
| std::get_if<bool>(&property.second); |
| if (intfEnable != nullptr) |
| { |
| ipv4Address.isActive = *intfEnable; |
| break; |
| } |
| } |
| } |
| } |
| if (ifacePair.first == "xyz.openbmc_project.Network.IP") |
| { |
| for (const auto& property : ifacePair.second) |
| { |
| if (property.first == "Address") |
| { |
| const std::string* address = |
| std::get_if<std::string>(&property.second); |
| if (address != nullptr) |
| { |
| ipv4Address.address = *address; |
| } |
| } |
| else if (property.first == "Origin") |
| { |
| const std::string* origin = |
| std::get_if<std::string>(&property.second); |
| if (origin != nullptr) |
| { |
| ipv4Address.origin = |
| translateAddressOriginDbusToRedfish(*origin, |
| true); |
| } |
| } |
| else if (property.first == "PrefixLength") |
| { |
| const uint8_t* mask = |
| std::get_if<uint8_t>(&property.second); |
| if (mask != nullptr) |
| { |
| // convert it to the string |
| ipv4Address.netmask = getNetmask(*mask); |
| } |
| } |
| else if (property.first == "Type" || |
| property.first == "Gateway") |
| { |
| // Type & Gateway is not used |
| continue; |
| } |
| else |
| { |
| BMCWEB_LOG_ERROR |
| << "Got extra property: " << property.first |
| << " on the " << objpath.first.str << " object"; |
| } |
| } |
| } |
| } |
| if (objpath.first == "/xyz/openbmc_project/network/hypervisor") |
| { |
| // System configuration shows up in the global namespace, so no |
| // need to check eth number |
| if (ifacePair.first == |
| "xyz.openbmc_project.Network.SystemConfiguration") |
| { |
| for (const auto& propertyPair : ifacePair.second) |
| { |
| if (propertyPair.first == "HostName") |
| { |
| const std::string* hostName = |
| std::get_if<std::string>(&propertyPair.second); |
| if (hostName != nullptr) |
| { |
| ethData.hostName = *hostName; |
| } |
| } |
| else if (propertyPair.first == "DefaultGateway") |
| { |
| const std::string* defaultGateway = |
| std::get_if<std::string>(&propertyPair.second); |
| if (defaultGateway != nullptr) |
| { |
| ethData.defaultGateway = *defaultGateway; |
| } |
| } |
| } |
| } |
| } |
| } |
| } |
| return idFound; |
| } |
| /** |
| * Function that retrieves all properties for given Hypervisor Ethernet |
| * Interface Object from Settings Manager |
| * @param ethIfaceId Hypervisor ethernet interface id to query on DBus |
| * @param callback a function that shall be called to convert Dbus output |
| * into JSON |
| */ |
| template <typename CallbackFunc> |
| void getHypervisorIfaceData( |
| const std::string& ethIfaceId, |
| const managedStore::ManagedObjectStoreContext& context, |
| CallbackFunc&& callback) |
| { |
| managedStore::GetManagedObjectStore()->getManagedObjectsWithContext( |
| "xyz.openbmc_project.Settings", {"/"}, context, |
| [ethIfaceId{std::string{ethIfaceId}}, |
| callback{std::forward<CallbackFunc>(callback)}]( |
| const boost::system::error_code& error, |
| const dbus::utility::ManagedObjectType& resp) { |
| EthernetInterfaceData ethData{}; |
| boost::container::flat_set<IPv4AddressData> ipv4Data; |
| if (error) |
| { |
| callback(false, ethData, ipv4Data); |
| return; |
| } |
| |
| bool found = |
| extractHypervisorInterfaceData(ethIfaceId, resp, ethData, ipv4Data); |
| if (!found) |
| { |
| BMCWEB_LOG_INFO << "Hypervisor Interface not found"; |
| } |
| callback(found, ethData, ipv4Data); |
| }); |
| } |
| |
| /** |
| * @brief Sets the Hypervisor Interface IPAddress DBUS |
| * |
| * @param[in] aResp Shared pointer for generating response message. |
| * @param[in] ipv4Address Address from the incoming request |
| * @param[in] ethIfaceId Hypervisor Interface Id |
| * |
| * @return None. |
| */ |
| inline void |
| setHypervisorIPv4Address(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& ethIfaceId, |
| const std::string& ipv4Address) |
| { |
| BMCWEB_LOG_DEBUG << "Setting the Hypervisor IPaddress : " << ipv4Address |
| << " on Iface: " << ethIfaceId; |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| aResp->strand_, |
| [aResp](const boost::system::error_code& ec) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "DBUS response error " << ec; |
| return; |
| } |
| BMCWEB_LOG_DEBUG << "Hypervisor IPaddress is Set"; |
| }, |
| "xyz.openbmc_project.Settings", |
| "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", |
| "org.freedesktop.DBus.Properties", "Set", |
| "xyz.openbmc_project.Network.IP", "Address", |
| dbus::utility::DbusVariantType(ipv4Address)); |
| } |
| |
| /** |
| * @brief Sets the Hypervisor Interface SubnetMask DBUS |
| * |
| * @param[in] aResp Shared pointer for generating response message. |
| * @param[in] subnet SubnetMask from the incoming request |
| * @param[in] ethIfaceId Hypervisor Interface Id |
| * |
| * @return None. |
| */ |
| inline void |
| setHypervisorIPv4Subnet(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& ethIfaceId, const uint8_t subnet) |
| { |
| BMCWEB_LOG_DEBUG << "Setting the Hypervisor subnet : " << subnet |
| << " on Iface: " << ethIfaceId; |
| |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| aResp->strand_, |
| [aResp](const boost::system::error_code& ec) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "DBUS response error " << ec; |
| return; |
| } |
| BMCWEB_LOG_DEBUG << "SubnetMask is Set"; |
| }, |
| "xyz.openbmc_project.Settings", |
| "/xyz/openbmc_project/network/hypervisor/" + ethIfaceId + "/ipv4/addr0", |
| "org.freedesktop.DBus.Properties", "Set", |
| "xyz.openbmc_project.Network.IP", "PrefixLength", |
| dbus::utility::DbusVariantType(subnet)); |
| } |
| |
| /** |
| * @brief Sets the Hypervisor Interface Gateway DBUS |
| * |
| * @param[in] aResp Shared pointer for generating response message. |
| * @param[in] gateway Gateway from the incoming request |
| * @param[in] ethIfaceId Hypervisor Interface Id |
| * |
| * @return None. |
| */ |
| inline void |
| setHypervisorIPv4Gateway(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& gateway) |
| { |
| BMCWEB_LOG_DEBUG |
| << "Setting the DefaultGateway to the last configured gateway"; |
| |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| aResp->strand_, |
| [aResp](const boost::system::error_code& ec) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "DBUS response error " << ec; |
| return; |
| } |
| BMCWEB_LOG_DEBUG << "Default Gateway is Set"; |
| }, |
| "xyz.openbmc_project.Settings", |
| "/xyz/openbmc_project/network/hypervisor", |
| "org.freedesktop.DBus.Properties", "Set", |
| "xyz.openbmc_project.Network.SystemConfiguration", "DefaultGateway", |
| dbus::utility::DbusVariantType(gateway)); |
| } |
| |
| /** |
| * @brief Creates a static IPv4 entry |
| * |
| * @param[in] ifaceId Id of interface upon which to create the IPv4 entry |
| * @param[in] prefixLength IPv4 prefix syntax for the subnet mask |
| * @param[in] gateway IPv4 address of this interfaces gateway |
| * @param[in] address IPv4 address to assign to this interface |
| * @param[io] asyncResp Response object that will be returned to client |
| * |
| * @return None |
| */ |
| 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) |
| { |
| setHypervisorIPv4Address(asyncResp, ifaceId, address); |
| setHypervisorIPv4Gateway(asyncResp, gateway); |
| setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); |
| } |
| |
| /** |
| * @brief Deletes given IPv4 interface |
| * |
| * @param[in] ifaceId Id of interface whose IP should be deleted |
| * @param[io] asyncResp Response object that will be returned to client |
| * |
| * @return None |
| */ |
| inline void |
| deleteHypervisorIPv4(const std::string& ifaceId, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| std::string address = "0.0.0.0"; |
| std::string gateway = "0.0.0.0"; |
| const uint8_t prefixLength = 0; |
| setHypervisorIPv4Address(asyncResp, ifaceId, address); |
| setHypervisorIPv4Gateway(asyncResp, gateway); |
| setHypervisorIPv4Subnet(asyncResp, ifaceId, prefixLength); |
| } |
| |
| inline void parseInterfaceData( |
| nlohmann::json& jsonResponse, const std::string& ifaceId, |
| const EthernetInterfaceData& ethData, |
| const boost::container::flat_set<IPv4AddressData>& ipv4Data) |
| { |
| jsonResponse["Id"] = ifaceId; |
| jsonResponse["@odata.id"] = |
| crow::utility::urlFromPieces("redfish", "v1", "Systems", "hypervisor", |
| "EthernetInterfaces", ifaceId); |
| jsonResponse["InterfaceEnabled"] = true; |
| jsonResponse["MACAddress"] = ethData.macAddress; |
| |
| jsonResponse["HostName"] = ethData.hostName; |
| jsonResponse["DHCPv4"]["DHCPEnabled"] = |
| translateDhcpEnabledToBool(ethData.dhcpEnabled, true); |
| |
| nlohmann::json& ipv4Array = jsonResponse["IPv4Addresses"]; |
| nlohmann::json& ipv4StaticArray = jsonResponse["IPv4StaticAddresses"]; |
| ipv4Array = nlohmann::json::array(); |
| ipv4StaticArray = nlohmann::json::array(); |
| for (const auto& ipv4Config : ipv4Data) |
| { |
| if (ipv4Config.isActive) |
| { |
| nlohmann::json::object_t ipv4; |
| ipv4["AddressOrigin"] = ipv4Config.origin; |
| ipv4["SubnetMask"] = ipv4Config.netmask; |
| ipv4["Address"] = ipv4Config.address; |
| ipv4["Gateway"] = ethData.defaultGateway; |
| |
| if (ipv4Config.origin == "Static") |
| { |
| ipv4StaticArray.emplace_back(ipv4); |
| } |
| ipv4Array.emplace_back(std::move(ipv4)); |
| } |
| } |
| } |
| |
| inline void setDHCPEnabled(const std::string& ifaceId, |
| const bool& ipv4DHCPEnabled, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| const std::string dhcp = getDhcpEnabledEnumeration(ipv4DHCPEnabled, false); |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| asyncResp->strand_, |
| [asyncResp](const boost::system::error_code& ec) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| }, |
| "xyz.openbmc_project.Settings", |
| "/xyz/openbmc_project/network/hypervisor/" + ifaceId, |
| "org.freedesktop.DBus.Properties", "Set", |
| "xyz.openbmc_project.Network.EthernetInterface", "DHCPEnabled", |
| dbus::utility::DbusVariantType{dhcp}); |
| |
| // Set the IPv4 address origin to the DHCP / Static as per the new value |
| // of the DHCPEnabled property |
| std::string origin; |
| if (!ipv4DHCPEnabled) |
| { |
| origin = "xyz.openbmc_project.Network.IP.AddressOrigin.Static"; |
| } |
| else |
| { |
| // DHCPEnabled is set to true. Delete the current IPv4 settings |
| // to receive the new values from DHCP server. |
| deleteHypervisorIPv4(ifaceId, asyncResp); |
| origin = "xyz.openbmc_project.Network.IP.AddressOrigin.DHCP"; |
| } |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| asyncResp->strand_, |
| [asyncResp](const boost::system::error_code& ec) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "DBUS response error " << ec; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| BMCWEB_LOG_DEBUG << "Hypervisor IPaddress Origin is Set"; |
| }, |
| "xyz.openbmc_project.Settings", |
| "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", |
| "org.freedesktop.DBus.Properties", "Set", |
| "xyz.openbmc_project.Network.IP", "Origin", |
| dbus::utility::DbusVariantType(origin)); |
| } |
| |
| inline void handleHypervisorIPv4StaticPatch( |
| const std::string& ifaceId, const nlohmann::json& input, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| if ((!input.is_array()) || input.empty()) |
| { |
| messages::propertyValueTypeError(asyncResp->res, input.dump(), |
| "IPv4StaticAddresses"); |
| return; |
| } |
| |
| // Hypervisor considers the first IP address in the array list |
| // as the Hypervisor's virtual management interface supports single IPv4 |
| // address |
| const nlohmann::json& thisJson = input[0]; |
| |
| if (!thisJson.is_null() && !thisJson.empty()) |
| { |
| // For the error string |
| std::string pathString = "IPv4StaticAddresses/1"; |
| std::optional<std::string> address; |
| std::optional<std::string> subnetMask; |
| std::optional<std::string> gateway; |
| nlohmann::json thisJsonCopy = thisJson; |
| if (!json_util::readJson(thisJsonCopy, asyncResp->res, "Address", |
| address, "SubnetMask", subnetMask, "Gateway", |
| gateway)) |
| { |
| messages::propertyValueFormatError( |
| asyncResp->res, |
| thisJson.dump(2, ' ', true, |
| nlohmann::json::error_handler_t::replace), |
| pathString); |
| return; |
| } |
| |
| uint8_t prefixLength = 0; |
| bool errorInEntry = false; |
| if (address) |
| { |
| if (!ip_util::ipv4VerifyIpAndGetBitcount(*address)) |
| { |
| messages::propertyValueFormatError(asyncResp->res, *address, |
| pathString + "/Address"); |
| errorInEntry = true; |
| } |
| } |
| else |
| { |
| messages::propertyMissing(asyncResp->res, pathString + "/Address"); |
| errorInEntry = true; |
| } |
| |
| if (subnetMask) |
| { |
| if (!ip_util::ipv4VerifyIpAndGetBitcount(*subnetMask, |
| &prefixLength)) |
| { |
| messages::propertyValueFormatError(asyncResp->res, *subnetMask, |
| pathString + "/SubnetMask"); |
| errorInEntry = true; |
| } |
| } |
| else |
| { |
| messages::propertyMissing(asyncResp->res, |
| pathString + "/SubnetMask"); |
| errorInEntry = true; |
| } |
| |
| if (gateway) |
| { |
| if (!ip_util::ipv4VerifyIpAndGetBitcount(*gateway)) |
| { |
| messages::propertyValueFormatError(asyncResp->res, *gateway, |
| pathString + "/Gateway"); |
| errorInEntry = true; |
| } |
| } |
| else |
| { |
| messages::propertyMissing(asyncResp->res, pathString + "/Gateway"); |
| errorInEntry = true; |
| } |
| |
| if (errorInEntry) |
| { |
| return; |
| } |
| |
| BMCWEB_LOG_DEBUG << "Calling createHypervisorIPv4 on : " << ifaceId |
| << "," << *address; |
| createHypervisorIPv4(ifaceId, prefixLength, *gateway, *address, |
| asyncResp); |
| // Set the DHCPEnabled to false since the Static IPv4 is set |
| setDHCPEnabled(ifaceId, false, asyncResp); |
| } |
| else |
| { |
| if (thisJson.is_null()) |
| { |
| deleteHypervisorIPv4(ifaceId, asyncResp); |
| } |
| } |
| } |
| |
| inline void handleHypervisorHostnamePatch( |
| const std::string& hostName, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| if (!isHostnameValid(hostName)) |
| { |
| messages::propertyValueFormatError(asyncResp->res, hostName, |
| "HostName"); |
| return; |
| } |
| |
| asyncResp->res.jsonValue["HostName"] = hostName; |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| asyncResp->strand_, |
| [asyncResp](const boost::system::error_code& ec) { |
| if (ec) |
| { |
| messages::internalError(asyncResp->res); |
| } |
| }, |
| "xyz.openbmc_project.Settings", |
| "/xyz/openbmc_project/network/hypervisor", |
| "org.freedesktop.DBus.Properties", "Set", |
| "xyz.openbmc_project.Network.SystemConfiguration", "HostName", |
| dbus::utility::DbusVariantType(hostName)); |
| } |
| |
| inline void |
| setIPv4InterfaceEnabled(const std::string& ifaceId, const bool& isActive, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| asyncResp->strand_, |
| [asyncResp](const boost::system::error_code& ec) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| }, |
| "xyz.openbmc_project.Settings", |
| "/xyz/openbmc_project/network/hypervisor/" + ifaceId + "/ipv4/addr0", |
| "org.freedesktop.DBus.Properties", "Set", |
| "xyz.openbmc_project.Object.Enable", "Enabled", |
| dbus::utility::DbusVariantType(isActive)); |
| } |
| |
| inline void handleHypervisorEthernetInterfaceCollectionGet( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.Network.EthernetInterface"}; |
| |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::GetManagedObjectStore()->getSubTreePaths( |
| "/xyz/openbmc_project/network/hypervisor", 0, interfaces, |
| requestContext, |
| [asyncResp]( |
| const boost::system::error_code& error, |
| const dbus::utility::MapperGetSubTreePathsResponse& ifaceList) { |
| if (error) |
| { |
| messages::resourceNotFound(asyncResp->res, "System", "hypervisor"); |
| return; |
| } |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#EthernetInterfaceCollection." |
| "EthernetInterfaceCollection"; |
| asyncResp->res.jsonValue["@odata.id"] = |
| "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; |
| asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet " |
| "Interface Collection"; |
| asyncResp->res.jsonValue["Description"] = |
| "Collection of Virtual Management " |
| "Interfaces for the hypervisor"; |
| |
| nlohmann::json& ifaceArray = asyncResp->res.jsonValue["Members"]; |
| ifaceArray = nlohmann::json::array(); |
| for (const std::string& iface : ifaceList) |
| { |
| sdbusplus::message::object_path path(iface); |
| std::string name = path.filename(); |
| if (name.empty()) |
| { |
| continue; |
| } |
| nlohmann::json::object_t ethIface; |
| ethIface["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Systems", "hypervisor", "EthernetInterfaces", |
| name); |
| ifaceArray.emplace_back(std::move(ethIface)); |
| } |
| asyncResp->res.jsonValue["Members@odata.count"] = ifaceArray.size(); |
| }); |
| } |
| |
| inline void handleHypervisorEthernetInterfaceGet( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, const std::string& id) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| getHypervisorIfaceData( |
| id, context, |
| [asyncResp, ifaceId{std::string(id)}]( |
| const bool& success, const EthernetInterfaceData& ethData, |
| const boost::container::flat_set<IPv4AddressData>& ipv4Data) { |
| if (!success) |
| { |
| messages::resourceNotFound(asyncResp->res, "EthernetInterface", |
| ifaceId); |
| return; |
| } |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#EthernetInterface.v1_6_0.EthernetInterface"; |
| asyncResp->res.jsonValue["Name"] = "Hypervisor Ethernet Interface"; |
| asyncResp->res.jsonValue["Description"] = |
| "Hypervisor's Virtual Management Ethernet Interface"; |
| parseInterfaceData(asyncResp->res.jsonValue, ifaceId, ethData, |
| ipv4Data); |
| }); |
| } |
| |
| inline void handleHypervisorSystemGet( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| dbus_utils::getProperty<std::string>( |
| "xyz.openbmc_project.Settings", |
| "/xyz/openbmc_project/network/hypervisor", |
| "xyz.openbmc_project.Network.SystemConfiguration", "HostName", |
| requestContext, |
| [asyncResp](const boost::system::error_code& ec, |
| const std::string& /*hostName*/) { |
| if (ec) |
| { |
| messages::resourceNotFound(asyncResp->res, "System", "hypervisor"); |
| return; |
| } |
| BMCWEB_LOG_DEBUG << "Hypervisor is available"; |
| |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#ComputerSystem.v1_6_0.ComputerSystem"; |
| asyncResp->res.jsonValue["@odata.id"] = |
| "/redfish/v1/Systems/hypervisor"; |
| asyncResp->res.jsonValue["Description"] = "Hypervisor"; |
| asyncResp->res.jsonValue["Name"] = "Hypervisor"; |
| asyncResp->res.jsonValue["Id"] = "hypervisor"; |
| asyncResp->res.jsonValue["SystemType"] = "OS"; |
| nlohmann::json::array_t managedBy; |
| nlohmann::json::object_t manager; |
| manager["@odata.id"] = "/redfish/v1/Managers/bmc"; |
| managedBy.emplace_back(std::move(manager)); |
| asyncResp->res.jsonValue["Links"]["ManagedBy"] = std::move(managedBy); |
| asyncResp->res.jsonValue["EthernetInterfaces"]["@odata.id"] = |
| "/redfish/v1/Systems/hypervisor/EthernetInterfaces"; |
| getHypervisorState(asyncResp); |
| getHypervisorActions(asyncResp); |
| // TODO: Add "SystemType" : "hypervisor" |
| }); |
| } |
| |
| inline void handleHypervisorEthernetInterfacePatch( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& ifaceId) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| std::optional<std::string> hostName; |
| std::optional<std::vector<nlohmann::json>> ipv4StaticAddresses; |
| std::optional<nlohmann::json> ipv4Addresses; |
| std::optional<nlohmann::json> dhcpv4; |
| std::optional<bool> ipv4DHCPEnabled; |
| |
| if (!json_util::readJsonPatch(req, asyncResp->res, "HostName", hostName, |
| "IPv4StaticAddresses", ipv4StaticAddresses, |
| "IPv4Addresses", ipv4Addresses, "DHCPv4", |
| dhcpv4)) |
| { |
| return; |
| } |
| |
| if (ipv4Addresses) |
| { |
| messages::propertyNotWritable(asyncResp->res, "IPv4Addresses"); |
| return; |
| } |
| |
| if (dhcpv4) |
| { |
| if (!json_util::readJson(*dhcpv4, asyncResp->res, "DHCPEnabled", |
| ipv4DHCPEnabled)) |
| { |
| return; |
| } |
| } |
| managedStore::ManagedObjectStoreContext context(asyncResp); |
| getHypervisorIfaceData( |
| ifaceId, context, |
| [asyncResp, ifaceId, hostName = std::move(hostName), |
| ipv4StaticAddresses = std::move(ipv4StaticAddresses), ipv4DHCPEnabled, |
| dhcpv4 = std::move(dhcpv4)]( |
| const bool& success, const EthernetInterfaceData& ethData, |
| const boost::container::flat_set<IPv4AddressData>&) { |
| if (!success) |
| { |
| messages::resourceNotFound(asyncResp->res, "EthernetInterface", |
| ifaceId); |
| return; |
| } |
| |
| if (ipv4StaticAddresses) |
| { |
| const nlohmann::json& ipv4Static = *ipv4StaticAddresses; |
| if (ipv4Static.begin() == ipv4Static.end()) |
| { |
| messages::propertyValueTypeError( |
| asyncResp->res, |
| ipv4Static.dump(2, ' ', true, |
| nlohmann::json::error_handler_t::replace), |
| "IPv4StaticAddresses"); |
| return; |
| } |
| |
| // One and only one hypervisor instance supported |
| if (ipv4Static.size() != 1) |
| { |
| messages::propertyValueFormatError( |
| asyncResp->res, |
| ipv4Static.dump(2, ' ', true, |
| nlohmann::json::error_handler_t::replace), |
| "IPv4StaticAddresses"); |
| return; |
| } |
| |
| const nlohmann::json& ipv4Json = ipv4Static[0]; |
| // Check if the param is 'null'. If its null, it means |
| // that user wants to delete the IP address. Deleting |
| // the IP address is allowed only if its statically |
| // configured. Deleting the address originated from DHCP |
| // is not allowed. |
| if ((ipv4Json.is_null()) && |
| (translateDhcpEnabledToBool(ethData.dhcpEnabled, true))) |
| { |
| BMCWEB_LOG_INFO << "Ignoring the delete on ipv4StaticAddresses " |
| "as the interface is DHCP enabled"; |
| } |
| else |
| { |
| handleHypervisorIPv4StaticPatch(ifaceId, ipv4Static, asyncResp); |
| } |
| } |
| |
| if (hostName) |
| { |
| handleHypervisorHostnamePatch(*hostName, asyncResp); |
| } |
| |
| if (dhcpv4) |
| { |
| setDHCPEnabled(ifaceId, *ipv4DHCPEnabled, asyncResp); |
| } |
| |
| // Set this interface to disabled/inactive. This will be set |
| // to enabled/active by the pldm once the hypervisor |
| // consumes the updated settings from the user. |
| setIPv4InterfaceEnabled(ifaceId, false, asyncResp); |
| }); |
| asyncResp->res.result(boost::beast::http::status::accepted); |
| } |
| |
| inline void handleHypervisorResetActionGet( |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| // Only return action info if hypervisor D-Bus object present |
| constexpr std::array<std::string_view, 1> interfaces = { |
| "xyz.openbmc_project.State.Host"}; |
| managedStore::ManagedObjectStoreContext requestContext(asyncResp); |
| managedStore::GetManagedObjectStore()->getDbusObject( |
| "/xyz/openbmc_project/state/hypervisor0", interfaces, requestContext, |
| [asyncResp]( |
| const boost::system::error_code& ec, |
| const std::vector<std::pair<std::string, std::vector<std::string>>>& |
| objInfo) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error " << ec; |
| |
| // No hypervisor objects found by mapper |
| if (ec.value() == boost::system::errc::io_error) |
| { |
| messages::resourceNotFound(asyncResp->res, "hypervisor", |
| "ResetActionInfo"); |
| return; |
| } |
| |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| // One and only one hypervisor instance supported |
| if (objInfo.size() != 1) |
| { |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| |
| // The hypervisor object only support the ability to |
| // turn On The system object Action should be utilized |
| // for other operations |
| |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#ActionInfo.v1_1_2.ActionInfo"; |
| asyncResp->res.jsonValue["@odata.id"] = |
| "/redfish/v1/Systems/hypervisor/ResetActionInfo"; |
| asyncResp->res.jsonValue["Name"] = "Reset Action Info"; |
| asyncResp->res.jsonValue["Id"] = "ResetActionInfo"; |
| nlohmann::json::array_t parameters; |
| nlohmann::json::object_t parameter; |
| parameter["Name"] = "ResetType"; |
| parameter["Required"] = true; |
| parameter["DataType"] = "String"; |
| nlohmann::json::array_t allowed; |
| allowed.emplace_back("On"); |
| parameter["AllowableValues"] = std::move(allowed); |
| parameters.emplace_back(std::move(parameter)); |
| asyncResp->res.jsonValue["Parameters"] = std::move(parameters); |
| }); |
| } |
| |
| inline void handleHypervisorSystemResetPost( |
| App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| std::optional<std::string> resetType; |
| if (!json_util::readJsonAction(req, asyncResp->res, "ResetType", resetType)) |
| { |
| // readJson adds appropriate error to response |
| return; |
| } |
| |
| if (!resetType) |
| { |
| messages::actionParameterMissing(asyncResp->res, "ComputerSystem.Reset", |
| "ResetType"); |
| return; |
| } |
| |
| // Hypervisor object only support On operation |
| if (resetType != "On") |
| { |
| messages::propertyValueNotInList(asyncResp->res, *resetType, |
| "ResetType"); |
| return; |
| } |
| |
| std::string command = "xyz.openbmc_project.State.Host.Transition.On"; |
| |
| managedStore::GetManagedObjectStore()->PostDbusCallToIoContextThreadSafe( |
| asyncResp->strand_, |
| [asyncResp, resetType](const boost::system::error_code& ec) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "D-Bus responses error: " << ec; |
| if (ec.value() == boost::asio::error::invalid_argument) |
| { |
| messages::actionParameterNotSupported(asyncResp->res, |
| *resetType, "Reset"); |
| return; |
| } |
| |
| if (ec.value() == boost::asio::error::host_unreachable) |
| { |
| messages::resourceNotFound(asyncResp->res, "Actions", "Reset"); |
| return; |
| } |
| |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| messages::success(asyncResp->res); |
| }, |
| "xyz.openbmc_project.State.Hypervisor", |
| "/xyz/openbmc_project/state/hypervisor0", |
| "org.freedesktop.DBus.Properties", "Set", |
| "xyz.openbmc_project.State.Host", "RequestedHostTransition", |
| dbus::utility::DbusVariantType{std::move(command)}); |
| } |
| |
| inline void requestRoutesHypervisorSystems(App& app) |
| { |
| /** |
| * HypervisorInterfaceCollection class to handle the GET and PATCH on |
| * Hypervisor Interface |
| */ |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Systems/hypervisor/EthernetInterfaces/") |
| .privileges(redfish::privileges::getEthernetInterfaceCollection) |
| .methods(boost::beast::http::verb::get)(std::bind_front( |
| handleHypervisorEthernetInterfaceCollectionGet, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, |
| "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") |
| .privileges(redfish::privileges::getEthernetInterface) |
| .methods(boost::beast::http::verb::get)(std::bind_front( |
| handleHypervisorEthernetInterfaceGet, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, |
| "/redfish/v1/Systems/hypervisor/EthernetInterfaces/<str>/") |
| .privileges(redfish::privileges::patchEthernetInterface) |
| .methods(boost::beast::http::verb::patch)(std::bind_front( |
| handleHypervisorEthernetInterfacePatch, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, |
| "/redfish/v1/Systems/hypervisor/Actions/ComputerSystem.Reset/") |
| .privileges(redfish::privileges::postComputerSystem) |
| .methods(boost::beast::http::verb::post)( |
| std::bind_front(handleHypervisorSystemResetPost, std::ref(app))); |
| } |
| } // namespace redfish |