| /* |
| // 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 |