Add ConnectX-9 PortMetrics and TransceiverTelemetry support to D-Bus network ports Expose PortMetrics and TransceiverTelemetry for ConnectX-9 network ports in gBMCWeb: - Add @odata.id link to Metrics in buildNetworkPortDBus. - Implement buildNetworkPortMetricDBus to query PortECC, PortPacketCounters, and PCIeECC from NSM D-Bus interfaces. - Route handleNetworkPortMetricGetDBus to buildNetworkPortMetricDBus. Tested: Verified PortMetrics and TransceiverTelemetry endpoints return expected metric values on ConnectX-9 network ports. Google-Bug-Id:540603557 PiperOrigin-RevId: 970680678 Change-Id: I953cf2ff8eb51e43254fa7f9efb0321d517b0dc9
diff --git a/redfish-core/lib/network_adapter.hpp b/redfish-core/lib/network_adapter.hpp index b022448..f7c890b 100644 --- a/redfish-core/lib/network_adapter.hpp +++ b/redfish-core/lib/network_adapter.hpp
@@ -28,9 +28,12 @@ #include <memory> #include <string> #include <string_view> +#include <type_traits> #include <utility> +#include <variant> #include <vector> +#include "absl/strings/str_cat.h" #include "boost/system/error_code.hpp" // NOLINT #include "bmcweb_config.h" #include "app.hpp" @@ -739,8 +742,9 @@ } else { // usb sideband using ifalias std::string ifalias; - if (readOneLineFile(std::filesystem::path(netSysfsPath) / portId / "ifalias", - ifalias)) { + if (readOneLineFile( + std::filesystem::path(netSysfsPath) / portId / "ifalias", + ifalias)) { if (ifalias.size() >= 5 && ifalias.compare(ifalias.size() - 5, 5, "-side") == 0) { managedStore::ManagedObjectStoreContext requestContext5(asyncResp); @@ -752,31 +756,32 @@ [asyncResp]( const boost::system::error_code& ec5, const dbus::utility::DBusPropertiesMap& propertiesList) { - if (ec5) { - BMCWEB_LOG_ERROR << "GetAllProperties failed: " << ec5.what(); - return; - } - const std::string* locationCode = nullptr; - const std::string* partLocationContext = nullptr; - const bool success = sdbusplus::unpackPropertiesNoThrow( - dbus_utils::UnpackErrorPrinter(), propertiesList, "LocationCode", - locationCode, "PartLocationContext", partLocationContext); - if (!success) { - BMCWEB_LOG_ERROR << "Failed to unpack BmcNet_Side properties"; - return; - } - if (locationCode != nullptr) { - asyncResp->res - .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = - *locationCode; - } - if (partLocationContext != nullptr) { - asyncResp->res.jsonValue["Location"]["PartLocationContext"] = - *partLocationContext; - } - asyncResp->res.jsonValue["Oem"]["Google"]["ConnectionPath"] = - "SIDEBAND"; - }); + if (ec5) { + BMCWEB_LOG_ERROR << "GetAllProperties failed: " << ec5.what(); + return; + } + const std::string* locationCode = nullptr; + const std::string* partLocationContext = nullptr; + const bool success = sdbusplus::unpackPropertiesNoThrow( + dbus_utils::UnpackErrorPrinter(), propertiesList, + "LocationCode", locationCode, "PartLocationContext", + partLocationContext); + if (!success) { + BMCWEB_LOG_ERROR << "Failed to unpack BmcNet_Side properties"; + return; + } + if (locationCode != nullptr) { + asyncResp->res + .jsonValue["Location"]["PartLocation"]["ServiceLabel"] = + *locationCode; + } + if (partLocationContext != nullptr) { + asyncResp->res.jsonValue["Location"]["PartLocationContext"] = + *partLocationContext; + } + asyncResp->res.jsonValue["Oem"]["Google"]["ConnectionPath"] = + "SIDEBAND"; + }); } } } @@ -805,6 +810,10 @@ networkAdapterId, "Ports", portId); asyncResp->res.jsonValue["Name"] = portId; asyncResp->res.jsonValue["Id"] = portId; + asyncResp->res.jsonValue["Metrics"]["@odata.id"] = + crow::utility::urlFromPieces("redfish", "v1", "Chassis", chassisId, + "NetworkAdapters", networkAdapterId, "Ports", + portId, "Metrics"); managedStore::ManagedObjectStoreContext requestContext(asyncResp); managedStore::GetManagedObjectStore()->getAllProperties( @@ -939,13 +948,349 @@ sysfsGetNetworkPortMetrics(portId); } +inline void buildNetworkPortMetricDBus( + 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"] = "#PortMetrics.v1_3_0.PortMetrics"; + asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( + "redfish", "v1", "Chassis", chassisId, "NetworkAdapters", + networkAdapterId, "Ports", portId, "Metrics"); + asyncResp->res.jsonValue["Name"] = portId + " Metrics"; + asyncResp->res.jsonValue["Id"] = "Metrics"; + asyncResp->res.jsonValue["CorrectableBitErrors"] = 0; + asyncResp->res.jsonValue["SymbolErrors"] = 0; + asyncResp->res.jsonValue["RXBroadcastPackets"] = 0; + asyncResp->res.jsonValue["RXMulticastPackets"] = 0; + asyncResp->res.jsonValue["RXUnicastPackets"] = 0; + asyncResp->res.jsonValue["TXBroadcastPackets"] = 0; + asyncResp->res.jsonValue["TXMulticastPackets"] = 0; + asyncResp->res.jsonValue["TXUnicastPackets"] = 0; + asyncResp->res + .jsonValue["Oem"]["Google"]["TransceiverTelemetry"]["FECCorrectedCount"] = + 0; + asyncResp->res.jsonValue["Oem"]["Google"]["TransceiverTelemetry"] + ["FECUncorrectableCount"] = 0; + asyncResp->res.jsonValue["Oem"]["Google"]["TransceiverTelemetry"] + ["PerLaneFECCounters"] = nlohmann::json::array(); + asyncResp->res + .jsonValue["Oem"]["Google"]["TransceiverTelemetry"]["FECHistogramBins"] = + nlohmann::json::array(); + + managedStore::ManagedObjectStoreContext requestContext(asyncResp); + + // Query PortECC metrics + managedStore::GetManagedObjectStore()->getAllProperties( + serviceName, networkPortPath, "xyz.openbmc_project.Metrics.PortECC", + requestContext, + [asyncResp](const boost::system::error_code& ec, + const dbus::utility::DBusPropertiesMap& propertiesList) { + if (ec) { + return; + } + for (const auto& [propName, propValue] : propertiesList) { + uint64_t numVal = 0; + bool hasNum = false; + std::visit( + [&numVal, &hasNum](const auto& v) { + using T = std::decay_t<decltype(v)>; + if constexpr (std::is_arithmetic_v<T>) { + numVal = static_cast<uint64_t>(v); + hasNum = true; + } + }, + propValue); + + if (hasNum) { + if (propName == "CorrectedBits") { + asyncResp->res.jsonValue["CorrectableBitErrors"] = numVal; + asyncResp->res.jsonValue["Oem"]["Google"]["TransceiverTelemetry"] + ["FECCorrectedCount"] = numVal; + } else if (propName == "SymbolErrorRXBytes") { + asyncResp->res.jsonValue["SymbolErrors"] = numVal; + asyncResp->res.jsonValue["Oem"]["Google"]["TransceiverTelemetry"] + ["FECUncorrectableCount"] = numVal; + } + } + if (propName == "RawErrorsPerLane") { + std::visit( + [asyncResp](const auto& v) { + using T = std::decay_t<decltype(v)>; + if constexpr (std::is_same_v<T, std::vector<uint32_t>> || + std::is_same_v<T, std::vector<uint64_t>> || + std::is_same_v<T, std::vector<int32_t>> || + std::is_same_v<T, std::vector<int64_t>>) { + std::vector<uint64_t> uCounters; + uCounters.reserve(v.size()); + for (auto elem : v) { + uCounters.push_back(static_cast<uint64_t>(elem)); + } + asyncResp->res + .jsonValue["Oem"]["Google"]["TransceiverTelemetry"] + ["PerLaneFECCounters"] = uCounters; + } + }, + propValue); + } + } + }); + + // Query PortPacketCounters + managedStore::GetManagedObjectStore()->getAllProperties( + serviceName, networkPortPath, + "xyz.openbmc_project.Metrics.PortPacketCounters", requestContext, + [asyncResp](const boost::system::error_code& ec, + const dbus::utility::DBusPropertiesMap& propertiesList) { + if (ec) { + return; + } + for (const auto& [propName, propValue] : propertiesList) { + uint64_t numVal = 0; + bool hasNum = false; + std::visit( + [&numVal, &hasNum](const auto& v) { + using T = std::decay_t<decltype(v)>; + if constexpr (std::is_arithmetic_v<T>) { + numVal = static_cast<uint64_t>(v); + hasNum = true; + } + }, + propValue); + + if (hasNum) { + if (propName == "RXBroadcastPkts") { + asyncResp->res.jsonValue["RXBroadcastPackets"] = numVal; + } else if (propName == "RXMulticastPkts") { + asyncResp->res.jsonValue["RXMulticastPackets"] = numVal; + } else if (propName == "RXUnicastPkts") { + asyncResp->res.jsonValue["RXUnicastPackets"] = numVal; + } else if (propName == "TXBroadcastPkts") { + asyncResp->res.jsonValue["TXBroadcastPackets"] = numVal; + } else if (propName == "TXMulticastPkts") { + asyncResp->res.jsonValue["TXMulticastPackets"] = numVal; + } else if (propName == "TXUnicastPkts") { + asyncResp->res.jsonValue["TXUnicastPackets"] = numVal; + } + } + } + }); + + // Query EthPort metrics + managedStore::GetManagedObjectStore()->getAllProperties( + serviceName, networkPortPath, "xyz.openbmc_project.Metrics.EthPort", + requestContext, + [asyncResp](const boost::system::error_code& ec, + const dbus::utility::DBusPropertiesMap& propertiesList) { + if (ec) { + return; + } + for (const auto& [propName, propValue] : propertiesList) { + uint64_t numVal = 0; + bool hasNum = false; + std::visit( + [&numVal, &hasNum](const auto& v) { + using T = std::decay_t<decltype(v)>; + if constexpr (std::is_arithmetic_v<T>) { + numVal = static_cast<uint64_t>(v); + hasNum = true; + } + }, + propValue); + + if (hasNum) { + if (propName == "RXFCSErrors") { + asyncResp->res.jsonValue["RXFCSErrors"] = numVal; + } else if (propName == "RXAlignmentErrors") { + asyncResp->res.jsonValue["RXAlignmentErrors"] = numVal; + } else if (propName == "RXFalseCarrierDetections") { + asyncResp->res.jsonValue["RXFalseCarrierDetections"] = numVal; + } else if (propName == "RXRuntPkts") { + asyncResp->res.jsonValue["RXRuntPackets"] = numVal; + } else if (propName == "RXJabberPkts") { + asyncResp->res.jsonValue["RXJabberPackets"] = numVal; + } else if (propName == "RXXOFFFrames") { + asyncResp->res.jsonValue["PauseXOFFFramesReceived"] = numVal; + } else if (propName == "RXXONFrames") { + asyncResp->res.jsonValue["PauseXONFramesReceived"] = numVal; + } else if (propName == "TXXOFFFrames") { + asyncResp->res.jsonValue["PauseXOFFFramesTransmitted"] = numVal; + } else if (propName == "TXXONFrames") { + asyncResp->res.jsonValue["PauseXONFramesTransmitted"] = numVal; + } else if (propName == "TXSingleCollisionFrames") { + asyncResp->res.jsonValue["SingleCollisionTransmitFrames"] = + numVal; + } else if (propName == "TXMultipleCollisionFrames") { + asyncResp->res.jsonValue["MultipleCollisionTransmitFrames"] = + numVal; + } else if (propName == "TXLateCollisionFrames") { + asyncResp->res.jsonValue["LateCollisionFrames"] = numVal; + } else if (propName == "TXExcessCollisionFrames") { + asyncResp->res.jsonValue["ExcessiveCollisionFrames"] = numVal; + } + } + } + }); + + // Query IBPort metrics + managedStore::GetManagedObjectStore()->getAllProperties( + serviceName, networkPortPath, "xyz.openbmc_project.Metrics.IBPort", + requestContext, + [asyncResp](const boost::system::error_code& ec, + const dbus::utility::DBusPropertiesMap& propertiesList) { + if (ec) { + return; + } + for (const auto& [propName, propValue] : propertiesList) { + if (propName == "BitErrorRate" || propName == "EffectiveBER" || + propName == "TotalRawBER") { + double dVal = 0.0; + bool hasD = false; + std::visit( + [&dVal, &hasD](const auto& v) { + using T = std::decay_t<decltype(v)>; + if constexpr (std::is_floating_point_v<T> || + std::is_arithmetic_v<T>) { + dVal = static_cast<double>(v); + hasD = true; + } + }, + propValue); + if (hasD) { + asyncResp->res.jsonValue["Oem"]["Google"]["TransceiverTelemetry"] + [propName] = dVal; + } + } else if (propName == "LinkDownCount") { + uint64_t numVal = 0; + bool hasNum = false; + std::visit( + [&numVal, &hasNum](const auto& v) { + using T = std::decay_t<decltype(v)>; + if constexpr (std::is_arithmetic_v<T>) { + numVal = static_cast<uint64_t>(v); + hasNum = true; + } + }, + propValue); + if (hasNum) { + asyncResp->res.jsonValue["LinkDownCount"] = numVal; + } + } + } + }); + + // Query PortMetricsOem2 (RXBytes, TXBytes) + managedStore::GetManagedObjectStore()->getAllProperties( + serviceName, networkPortPath, + "xyz.openbmc_project.Metrics.PortMetricsOem2", requestContext, + [asyncResp](const boost::system::error_code& ec, + const dbus::utility::DBusPropertiesMap& propertiesList) { + if (ec) { + return; + } + for (const auto& [propName, propValue] : propertiesList) { + uint64_t numVal = 0; + bool hasNum = false; + std::visit( + [&numVal, &hasNum](const auto& v) { + using T = std::decay_t<decltype(v)>; + if constexpr (std::is_arithmetic_v<T>) { + numVal = static_cast<uint64_t>(v); + hasNum = true; + } + }, + propValue); + + if (hasNum) { + if (propName == "RXBytes") { + asyncResp->res.jsonValue["RXBytes"] = numVal; + } else if (propName == "TXBytes") { + asyncResp->res.jsonValue["TXBytes"] = numVal; + } + } + } + }); + + // Query PCIeECC + managedStore::GetManagedObjectStore()->getAllProperties( + serviceName, networkPortPath, "xyz.openbmc_project.PCIe.PCIeECC", + requestContext, + [asyncResp](const boost::system::error_code& ec, + const dbus::utility::DBusPropertiesMap& propertiesList) { + if (ec) { + return; + } + nlohmann::json& pcieObj = + asyncResp->res.jsonValue["Oem"]["Google"]["PCIeErrors"]; + for (const auto& [propName, propValue] : propertiesList) { + uint64_t val = 0; + bool hasVal = false; + std::visit( + [&val, &hasVal](const auto& v) { + using T = std::decay_t<decltype(v)>; + if constexpr (std::is_arithmetic_v<T>) { + val = static_cast<uint64_t>(v); + hasVal = true; + } + }, + propValue); + if (hasVal) { + pcieObj[propName] = val; + } + } + }); +} + inline void handleNetworkPortMetricGetDBus( - [[maybe_unused]] const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, + 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; + 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, "#PortMetrics.v1_3_0.PortMetrics", portId); + return; + } + BMCWEB_LOG_ERROR << "Failed to get " << portInventoryIntf + << " due to:" << ec.what(); + messages::internalError(asyncResp->res); + return; + } + + std::string matchingPath; + std::string matchingService; + std::string portIdSuffix1 = + absl::StrCat("/", chassisId, "/", networkAdapterId, "/", portId); + std::string portIdSuffix2 = + absl::StrCat("/", chassisId, "/NetworkAdapters/", networkAdapterId, + "/Ports/", portId); + + for (const auto& [path, connectionNames] : subtree) { + if (path.ends_with(portIdSuffix1) || path.ends_with(portIdSuffix2)) { + if (!connectionNames.empty()) { + matchingPath = path; + matchingService = connectionNames[0].first; + break; + } + } + } + + if (!matchingPath.empty()) { + buildNetworkPortMetricDBus(asyncResp, chassisId, networkAdapterId, + portId, matchingService, matchingPath); + return; + } + + messages::resourceNotFound(asyncResp->res, + "#PortMetrics.v1_3_0.PortMetrics", portId); + }); } inline void handleNetworkPortMetricGet(
diff --git a/test/redfish-core/lib/network_adapter_test.cpp b/test/redfish-core/lib/network_adapter_test.cpp index e930d05..7eb455d 100644 --- a/test/redfish-core/lib/network_adapter_test.cpp +++ b/test/redfish-core/lib/network_adapter_test.cpp
@@ -1,5 +1,6 @@ #include "network_adapter.hpp" +#include <cstdint> #include <memory> #include <vector> @@ -269,5 +270,192 @@ EXPECT_EQ(json["Oem"]["Google"]["Networking"]["ReceivedPacketsDropped"], 0); } +TEST_F(SnapshotFixture, GetNetworkPortMetricDBusReturnsCorrectResponse) { + auto* mockStore = + dynamic_cast<managedStore::MockSerializedManagedObjectStore*>( + managedStore::GetManagedObjectStore()); + + // 1. Mock port Inventory SubTree + managedStore::KeyType portSubTreeKey( + ManagedType::kManagedSubtree, "/xyz/openbmc_project/inventory", 0, + {"xyz.openbmc_project.Inventory.Item.Port"}); + dbus::utility::MapperGetSubTreeResponse portSubTree = { + {"/xyz/openbmc_project/inventory/system/chassis/CX9_0/NetworkAdapters/" + "CX9_0_NIC_1/Ports/Port_1", + {{"xyz.openbmc_project.NSM", + {"xyz.openbmc_project.Inventory.Item.Port"}}}}}; + EXPECT_TRUE( + mockStore + ->upsertMockObjectIntoManagedStore( + portSubTreeKey, + managedStore::MockManagedStoreTest::CreateValueType(portSubTree)) + .ok()); + + // 2. Mock PortECC properties + managedStore::KeyType eccKey( + ManagedType::kManagedPropertyMap, "xyz.openbmc_project.NSM", + sdbusplus::message::object_path( + "/xyz/openbmc_project/inventory/system/chassis/CX9_0/NetworkAdapters/" + "CX9_0_NIC_1/Ports/Port_1"), + "xyz.openbmc_project.Metrics.PortECC", ""); + dbus::utility::DBusPropertiesMap eccProperties; + eccProperties.emplace_back("CorrectedBits", + dbus::utility::DbusVariantType(uint64_t{42})); + eccProperties.emplace_back("SymbolErrorRXBytes", + dbus::utility::DbusVariantType(uint64_t{7})); + std::vector<uint32_t> perLaneCounters = {10, 20, 30, 40}; + eccProperties.emplace_back("RawErrorsPerLane", + dbus::utility::DbusVariantType(perLaneCounters)); + EXPECT_TRUE( + mockStore + ->upsertMockObjectIntoManagedStore( + eccKey, managedStore::MockManagedStoreTest::CreateValueType( + eccProperties)) + .ok()); + + // 3. Mock PortPacketCounters properties + managedStore::KeyType packetKey( + ManagedType::kManagedPropertyMap, "xyz.openbmc_project.NSM", + sdbusplus::message::object_path( + "/xyz/openbmc_project/inventory/system/chassis/CX9_0/NetworkAdapters/" + "CX9_0_NIC_1/Ports/Port_1"), + "xyz.openbmc_project.Metrics.PortPacketCounters", ""); + dbus::utility::DBusPropertiesMap packetProperties; + packetProperties.emplace_back("RXBroadcastPkts", + dbus::utility::DbusVariantType(uint64_t{100})); + packetProperties.emplace_back("RXMulticastPkts", + dbus::utility::DbusVariantType(uint64_t{200})); + packetProperties.emplace_back("RXUnicastPkts", + dbus::utility::DbusVariantType(uint64_t{300})); + packetProperties.emplace_back("TXBroadcastPkts", + dbus::utility::DbusVariantType(uint64_t{400})); + packetProperties.emplace_back("TXMulticastPkts", + dbus::utility::DbusVariantType(uint64_t{500})); + packetProperties.emplace_back("TXUnicastPkts", + dbus::utility::DbusVariantType(uint64_t{600})); + EXPECT_TRUE( + mockStore + ->upsertMockObjectIntoManagedStore( + packetKey, managedStore::MockManagedStoreTest::CreateValueType( + packetProperties)) + .ok()); + + // 4. Mock EthPort properties + managedStore::KeyType ethKey( + ManagedType::kManagedPropertyMap, "xyz.openbmc_project.NSM", + sdbusplus::message::object_path( + "/xyz/openbmc_project/inventory/system/chassis/CX9_0/NetworkAdapters/" + "CX9_0_NIC_1/Ports/Port_1"), + "xyz.openbmc_project.Metrics.EthPort", ""); + dbus::utility::DBusPropertiesMap ethProperties; + ethProperties.emplace_back("RXFCSErrors", + dbus::utility::DbusVariantType(uint64_t{3})); + ethProperties.emplace_back("RXAlignmentErrors", + dbus::utility::DbusVariantType(uint64_t{1})); + ethProperties.emplace_back("PauseXOFFFramesReceived", + dbus::utility::DbusVariantType(uint64_t{5})); + EXPECT_TRUE( + mockStore + ->upsertMockObjectIntoManagedStore( + ethKey, managedStore::MockManagedStoreTest::CreateValueType( + ethProperties)) + .ok()); + + // 5. Mock IBPort properties + managedStore::KeyType ibKey( + ManagedType::kManagedPropertyMap, "xyz.openbmc_project.NSM", + sdbusplus::message::object_path( + "/xyz/openbmc_project/inventory/system/chassis/CX9_0/NetworkAdapters/" + "CX9_0_NIC_1/Ports/Port_1"), + "xyz.openbmc_project.Metrics.IBPort", ""); + dbus::utility::DBusPropertiesMap ibProperties; + ibProperties.emplace_back("BitErrorRate", + dbus::utility::DbusVariantType(double{1e-12})); + ibProperties.emplace_back("LinkDownCount", + dbus::utility::DbusVariantType(uint64_t{2})); + EXPECT_TRUE( + mockStore + ->upsertMockObjectIntoManagedStore( + ibKey, + managedStore::MockManagedStoreTest::CreateValueType(ibProperties)) + .ok()); + + // 6. Mock PortMetricsOem2 properties + managedStore::KeyType oem2Key( + ManagedType::kManagedPropertyMap, "xyz.openbmc_project.NSM", + sdbusplus::message::object_path( + "/xyz/openbmc_project/inventory/system/chassis/CX9_0/NetworkAdapters/" + "CX9_0_NIC_1/Ports/Port_1"), + "xyz.openbmc_project.Metrics.PortMetricsOem2", ""); + dbus::utility::DBusPropertiesMap oem2Properties; + oem2Properties.emplace_back( + "RXBytes", dbus::utility::DbusVariantType(uint64_t{1048576})); + oem2Properties.emplace_back( + "TXBytes", dbus::utility::DbusVariantType(uint64_t{2097152})); + EXPECT_TRUE( + mockStore + ->upsertMockObjectIntoManagedStore( + oem2Key, managedStore::MockManagedStoreTest::CreateValueType( + oem2Properties)) + .ok()); + + // 7. Mock PCIeECC properties + managedStore::KeyType pcieKey( + ManagedType::kManagedPropertyMap, "xyz.openbmc_project.NSM", + sdbusplus::message::object_path( + "/xyz/openbmc_project/inventory/system/chassis/CX9_0/NetworkAdapters/" + "CX9_0_NIC_1/Ports/Port_1"), + "xyz.openbmc_project.PCIe.PCIeECC", ""); + dbus::utility::DBusPropertiesMap pcieProperties; + pcieProperties.emplace_back("ceCount", + dbus::utility::DbusVariantType(uint64_t{9})); + pcieProperties.emplace_back("ueCount", + dbus::utility::DbusVariantType(uint64_t{0})); + EXPECT_TRUE( + mockStore + ->upsertMockObjectIntoManagedStore( + pcieKey, managedStore::MockManagedStoreTest::CreateValueType( + pcieProperties)) + .ok()); + + handleNetworkPortMetricGetDBus(share_async_resp_, "CX9_0", "CX9_0_NIC_1", + "Port_1"); + + RunIoUntilDone(); + nlohmann::json& json = share_async_resp_->res.jsonValue; + EXPECT_EQ( + json["@odata.id"], + "/redfish/v1/Chassis/CX9_0/NetworkAdapters/CX9_0_NIC_1/Ports/Port_1/" + "Metrics"); + EXPECT_EQ(json["@odata.type"], "#PortMetrics.v1_3_0.PortMetrics"); + EXPECT_EQ(json["Name"], "Port_1 Metrics"); + EXPECT_EQ(json["Id"], "Metrics"); + EXPECT_EQ(json["CorrectableBitErrors"], 42); + EXPECT_EQ(json["SymbolErrors"], 7); + EXPECT_EQ(json["RXBroadcastPackets"], 100); + EXPECT_EQ(json["RXMulticastPackets"], 200); + EXPECT_EQ(json["RXUnicastPackets"], 300); + EXPECT_EQ(json["TXBroadcastPackets"], 400); + EXPECT_EQ(json["TXMulticastPackets"], 500); + EXPECT_EQ(json["TXUnicastPackets"], 600); + EXPECT_EQ(json["RXFCSErrors"], 3); + EXPECT_EQ(json["RXAlignmentErrors"], 1); + EXPECT_EQ(json["RXBytes"], 1048576); + EXPECT_EQ(json["TXBytes"], 2097152); + EXPECT_EQ(json["Oem"]["Google"]["TransceiverTelemetry"]["FECCorrectedCount"], + 42); + EXPECT_EQ( + json["Oem"]["Google"]["TransceiverTelemetry"]["FECUncorrectableCount"], + 7); + EXPECT_EQ(json["Oem"]["Google"]["TransceiverTelemetry"]["PerLaneFECCounters"], + (std::vector<uint64_t>{10, 20, 30, 40})); + EXPECT_DOUBLE_EQ(json["Oem"]["Google"]["TransceiverTelemetry"]["BitErrorRate"] + .get<double>(), + 1e-12); + EXPECT_EQ(json["LinkDownCount"], 2); + EXPECT_EQ(json["Oem"]["Google"]["PCIeErrors"]["ceCount"], 9); + EXPECT_EQ(json["Oem"]["Google"]["PCIeErrors"]["ueCount"], 0); +} + } // namespace } // namespace redfish