diff options
| author | Harshit Aghera <haghera@nvidia.com> | 2025-10-24 10:21:16 +0300 |
|---|---|---|
| committer | Ed Tanous <ed@tanous.net> | 2026-03-12 18:25:39 +0300 |
| commit | f99bc45d579b751e693bd1655dfd623bd84475f8 (patch) | |
| tree | 012b2bebcce440068fa7b1ad0377dea65e8f043b | |
| parent | 2fd07a9f05c2d32728023c59081c99f563802231 (diff) | |
| download | bmcweb-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.md | 79 | ||||
| -rw-r--r-- | redfish-core/lib/chassis.hpp | 4 | ||||
| -rw-r--r-- | redfish-core/lib/network_adapter.hpp | 543 | ||||
| -rw-r--r-- | redfish-core/src/redfish.cpp | 2 |
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); |
