blob: abdfadb3fcbd4fb99b26db74e7a3beec82f184a4 [file] [log] [blame]
#ifndef THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_HYPERVISOR_SYSTEM_H_
#define THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_HYPERVISOR_SYSTEM_H_
#include <array>
#include <cstdint>
#include <functional>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include <utility>
#include <variant>
#include <vector>
#include "boost/container/flat_set.hpp" // NOLINT
#include "app.hpp"
#include "http_request.hpp"
#include "logging.hpp"
#include "utility.hpp"
#include "async_resp.hpp"
#include "dbus_utility.hpp"
#include "error_messages.hpp"
#include "query.hpp"
#include "registries/privilege_registry.hpp"
#include "dbus_utils.hpp"
#include "ip_utils.hpp"
#include "json_utils.hpp"
#include "ethernet.hpp"
#include <nlohmann/json.hpp>
#include "managed_store.hpp"
#include "managed_store_types.hpp"
#include "sdbusplus/message/native_types.hpp"
#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
#endif // THIRD_PARTY_GBMCWEB_REDFISH_CORE_LIB_HYPERVISOR_SYSTEM_H_