blob: f11a7f91a04e5cb60deb0dd300602a494c03cff0 [file] [log] [blame]
/*
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
*/
#pragma once
#include "app.hpp"
#include "dbus_utility.hpp"
#include "health.hpp"
#include "human_sort.hpp"
#include "managed_store_types.hpp"
#include "openbmc_dbus_rest.hpp"
#include "query.hpp"
#include "redfish_util.hpp"
#include "registries/privilege_registry.hpp"
#include "utils/dbus_utils.hpp"
#include <boost/system/error_code.hpp>
#include <sdbusplus/asio/property.hpp>
#include <sdbusplus/unpack_properties.hpp>
#include <utils/location_utils.hpp>
#include "managed_store.hpp"
#ifdef UNIT_TEST_BUILD
#include "test/g3/mock_managed_store.hpp" // NOLINT
#endif
namespace redfish
{
constexpr char const* networkAdapterInventoryIntf =
"xyz.openbmc_project.Inventory.Item.NetworkAdapter";
constexpr char const* portInventoryIntf =
"xyz.openbmc_project.Inventory.Item.Port";
constexpr char const* inventoryPrefix = "/xyz/openbmc_project/inventory";
#ifdef UNIT_TEST_BUILD
constexpr const char* netSysfsPath = sysfsNetBasePathUT;
#else
constexpr const char* netSysfsPath = "/sys/class/net/";
#endif
// Helper function to read a file and return its contents as a string
static bool readOneLineFile(const std::filesystem::path& filePath,
std::string& content)
{
try
{
std::ifstream ifs(filePath);
if (ifs.is_open() && std::getline(ifs, content))
{
ifs.close();
return true;
}
}
catch (...)
{}
return false;
}
// Helper function for convert a string to uint64 type
// if string is invalid, return default val instead
static uint64_t stringToUint64(const std::string_view str, uint64_t defVal = 0)
{
uint64_t value = defVal;
std::from_chars(str.begin(), str.end(), value);
return value;
}
static std::vector<std::string> sysfsGetEtherDevList()
{
std::vector<std::string> ethList;
// Loop through the directory looking for redfish log files
for (const std::filesystem::directory_entry& dirEnt :
std::filesystem::directory_iterator(netSysfsPath))
{
std::string type;
// ignore non-ethernet device
if (readOneLineFile(dirEnt.path() / "type", type) &&
stringToUint64(type) == 1)
{
ethList.push_back(dirEnt.path().filename());
}
}
return ethList;
}
static std::vector<std::string> sysfsGetEthAddrList(const std::string& portName)
{
std::vector<std::string> addrList;
std::filesystem::path macPath =
std::filesystem::path(netSysfsPath) / portName / "address";
std::string mac;
try
{
std::ifstream ifs(macPath);
if (ifs.is_open())
{
while (std::getline(ifs, mac))
{
addrList.push_back(std::move(mac));
}
}
}
catch (...)
{}
return addrList;
}
// gsys uses ethtool ETHTOOL_GLINK, which checks IFF_UP and carriers.
// LinkStatus follows the same metrics with LinkState
static std::string sysfsGetLinkState(const std::string& portName)
{
std::filesystem::path carrierPath =
std::filesystem::path(netSysfsPath) / portName / "carrier";
std::filesystem::path ifflagPath =
std::filesystem::path(netSysfsPath) / portName / "flags";
std::string flags;
std::string carrier;
if (readOneLineFile(ifflagPath, flags) &&
static_cast<bool>(stringToUint64(flags) | IFF_UP) &&
readOneLineFile(carrierPath, carrier) && stringToUint64(carrier) == 1)
{
return "Enabled";
}
return "Disabled";
}
static double sysfsGetSpeedGbps(const std::string& portName)
{
std::filesystem::path speedPath =
std::filesystem::path(netSysfsPath) / portName / "speed";
std::string speed;
if (readOneLineFile(speedPath, speed))
{
return double(stringToUint64(speed)) / 1000;
}
return 0;
}
static nlohmann::json sysfsGetNetworkPortMetrics(const std::string& portName)
{
nlohmann::json metrics;
std::filesystem::path statPath =
std::filesystem::path(netSysfsPath) / portName / "statistics";
std::string carrierChanges;
std::string rxCrcErrors;
std::string rxErrors;
std::string rxDropped;
std::string rxPackets;
if (readOneLineFile(std::filesystem::path(netSysfsPath) / portName /
"carrier_changes",
carrierChanges))
{
metrics["CarrierChanges"] = stringToUint64(
carrierChanges, std::numeric_limits<uint64_t>::max());
}
if (readOneLineFile(statPath / "rx_crc_errors", rxCrcErrors))
{
metrics["RXCRCErrors"] =
stringToUint64(rxCrcErrors, std::numeric_limits<uint64_t>::max());
}
if (readOneLineFile(statPath / "rx_errors", rxErrors))
{
metrics["RXErrors"] =
stringToUint64(rxErrors, std::numeric_limits<uint64_t>::max());
}
if (readOneLineFile(statPath / "rx_dropped", rxDropped))
{
metrics["ReceivedPacketsDropped"] =
stringToUint64(rxDropped, std::numeric_limits<uint64_t>::max());
}
if (readOneLineFile(statPath / "rx_packets", rxPackets))
{
metrics["ReceivedPackets"] = stringToUint64(rxPackets, 0);
}
return metrics;
}
template <typename Func, typename... Args>
auto handleChassisNetworkAdapterCommon(
crow::App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string chassisId, Func funcSysfs, Func funcDBus, Args... args)
-> decltype(funcSysfs(asyncResp, chassisId, args...),
funcDBus(asyncResp, chassisId, args...))
{
if (!redfish::setUpRedfishRoute(app, req, asyncResp))
{
return;
}
managedStore::ManagedObjectStoreContext context(asyncResp);
dbus_utils::getProperty<std::string>(
"xyz.openbmc_project.EntityManager",
"/xyz/openbmc_project/inventory/system/board/" + chassisId + "/BmcNet",
"xyz.openbmc_project.Configuration.BmcNet", "Name", context,
[asyncResp, chassisId, funcSysfs, funcDBus,
args...](const boost::system::error_code& ec2,
const std::string& bmcNetName) {
if (ec2 || bmcNetName != "BmcNet")
{
// non-bmc chassis
funcDBus(asyncResp, chassisId, args...);
}
else
{
// bmc chassis
funcSysfs(asyncResp, chassisId, args...);
}
});
}
inline void handleChassisNetworkAdapterCollectionGetSysfs(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId)
{
/**
* Functions triggers appropriate requests
*/
asyncResp->res.jsonValue["@odata.type"] =
"#NetworkAdapterCollection.NetworkAdapterCollection";
asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId, "NetworkAdapters");
asyncResp->res.jsonValue["Name"] =
"Network adapter Collection for chassis bmc";
nlohmann::json& members = asyncResp->res.jsonValue["Members"];
members = nlohmann::json::array();
nlohmann::json::object_t member;
member["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId, "NetworkAdapters", "0");
members.push_back(std::move(member));
asyncResp->res.jsonValue["Members@odata.count"] = members.size();
}
// Chassis NetworkAdapters, this URL will show all the NetworkAdapterCollection
// information
inline void handleChassisNetworkAdapterCollectionGetDBus(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId)
{
// mapper call lambda
constexpr std::array<std::string_view, 2> interfaces = {
"xyz.openbmc_project.Inventory.Item.Board",
"xyz.openbmc_project.Inventory.Item.Chassis"};
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::GetManagedObjectStore()->getSubTree(
inventoryPrefix, 0, interfaces, requestContext,
[asyncResp, chassisId, requestContext](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
if (ec)
{
if (ec == boost::system::errc::host_unreachable)
{
messages::resourceNotFound(asyncResp->res, "Chassis",
chassisId);
return;
}
BMCWEB_LOG_ERROR << "Failed to get chassis subtree:" << ec.what();
messages::internalError(asyncResp->res);
return;
}
// Iterate over all retrieved ObjectPaths.
for (const auto& [path, connectionNames] : subtree)
{
sdbusplus::message::object_path objPath(path);
if (objPath.filename() != chassisId)
{
continue;
}
if (connectionNames.empty())
{
BMCWEB_LOG_DEBUG << "Got 0 Connection names";
continue;
}
asyncResp->res.jsonValue["@odata.type"] =
"#NetworkAdapterCollection.NetworkAdapterCollection";
asyncResp->res.jsonValue["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Chassis",
chassisId, "NetworkAdapters");
asyncResp->res.jsonValue["Name"] = "NetworkAdapter Collection";
// Get /network_adapter association
dbus_utils::getAssociationEndPoints(
path + "/network_adapter", requestContext,
[asyncResp,
chassisId](const boost::system::error_code& ec1,
const dbus::utility::MapperEndPoints& resp) {
nlohmann::json& members = asyncResp->res.jsonValue["Members"];
members = nlohmann::json::array();
if (ec1)
{
// "/network_adapter" association is optional for chassis
BMCWEB_LOG_DEBUG
<< "Cannot get chassis NetworkAdapter association:"
<< ec1.what();
asyncResp->res.jsonValue["Members@odata.count"] = 0;
return;
}
std::vector<std::string> leafNames;
for (const auto& nic : resp)
{
sdbusplus::message::object_path nicPath(nic);
leafNames.push_back(nicPath.filename());
}
std::sort(leafNames.begin(), leafNames.end(),
AlphanumLess<std::string>());
for (const auto& leafName : leafNames)
{
nlohmann::json::object_t member;
member["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId,
"NetworkAdapters", leafName);
members.push_back(std::move(member));
}
asyncResp->res.jsonValue["Members@odata.count"] = resp.size();
}); // end association lambda
return; // No need to check other ObjectPaths
} // end Iterate over all retrieved ObjectPaths
// chassisId is not found
messages::resourceNotFound(asyncResp->res, "Chassis", chassisId);
});
}
// Chassis NetworkAdapters, this URL will show all the NetworkAdapterCollection
// information
inline void handleChassisNetworkAdapterCollectionGet(
crow::App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId)
{
handleChassisNetworkAdapterCommon(
app, req, asyncResp, chassisId,
handleChassisNetworkAdapterCollectionGetSysfs,
handleChassisNetworkAdapterCollectionGetDBus);
}
inline void requestRoutesChassisNetworkAdapterCollection(App& app)
{
BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/NetworkAdapters/")
.privileges(redfish::privileges::getNetworkAdapterCollection)
.methods(boost::beast::http::verb::get)(std::bind_front(
handleChassisNetworkAdapterCollectionGet, std::ref(app)));
}
inline void buildNetworkAdapterDBus(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId,
const std::string& service, const std::string& networkAdapterPath)
{
asyncResp->res.jsonValue["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId,
"NetworkAdapters", networkAdapterId);
asyncResp->res.jsonValue["@odata.type"] =
"#NetworkAdapter.v1_9_0.NetworkAdapter";
asyncResp->res.jsonValue["Name"] = networkAdapterId;
asyncResp->res.jsonValue["Id"] = networkAdapterId;
asyncResp->res.jsonValue["Status"]["State"] = "Enabled";
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
dbus_utils::getProperty<std::string>(
service, networkAdapterPath, networkAdapterInventoryIntf,
"StatusHealth", requestContext,
[asyncResp](const boost::system::error_code& ec,
const std::string& statusHealth) {
if (ec)
{
BMCWEB_LOG_ERROR << "Get StatusHealth property failed:"
<< ec.what();
messages::internalError(asyncResp->res);
return;
}
if (statusHealth.empty())
{
BMCWEB_LOG_ERROR << "Empty StatusHealth property";
messages::internalError(asyncResp->res);
return;
}
asyncResp->res.jsonValue["Status"]["Health"] = statusHealth;
});
dbus_utils::getAssociationEndPoints(
networkAdapterPath + "/all_ports", requestContext,
[asyncResp, chassisId,
networkAdapterId](const boost::system::error_code& ec,
const dbus::utility::MapperEndPoints& resp) {
if (ec || resp.empty())
{
return; // no ports = no failures
}
nlohmann::json reference;
reference["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId, "NetworkAdapters",
networkAdapterId, "Ports");
asyncResp->res.jsonValue["Ports"] = std::move(reference);
});
}
inline void handleChassisNetworkAdapterGetSysfs(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId)
{
// currently we only define one network adapter id and put all the bmc ports
// under it.
asyncResp->res.jsonValue["@odata.type"] = "#NetworkAdapter.NetworkAdapter";
asyncResp->res.jsonValue["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId,
"NetworkAdapters", networkAdapterId);
asyncResp->res.jsonValue["Name"] = "Network adapter";
nlohmann::json& members = asyncResp->res.jsonValue["Members"];
members = nlohmann::json::array();
nlohmann::json::object_t member;
member["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId, "NetworkAdapters",
networkAdapterId, "Ports");
members.push_back(std::move(member));
asyncResp->res.jsonValue["Members@odata.count"] = members.size();
}
inline void handleChassisNetworkAdapterGetDBus(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId)
{
constexpr std::array<std::string_view, 1> interfaces = {
networkAdapterInventoryIntf};
// mapper call chassis
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::GetManagedObjectStore()->getSubTree(
inventoryPrefix, 0, interfaces, requestContext,
[asyncResp, chassisId, networkAdapterId, requestContext](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
if (ec)
{
BMCWEB_LOG_ERROR << "Get NetworkAdapter inventory failed:"
<< ec.what();
messages::internalError(asyncResp->res);
return;
}
// Iterate over all retrieved ObjectPaths.
for (const auto& [objectPath, serviceMap] : subtree)
{
std::string networkAdapterPath =
crow::utility::urlFromPieces(
chassisId, networkAdapterId).buffer();
if (!objectPath.ends_with(networkAdapterPath))
{
continue;
}
if (serviceMap.empty())
{
BMCWEB_LOG_DEBUG << "Got 0 Connection names";
continue;
}
const std::string& serviceName = serviceMap[0].first;
buildNetworkAdapterDBus(asyncResp, chassisId, networkAdapterId,
serviceName, objectPath);
return;
}
// chassisId is not found
std::string networkAdapterPath =
crow::utility::urlFromPieces(
chassisId, networkAdapterId).buffer();
messages::resourceNotFound(asyncResp->res, "Chassis/NetworkAdapter",
networkAdapterId);
});
}
inline void handleChassisNetworkAdapterGet(
crow::App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId)
{
handleChassisNetworkAdapterCommon(
app, req, asyncResp, chassisId, handleChassisNetworkAdapterGetSysfs,
handleChassisNetworkAdapterGetDBus, networkAdapterId);
}
inline void requestRoutesChassisNetworkAdapter(App& app)
{
BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/NetworkAdapters/<str>/")
.privileges(redfish::privileges::getNetworkAdapter)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleChassisNetworkAdapterGet, std::ref(app)));
}
inline void handleNetworkPortCollectionGetSysfs(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId)
{
/**
* Functions triggers appropriate requests on DBus
*/
// currently we only define one network adapter and put all the ports under
// it.
asyncResp->res.jsonValue["@odata.type"] = "#PortCollection.PortCollection";
asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId, "NetworkAdapters",
networkAdapterId, "Ports");
asyncResp->res.jsonValue["Name"] = "Network ports";
nlohmann::json& members = asyncResp->res.jsonValue["Members"];
members = nlohmann::json::array();
for (auto& i : sysfsGetEtherDevList())
{
nlohmann::json::object_t member;
member["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId, "NetworkAdapters",
networkAdapterId, "Ports", i);
members.push_back(std::move(member));
}
asyncResp->res.jsonValue["Members@odata.count"] = members.size();
}
inline void buildNetworkPortCollectionDBus(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId,
const std::string& networkAdapterPath)
{
asyncResp->res.jsonValue["@odata.type"] = "#PortCollection.PortCollection";
asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId, "NetworkAdapters",
networkAdapterId, "Ports");
asyncResp->res.jsonValue["Name"] = "Network Port Collection";
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
dbus_utils::getAssociationEndPoints(
networkAdapterPath + "/all_ports", requestContext,
[asyncResp, chassisId,
networkAdapterId](const boost::system::error_code& ec,
const dbus::utility::MapperEndPoints& resp) {
nlohmann::json& members = asyncResp->res.jsonValue["Members"];
members = nlohmann::json::array();
if (ec)
{
BMCWEB_LOG_DEBUG << "Cannot find chassis /all_ports association:"
<< ec.what();
asyncResp->res.jsonValue["Members@odata.count"] = 0;
return;
}
std::vector<std::string> leafNames;
for (const auto& port : resp)
{
sdbusplus::message::object_path portPath(port);
leafNames.push_back(portPath.filename());
}
std::sort(leafNames.begin(), leafNames.end(),
AlphanumLess<std::string>());
for (const auto& leafName : leafNames)
{
nlohmann::json::object_t member;
member["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId, "NetworkAdapters",
networkAdapterId, "Ports", leafName);
members.push_back(std::move(member));
}
asyncResp->res.jsonValue["Members@odata.count"] = members.size();
}); // end association lambda
}
inline void handleNetworkPortCollectionGetDBus(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId)
{
constexpr std::array<std::string_view, 1> interfaces = {
networkAdapterInventoryIntf};
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::GetManagedObjectStore()->getSubTree(
inventoryPrefix, 0, interfaces, requestContext,
[asyncResp, chassisId, networkAdapterId, requestContext](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
if (ec)
{
if (ec == boost::system::errc::host_unreachable)
{
messages::resourceNotFound(
asyncResp->res, "#NetworkAdapter.v1_9_0.NetworkAdapter",
networkAdapterId);
return;
}
BMCWEB_LOG_ERROR << "Failed to get " << networkAdapterInventoryIntf
<< " due to :" << ec.what();
messages::internalError(asyncResp->res);
return;
}
// Iterate over all retrieved ObjectPaths.
for (const auto& [path, connectionNames] : subtree)
{
std::string networkAdapterPath =
crow::utility::urlFromPieces(
chassisId, networkAdapterId).buffer();
if (!path.ends_with(networkAdapterPath))
{
continue;
}
if (connectionNames.empty())
{
BMCWEB_LOG_DEBUG << "Got 0 Connection names";
continue;
}
buildNetworkPortCollectionDBus(asyncResp, chassisId,
networkAdapterId, path);
break;
} // end Iterate over all retrieved ObjectPaths
});
}
inline void handleNetworkPortCollectionGet(
crow::App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId)
{
handleChassisNetworkAdapterCommon(
app, req, asyncResp, chassisId, handleNetworkPortCollectionGetSysfs,
handleNetworkPortCollectionGetDBus, networkAdapterId);
}
inline void requestRoutesNetworkPortCollection(App& app)
{
BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/NetworkAdapters/<str>/Ports/")
.privileges(redfish::privileges::getPortCollection)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleNetworkPortCollectionGet, std::ref(app)));
}
inline void handleNetworkPortGetSysfs(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId,
const std::string& portId)
{
asyncResp->res.jsonValue["@odata.type"] = "#Port.Port";
asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId, "NetworkAdapters",
networkAdapterId, "Ports", portId);
asyncResp->res.jsonValue["Metrics"]["@odata.id"] =
crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId,
"NetworkAdapters", networkAdapterId,
"Ports", portId, "Metrics");
if (portId == "front")
{
asyncResp->res.jsonValue["Locations"]["ServiceLable"] =
"FrontServiceLable";
}
std::vector<std::string> ethList = sysfsGetEthAddrList(portId);
asyncResp->res.jsonValue["Ethernet"]["AssociatedMACAddresses"] =
std::move(ethList);
asyncResp->res.jsonValue["LinkState"] = sysfsGetLinkState(portId);
if (asyncResp->res.jsonValue["LinkState"] == "Enabled")
{
asyncResp->res.jsonValue["LinkStatus"] = "LinkUp";
}
else
{
asyncResp->res.jsonValue["LinkStatus"] = "LinkDown";
}
asyncResp->res.jsonValue["CurrentSpeedGbps"] = sysfsGetSpeedGbps(portId);
asyncResp->res.jsonValue["Name"] = portId;
}
inline void buildNetworkPortDBus(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId,
const std::string& portId, const std::string& serviceName,
const std::string& networkPortPath)
{
asyncResp->res.jsonValue["@odata.type"] = "#Port.v1_9_0.Port";
asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId, "NetworkAdapters",
networkAdapterId, "Ports", portId);
asyncResp->res.jsonValue["Name"] = portId;
asyncResp->res.jsonValue["Id"] = portId;
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::GetManagedObjectStore()->getAllProperties(
serviceName, networkPortPath, portInventoryIntf, requestContext,
[asyncResp, serviceName, networkPortPath](
const boost::system::error_code& ec,
const dbus::utility::DBusPropertiesMap& propertiesList) {
if (ec)
{
BMCWEB_LOG_ERROR << "getAllProperties on path " << networkPortPath
<< " iface " << portInventoryIntf << " service "
<< serviceName << " failed with code "
<< ec.what();
messages::internalError(asyncResp->res);
return;
}
const std::string* linkState = nullptr;
const std::string* linkStatus = nullptr;
const double* currentSpeedGbps = nullptr;
const bool success = sdbusplus::unpackPropertiesNoThrow(
dbus_utils::UnpackErrorPrinter(), propertiesList, "LinkState",
linkState, "LinkStatus", linkStatus, "CurrentSpeedGbps",
currentSpeedGbps);
if (!success)
{
BMCWEB_LOG_ERROR << "Failed to unpack " << portInventoryIntf;
messages::internalError(asyncResp->res);
return;
}
if (linkState == nullptr || linkState->empty())
{
BMCWEB_LOG_ERROR << "Empty LinkState property in "
<< portInventoryIntf;
messages::internalError(asyncResp->res);
return;
}
asyncResp->res.jsonValue["LinkState"] = *linkState;
if (linkStatus == nullptr || linkStatus->empty())
{
BMCWEB_LOG_ERROR << "Empty LinkStatus property in "
<< portInventoryIntf;
messages::internalError(asyncResp->res);
return;
}
asyncResp->res.jsonValue["LinkStatus"] = *linkStatus;
if (currentSpeedGbps == nullptr)
{
BMCWEB_LOG_ERROR << "Empty CurrentSpeedGbps property in "
<< portInventoryIntf;
messages::internalError(asyncResp->res);
return;
}
asyncResp->res.jsonValue["CurrentSpeedGbps"] = *currentSpeedGbps;
});
}
inline void handleNetworkPortGetDBus(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId,
const std::string& portId)
{
constexpr std::array<std::string_view, 1> interfaces = {portInventoryIntf};
managedStore::ManagedObjectStoreContext requestContext(asyncResp);
managedStore::GetManagedObjectStore()->getSubTree(
inventoryPrefix, 0, interfaces, requestContext,
[asyncResp, chassisId, networkAdapterId, portId, requestContext](
const boost::system::error_code& ec,
const dbus::utility::MapperGetSubTreeResponse& subtree) {
if (ec)
{
if (ec == boost::system::errc::host_unreachable)
{
messages::resourceNotFound(asyncResp->res, "#Port.v1_9_0.Port",
portId);
return;
}
BMCWEB_LOG_ERROR << "Failed to get " << portInventoryIntf
<< " due to:" << ec.what();
messages::internalError(asyncResp->res);
return;
}
// Iterate over all retrieved ObjectPaths.
for (const auto& [path, connectionNames] : subtree)
{
std::string portIdPath =
crow::utility::urlFromPieces(
chassisId, networkAdapterId, portId).buffer();
if (!path.ends_with(portIdPath))
{
continue;
}
if (connectionNames.empty())
{
BMCWEB_LOG_DEBUG << "Got 0 Connection names";
continue;
}
const std::string& serviceName = connectionNames[0].first;
buildNetworkPortDBus(asyncResp, chassisId, networkAdapterId, portId,
serviceName, path);
return;
} // end Iterate over all retrieved ObjectPaths
messages::resourceNotFound(asyncResp->res, "#Port.v1_9_0.Port", portId);
});
}
inline void
handleNetworkPortGet(crow::App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId,
const std::string& networkAdapterId,
const std::string& portId)
{
handleChassisNetworkAdapterCommon(
app, req, asyncResp, chassisId, handleNetworkPortGetSysfs,
handleNetworkPortGetDBus, networkAdapterId, portId);
}
inline void requestRoutesNetworkPort(App& app)
{
BMCWEB_ROUTE(app,
"/redfish/v1/Chassis/<str>/NetworkAdapters/<str>/Ports/<str>/")
.privileges(redfish::privileges::getPort)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleNetworkPortGet, std::ref(app)));
}
inline void handleNetworkPortMetricGetSysfs(
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId,
const std::string& portId)
{
asyncResp->res.jsonValue["@odata.type"] = "#PortMetrics.PortMetrics";
asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces(
"redfish", "v1", "Chassis", chassisId, "NetworkAdapters",
networkAdapterId, "Ports", portId, "Metrics");
asyncResp->res.jsonValue["Name"] = "Network port metrics";
asyncResp->res.jsonValue["Oem"]["Google"]["Networking"] =
sysfsGetNetworkPortMetrics(portId);
}
inline void handleNetworkPortMetricGetDBus(
[[maybe_unused]] const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId,
const std::string& portId)
{
BMCWEB_LOG_ERROR << "Metric not supported: chassis " << chassisId
<< " network adapter " << networkAdapterId << " port "
<< portId;
}
inline void handleNetworkPortMetricGet(
crow::App& app, const crow::Request& req,
const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
const std::string& chassisId, const std::string& networkAdapterId,
const std::string& portId)
{
handleChassisNetworkAdapterCommon(
app, req, asyncResp, chassisId, handleNetworkPortMetricGetSysfs,
handleNetworkPortMetricGetDBus, networkAdapterId, portId);
}
inline void requestRoutesNetworkPortMetrics(App& app)
{
BMCWEB_ROUTE(
app,
"/redfish/v1/Chassis/<str>/NetworkAdapters/<str>/Ports/<str>/Metrics")
.privileges(redfish::privileges::getPortMetrics)
.methods(boost::beast::http::verb::get)(
std::bind_front(handleNetworkPortMetricGet, std::ref(app)));
}
} // namespace redfish