summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHarshit Aghera <haghera@nvidia.com>2025-10-24 10:21:16 +0300
committerEd Tanous <ed@tanous.net>2026-03-12 18:25:39 +0300
commitf99bc45d579b751e693bd1655dfd623bd84475f8 (patch)
tree012b2bebcce440068fa7b1ad0377dea65e8f043b
parent2fd07a9f05c2d32728023c59081c99f563802231 (diff)
downloadbmcweb-f99bc45d579b751e693bd1655dfd623bd84475f8.tar.xz
NetworkAdapter: add support for Network Adapter Port Metrics
This patch enables support for Nvidia ConnectX network cards. Network adapter schemas are restricted to a single URI path format: /redfish/v1/Chassis/{ChassisId}/NetworkAdapters/{NetworkAdaptersId}/. [1] And thus, the port URI follows this structure /redfish/v1/Chassis/{ChassisId}/NetworkAdapters/{NetworkAdaptersId}/ Ports/{PortId}. [2] Route handler for collections and each individual components are added in this patch for each URI resource under /redfish/v1/Chassis/{ChassisId}/NetworkAdapters. Association between Chassis and NetworkAdapter is `containing` and `contained_by`. Association between NetworkAdapter and Port is `connecting` and `connected_to`. This patch enable support for following properties for Port Metrics URI of a Network Port. [3] - TXBytes - RXBytes - RXMulticastFrames - TXMulticastFrames - RXUnicastFrames - TXUnicastFrames - RXBroadcastFrames - TXBroadcastFrames - RXFCSErrors - RXFrameAlignmentErrors - RXFalseCarrierErrors - RXUndersizeFrames - RXOversizeFrames - RXPauseXONFrames - RXPauseXOFFFrames - TXPauseXONFrames - TXPauseXOFFFrames - TXSingleCollisions - TXMultipleCollisions - TXLateCollisions - TXExcessiveCollisions The patch uses "xyz.openbmc_project.Metric.Value" Interface for Network Port Metrics properties. Association between a Metric and a Port is `measuring` and `measured_by`. PDI patch - https://gerrit.openbmc.org/c/openbmc/bmcweb/+/84629 dbus-sensors patches - https://gerrit.openbmc.org/c/openbmc/dbus-sensors/+/84520 Tested: Build an image for nvl32-obmc machine with the following patch cherry picked. https://gerrit.openbmc.org/c/openbmc/dbus-sensors/+/84520 https://gerrit.openbmc.org/c/openbmc/openbmc/+/85490 The openbmc patch cherry-picks the following patches that are currently under review. ``` 1. device tree https://lore.kernel.org/all/aRbLqH8pLWCQryhu@molberding.nvidia.com/ 2. mctpd patches https://github.com/CodeConstruct/mctp/pull/85 3. u-boot changes https://lore.kernel.org/openbmc/20251121-msx4-v1-0-fc0118b666c1@nvidia.com/T/#t 4. kernel changes as specified in the openbmc patch (for espi) 5. entity-manager changes https://gerrit.openbmc.org/c/openbmc/entity-manager/+/85455 6. platform-init changes https://gerrit.openbmc.org/c/openbmc/platform-init/+/85456 7. spi changes https://lore.kernel.org/all/20251121-w25q01jv_fixup-v1-1-3d175050db73@nvidia.com/ ``` redfish service validator is passing. ``` $ curl -s -k -u 'root:0penBmc' https://${bmc_ip}/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters", "@odata.type": "#NetworkAdapterCollection.NetworkAdapterCollection", "Members": [ { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_0" }, { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1" }, { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_2" }, { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_3" } ], "Members@odata.count": 4, "Name": "Nvidia_IMGX_ConnectX8_SuperNIC_Switch Network Adapter Collection" }% $ curl -s -k -u 'root:0penBmc' https://${bmc_ip}/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1 { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1", "@odata.type": "#NetworkAdapter.v1_11_0.NetworkAdapter", "Id": "Nvidia_ConnectX_1", "Name": "Nvidia_ConnectX_1 Network Adapter", "Ports": { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1/Ports" }, "Status": { "Health": "OK", "HealthRollup": "OK", "State": "Enabled" } }% $ curl -s -k -u 'root:0penBmc' https://${bmc_ip}/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1/Ports { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1/Ports", "@odata.type": "#PortCollection.PortCollection", "Members": [ { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1/Ports/Port_0" }, { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1/Ports/Port_1" } ], "Members@odata.count": 2, "Name": "Nvidia_ConnectX_1 Port Collection" }% $ curl -s -k -u 'root:0penBmc' https://${bmc_ip}/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1/Ports/Port_0/ { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1/Ports/Port_0", "@odata.type": "#Port.v1_9_0.Port", "Id": "Port_0", "Metrics": { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1/Ports/Port_0/Metrics" }, "Name": "Nvidia_ConnectX_1 Port_0 Port", "Status": { "Health": "OK", "HealthRollup": "OK", "State": "Enabled" } }% $ curl -s -k -u 'root:0penBmc' https://${bmc_ip}/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1/Ports/Port_0/Metrics/ { "@odata.id": "/redfish/v1/Chassis/Nvidia_IMGX_ConnectX8_SuperNIC_Switch/NetworkAdapters/Nvidia_ConnectX_1/Ports/Port_0/Metrics", "@odata.type": "#PortMetrics.v1_7_0.PortMetrics", "Id": "Metrics", "Name": "Nvidia_ConnectX_1 Port_0 Port Metrics", "Networking": { "RXBroadcastFrames": 0, "RXFCSErrors": 0, "RXFalseCarrierErrors": 0, "RXFrameAlignmentErrors": 0, "RXMulticastFrames": 0, "RXOversizeFrames": 0, "RXPauseXOFFFrames": 0, "RXPauseXONFrames": 0, "RXUndersizeFrames": 0, "RXUnicastFrames": 0, "TXBroadcastFrames": 0, "TXExcessiveCollisions": 0, "TXLateCollisions": 0, "TXMulticastFrames": 0, "TXMultipleCollisions": 0, "TXPauseXOFFFrames": 0, "TXPauseXONFrames": 0, "TXSingleCollisions": 0, "TXUnicastFrames": 0 }, "RXBytes": 0, "TXBytes": 0 }% ``` [1]: https://redfish.dmtf.org/schemas/v1/NetworkAdapter_v1.xml [2]: https://redfish.dmtf.org/schemas/v1/Port_v1.xml [3]: https://redfish.dmtf.org/schemas/v1/PortMetrics_v1.xml Change-Id: I73c5a39b12f8f0a40026fb50c2ded53e0b225f67 Signed-off-by: Harshit Aghera <haghera@nvidia.com>
-rw-r--r--docs/Redfish.md79
-rw-r--r--redfish-core/lib/chassis.hpp4
-rw-r--r--redfish-core/lib/network_adapter.hpp543
-rw-r--r--redfish-core/src/redfish.cpp2
4 files changed, 628 insertions, 0 deletions
diff --git a/docs/Redfish.md b/docs/Redfish.md
index 90d249fb30..77b667f511 100644
--- a/docs/Redfish.md
+++ b/docs/Redfish.md
@@ -496,6 +496,85 @@ Fields common to all schemas
- SerialNumber
- SparePartNumber
+### /redfish/v1/Chassis/{ChassisId}/NetworkAdapters/
+
+#### NetworkAdapterCollection
+
+- @odata.id
+- @odata.type
+- Members
+- `Members@odata.count`
+- Name
+
+### /redfish/v1/Chassis/{ChassisId}/NetworkAdapters/{NetworkAdapterId}/
+
+#### NetworkAdapter
+
+- @odata.id
+- @odata.type
+- Id
+- Name
+- Ports
+- Status
+ - Health
+ - HealthRollup
+ - State
+
+### /redfish/v1/Chassis/{ChassisId}/NetworkAdapters/{NetworkAdapterId}/Ports/
+
+#### PortCollection
+
+- @odata.id
+- @odata.type
+- Members
+- `Members@odata.count`
+- Name
+
+### /redfish/v1/Chassis/{ChassisId}/NetworkAdapters/{NetworkAdapterId}/Ports/{PortId}/
+
+#### Port
+
+- @odata.id
+- @odata.type
+- Id
+- Metrics
+- Name
+- Status
+ - Health
+ - HealthRollup
+ - State
+
+### /redfish/v1/Chassis/{ChassisId}/NetworkAdapters/{NetworkAdapterId}/Ports/{PortId}/Metrics/
+
+#### PortMetrics
+
+- @odata.id
+- @odata.type
+- Id
+- Name
+- Networking
+ - RXBroadcastFrames
+ - RXFCSErrors
+ - RXFalseCarrierErrors
+ - RXFrameAlignmentErrors
+ - RXMulticastFrames
+ - RXOversizeFrames
+ - RXPauseXOFFFrames
+ - RXPauseXONFrames
+ - RXUndersizeFrames
+ - RXUnicastFrames
+ - TXBroadcastFrames
+ - TXExcessiveCollisions
+ - TXLateCollisions
+ - TXMulticastFrames
+ - TXMultipleCollisions
+ - TXPauseXOFFFrames
+ - TXPauseXONFrames
+ - TXSingleCollisions
+ - TXUnicastFrames
+- RXBytes
+- TXBytes
+
### /redfish/v1/EventService/
#### EventService
diff --git a/redfish-core/lib/chassis.hpp b/redfish-core/lib/chassis.hpp
index d577fb7c55..5cbf603c6e 100644
--- a/redfish-core/lib/chassis.hpp
+++ b/redfish-core/lib/chassis.hpp
@@ -468,6 +468,10 @@ inline void handleDecoratorAssetProperties(
asyncResp->res.jsonValue["Assembly"]["@odata.id"] =
boost::urls::format("/redfish/v1/Chassis/{}/Assembly", chassisId);
+ asyncResp->res.jsonValue["NetworkAdapters"]["@odata.id"] =
+ boost::urls::format("/redfish/v1/Chassis/{}/NetworkAdapters",
+ chassisId);
+
// SensorCollection
asyncResp->res.jsonValue["Sensors"]["@odata.id"] =
boost::urls::format("/redfish/v1/Chassis/{}/Sensors", chassisId);
diff --git a/redfish-core/lib/network_adapter.hpp b/redfish-core/lib/network_adapter.hpp
new file mode 100644
index 0000000000..8c8cf5b7e0
--- /dev/null
+++ b/redfish-core/lib/network_adapter.hpp
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-FileCopyrightText: Copyright OpenBMC Authors
+#pragma once
+
+#include "app.hpp"
+#include "async_resp.hpp"
+#include "dbus_utility.hpp"
+#include "error_messages.hpp"
+#include "generated/enums/resource.hpp"
+#include "http_request.hpp"
+#include "logging.hpp"
+#include "query.hpp"
+#include "registries/privilege_registry.hpp"
+#include "switch_port.hpp"
+#include "utils/chassis_utils.hpp"
+
+#include <boost/beast/http/verb.hpp>
+
+#include <array>
+#include <format>
+#include <functional>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <utility>
+
+static constexpr std::array<std::string_view, 1> networkAdapterInterface = {
+ "xyz.openbmc_project.Inventory.Item.NetworkAdapter"};
+
+namespace redfish
+{
+
+inline void handleNetworkAdapterPortMetricsPathsPortMetricsGet(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const boost::system::error_code& ec,
+ const dbus::utility::MapperGetSubTreeResponse& object)
+{
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR("DBus response error on GetAssociatedSubTree{}", ec);
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ for (const auto& [path, service] : object)
+ {
+ if (service.size() != 1)
+ {
+ continue;
+ }
+
+ sdbusplus::message::object_path objectPah(path);
+
+ const std::string metricType = objectPah.parent_path().filename();
+ const std::string metricName = objectPah.filename();
+
+ if (metricType != "nic")
+ {
+ continue;
+ }
+
+ const auto& serviceName = service.begin()->first;
+
+ if (metricName == "rx_bytes")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/RXBytes"_json_pointer);
+ }
+ else if (metricName == "tx_bytes")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/TXBytes"_json_pointer);
+ }
+ else if (metricName == "rx_multicast_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/RXMulticastFrames"_json_pointer);
+ }
+ else if (metricName == "tx_multicast_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/TXMulticastFrames"_json_pointer);
+ }
+ else if (metricName == "rx_broadcast_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/RXBroadcastFrames"_json_pointer);
+ }
+ else if (metricName == "tx_broadcast_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/TXBroadcastFrames"_json_pointer);
+ }
+ else if (metricName == "rx_unicast_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/RXUnicastFrames"_json_pointer);
+ }
+ else if (metricName == "tx_unicast_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/TXUnicastFrames"_json_pointer);
+ }
+ else if (metricName == "rx_fcs_errors")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/RXFCSErrors"_json_pointer);
+ }
+ else if (metricName == "rx_frame_alignment_errors")
+ {
+ getMetricProperty(
+ asyncResp, serviceName, path,
+ "/Networking/RXFrameAlignmentErrors"_json_pointer);
+ }
+ else if (metricName == "rx_false_carrier_errors")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/RXFalseCarrierErrors"_json_pointer);
+ }
+ else if (metricName == "rx_undersize_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/RXUndersizeFrames"_json_pointer);
+ }
+ else if (metricName == "rx_oversize_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/RXOversizeFrames"_json_pointer);
+ }
+ else if (metricName == "rx_pause_xon_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/RXPauseXONFrames"_json_pointer);
+ }
+ else if (metricName == "rx_pause_xoff_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/RXPauseXOFFFrames"_json_pointer);
+ }
+ else if (metricName == "tx_pause_xon_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/TXPauseXONFrames"_json_pointer);
+ }
+ else if (metricName == "tx_pause_xoff_frames")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/TXPauseXOFFFrames"_json_pointer);
+ }
+ else if (metricName == "tx_single_collisions")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/TXSingleCollisions"_json_pointer);
+ }
+ else if (metricName == "tx_multiple_collisions")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/TXMultipleCollisions"_json_pointer);
+ }
+ else if (metricName == "tx_late_collisions")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/TXLateCollisions"_json_pointer);
+ }
+ else if (metricName == "tx_excessive_collisions")
+ {
+ getMetricProperty(asyncResp, serviceName, path,
+ "/Networking/TXExcessiveCollisions"_json_pointer);
+ }
+ }
+}
+
+inline void handleNetworkAdapterPortPathPortMetricsGet(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId, const std::string& networkAdapterId,
+ const std::string& portId, const sdbusplus::message::object_path& portPath,
+ [[maybe_unused]] const std::string& serviceName)
+{
+ asyncResp->res.jsonValue["@odata.type"] = "#PortMetrics.v1_7_0.PortMetrics";
+ asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
+ "/redfish/v1/Chassis/{}/NetworkAdapters/{}/Ports/{}/Metrics", chassisId,
+ networkAdapterId, portId);
+ asyncResp->res.jsonValue["Id"] = "Metrics";
+ asyncResp->res.jsonValue["Name"] =
+ std::format("{} {} Port Metrics", networkAdapterId, portId);
+
+ const std::string associationPath = portPath / "measured_by";
+ dbus::utility::getAssociatedSubTree(
+ associationPath,
+ sdbusplus::message::object_path("/xyz/openbmc_project/metric"), 0,
+ std::array<std::string_view, 1>{"xyz.openbmc_project.Metric.Value"},
+ std::bind_front(handleNetworkAdapterPortMetricsPathsPortMetricsGet,
+ asyncResp));
+}
+
+inline void handleNetworkAdapterPathPortGet(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassiId, const std::string& networkAdapterId,
+ const std::string& portId, [[maybe_unused]] const std::string& portPath,
+ [[maybe_unused]] const std::string& serviceName)
+{
+ asyncResp->res.jsonValue["@odata.type"] = "#Port.v1_9_0.Port";
+ asyncResp->res.jsonValue["@odata.id"] =
+ std::format("/redfish/v1/Chassis/{}/NetworkAdapters/{}/Ports/{}",
+ chassiId, networkAdapterId, portId);
+ asyncResp->res.jsonValue["Id"] = portId;
+ asyncResp->res.jsonValue["Name"] =
+ std::format("{} {} Port", networkAdapterId, portId);
+
+ nlohmann::json& status = asyncResp->res.jsonValue["Status"];
+ status["Health"] = resource::Health::OK;
+ status["HealthRollup"] = resource::Health::OK;
+ status["State"] = resource::State::Enabled;
+
+ asyncResp->res.jsonValue["Metrics"]["@odata.id"] = std::format(
+ "/redfish/v1/Chassis/{}/NetworkAdapters/{}/Ports/{}/Metrics", chassiId,
+ networkAdapterId, portId);
+}
+
+inline void afterNetworkAdapterPortPaths(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& portId,
+ const std::function<void(const std::string& portPath,
+ const std::string& serviceName)>& callback,
+ const boost::system::error_code& ec,
+ const dbus::utility::MapperGetSubTreeResponse& object)
+{
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR("DBus response error on GetAssociatedSubTreeById {}",
+ ec);
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ std::string portPath;
+ std::string serviceName;
+ for (const auto& [path, service] : object)
+ {
+ std::string portName = sdbusplus::message::object_path(path).filename();
+ if (portName == portId)
+ {
+ portPath = path;
+ if (service.size() != 1)
+ {
+ messages::internalError(asyncResp->res);
+ return;
+ }
+ serviceName = service.begin()->first;
+ break;
+ }
+ }
+
+ if (portPath.empty())
+ {
+ messages::resourceNotFound(asyncResp->res, "Port", portId);
+ return;
+ }
+
+ callback(portPath, serviceName);
+}
+
+inline void getNetworkAdapterPortPath(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& portId,
+ std::function<void(const std::string& portPath,
+ const std::string& serviceName)>&& callback,
+ const std::string& path, [[maybe_unused]] const std::string& serviceName)
+{
+ std::string associationPath = path + "/connecting";
+ dbus::utility::getAssociatedSubTree(
+ associationPath,
+ sdbusplus::message::object_path{"/xyz/openbmc_project/inventory"}, 0,
+ std::array<std::string_view, 1>{
+ "xyz.openbmc_project.Inventory.Connector.Port"},
+ std::bind_front(afterNetworkAdapterPortPaths, asyncResp, portId,
+ std::move(callback)));
+}
+
+inline void handleNetworkAdapterPortPathPortCollection(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId, const std::string& networkAdapterId,
+ const boost::system::error_code& ec,
+ const dbus::utility::MapperGetSubTreePathsResponse& object)
+{
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR("DBus response error on GetSubTreePaths {}", ec);
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ asyncResp->res.jsonValue["@odata.type"] = "#PortCollection.PortCollection";
+ asyncResp->res.jsonValue["@odata.id"] =
+ std::format("/redfish/v1/Chassis/{}/NetworkAdapters/{}/Ports",
+ chassisId, networkAdapterId);
+ asyncResp->res.jsonValue["Name"] = networkAdapterId + " Port Collection";
+
+ asyncResp->res.jsonValue["Members@odata.count"] = object.size();
+
+ nlohmann::json::array_t members;
+ for (const std::string& path : object)
+ {
+ std::string name = sdbusplus::message::object_path(path).filename();
+ nlohmann::json::object_t member;
+ member["@odata.id"] =
+ std::format("/redfish/v1/Chassis/{}/NetworkAdapters/{}/Ports/{}",
+ chassisId, networkAdapterId, name);
+ members.emplace_back(std::move(member));
+ }
+
+ asyncResp->res.jsonValue["Members"] = std::move(members);
+}
+
+inline void getNetworkAdapterPortPaths(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId, const std::string& networkAdapterId,
+ const std::string& networkAdapterPath)
+{
+ std::string associationPath = networkAdapterPath + "/connecting";
+ dbus::utility::getAssociatedSubTreePaths(
+ associationPath,
+ sdbusplus::message::object_path{"/xyz/openbmc_project/inventory"}, 0,
+ std::array<std::string_view, 1>{
+ "xyz.openbmc_project.Inventory.Connector.Port"},
+ std::bind_front(handleNetworkAdapterPortPathPortCollection, asyncResp,
+ chassisId, networkAdapterId));
+}
+
+inline void handleNetworkAdapterPathNetworkAdapterGet(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId, const std::string& networkAdapterId,
+ [[maybe_unused]] const std::string& path)
+{
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#NetworkAdapter.v1_11_0.NetworkAdapter";
+ asyncResp->res.jsonValue["@odata.id"] =
+ std::format("/redfish/v1/Chassis/{}/NetworkAdapters/{}", chassisId,
+ networkAdapterId);
+ asyncResp->res.jsonValue["Id"] = networkAdapterId;
+ asyncResp->res.jsonValue["Name"] = networkAdapterId + " Network Adapter";
+
+ auto& status = asyncResp->res.jsonValue["Status"];
+ status["Health"] = resource::Health::OK;
+ status["HealthRollup"] = resource::Health::OK;
+ status["State"] = resource::State::Enabled;
+
+ asyncResp->res.jsonValue["Ports"]["@odata.id"] =
+ std::format("/redfish/v1/Chassis/{}/NetworkAdapters/{}/Ports",
+ chassisId, networkAdapterId);
+}
+
+inline void handleNetworkAdapterPaths(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& networkAdapterId,
+ const std::function<void(const std::string& path)>& callback,
+ const boost::system::error_code& ec,
+ const dbus::utility::MapperGetSubTreePathsResponse& object)
+{
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR("DBus response error on GetAssociatedSubTreeById {}",
+ ec);
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ for (const auto& path : object)
+ {
+ std::string name = sdbusplus::message::object_path(path).filename();
+ if (name == networkAdapterId)
+ {
+ callback(path);
+ return;
+ }
+ }
+
+ messages::resourceNotFound(asyncResp->res, "NetworkAdapter",
+ networkAdapterId);
+}
+
+inline void getNetworkAdapterPath(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId, const std::string& networkAdapterId,
+ std::function<void(const std::string& path)>&& callback)
+{
+ dbus::utility::getAssociatedSubTreePathsById(
+ chassisId, "/xyz/openbmc_project/inventory", chassisInterfaces,
+ "containing", networkAdapterInterface,
+ std::bind_front(handleNetworkAdapterPaths, asyncResp, networkAdapterId,
+ std::move(callback)));
+}
+
+inline void handleNetworkAdapterPathsNetworkAdapterCollection(
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId, const boost::system::error_code& ec,
+ const dbus::utility::MapperGetSubTreePathsResponse& object)
+{
+ if (ec)
+ {
+ BMCWEB_LOG_ERROR("DBus response error on GetSubTreePaths {}", ec);
+ messages::internalError(asyncResp->res);
+ return;
+ }
+
+ asyncResp->res.jsonValue["@odata.id"] =
+ std::format("/redfish/v1/Chassis/{}/NetworkAdapters", chassisId);
+ asyncResp->res.jsonValue["@odata.type"] =
+ "#NetworkAdapterCollection.NetworkAdapterCollection";
+ asyncResp->res.jsonValue["Name"] =
+ chassisId + " Network Adapter Collection";
+
+ asyncResp->res.jsonValue["Members@odata.count"] = object.size();
+
+ nlohmann::json& members = asyncResp->res.jsonValue["Members"];
+ members = nlohmann::json::array();
+ for (const std::string& path : object)
+ {
+ std::string name = sdbusplus::message::object_path(path).filename();
+ nlohmann::json member;
+ member["@odata.id"] = std::format(
+ "/redfish/v1/Chassis/{}/NetworkAdapters/{}", chassisId, name);
+ members.push_back(std::move(member));
+ }
+}
+
+inline void handleNetworkAdapterGet(
+ crow::App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId, const std::string& networkAdapterId)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ getNetworkAdapterPath(
+ asyncResp, chassisId, networkAdapterId,
+ std::bind_front(handleNetworkAdapterPathNetworkAdapterGet, asyncResp,
+ chassisId, networkAdapterId));
+}
+
+inline void handleNetworkAdapterCollectionGet(
+ crow::App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ dbus::utility::getAssociatedSubTreePathsById(
+ chassisId, "/xyz/openbmc_project/inventory", chassisInterfaces,
+ "containing", networkAdapterInterface,
+ std::bind_front(handleNetworkAdapterPathsNetworkAdapterCollection,
+ asyncResp, chassisId));
+}
+
+inline void handleNetworkAdapterPortMetricsGet(
+ 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)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ getNetworkAdapterPath(
+ asyncResp, chassisId, networkAdapterId,
+ std::bind_front(
+ getAssociatedPortPath, asyncResp, portId,
+ std::bind_front(handleNetworkAdapterPortPathPortMetricsGet,
+ asyncResp, chassisId, networkAdapterId, portId)));
+}
+
+inline void handleNetworkAdapterPortGet(
+ 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)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ getNetworkAdapterPath(
+ asyncResp, chassisId, networkAdapterId,
+ std::bind_front(
+ getAssociatedPortPath, asyncResp, portId,
+ std::bind_front(handleNetworkAdapterPathPortGet, asyncResp,
+ chassisId, networkAdapterId, portId)));
+}
+
+inline void handleNetworkAdapterPortCollectionGet(
+ App& app, const crow::Request& req,
+ const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
+ const std::string& chassisId, const std::string& networkAdapterId)
+{
+ if (!redfish::setUpRedfishRoute(app, req, asyncResp))
+ {
+ return;
+ }
+
+ getNetworkAdapterPath(asyncResp, chassisId, networkAdapterId,
+ std::bind_front(getNetworkAdapterPortPaths, asyncResp,
+ chassisId, networkAdapterId));
+}
+
+inline void requestRoutesChassisNetworkAdapter(App& app)
+{
+ BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/NetworkAdapters/")
+ .privileges(redfish::privileges::getNetworkAdapterCollection)
+ .methods(boost::beast::http::verb::get)(
+ std::bind_front(handleNetworkAdapterCollectionGet, std::ref(app)));
+
+ BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/NetworkAdapters/<str>/")
+ .privileges(redfish::privileges::getNetworkAdapter)
+ .methods(boost::beast::http::verb::get)(
+ std::bind_front(handleNetworkAdapterGet, std::ref(app)));
+
+ BMCWEB_ROUTE(app, "/redfish/v1/Chassis/<str>/NetworkAdapters/<str>/Ports/")
+ .privileges(redfish::privileges::getPortCollection)
+ .methods(boost::beast::http::verb::get)(std::bind_front(
+ handleNetworkAdapterPortCollectionGet, std::ref(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(handleNetworkAdapterPortGet, std::ref(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(handleNetworkAdapterPortMetricsGet, std::ref(app)));
+}
+} // namespace redfish
diff --git a/redfish-core/src/redfish.cpp b/redfish-core/src/redfish.cpp
index a472c57ec5..495e60f471 100644
--- a/redfish-core/src/redfish.cpp
+++ b/redfish-core/src/redfish.cpp
@@ -32,6 +32,7 @@
#include "metadata.hpp"
#include "metric_report.hpp"
#include "metric_report_definition.hpp"
+#include "network_adapter.hpp"
#include "network_protocol.hpp"
#include "odata.hpp"
#include "openbmc/openbmc_managers.hpp"
@@ -109,6 +110,7 @@ RedfishService::RedfishService(App& app)
requestRoutesChassis(app);
requestRoutesChassisResetAction(app);
requestRoutesChassisResetActionInfo(app);
+ requestRoutesChassisNetworkAdapter(app);
requestRoutesUpdateService(app);
requestRoutesStorage(app);
requestRoutesStorageController(app);