From c7ff2bcc893624b76bf5716157d6d99768f771da Mon Sep 17 00:00:00 2001 From: Mukesh Moopath Velayudhan Date: Mon, 25 Nov 2024 17:10:16 -0800 Subject: [PATCH] Add Flex counters bulk Dash meter stats fetch support in syncd (#1460) This PR adds syncd FlexCounter support to periodically fetch SAI bulk stats for Dash meter buckets and write the stats to COUNTERS_DB. Multiple Meter buckets, each with inbound and outbound byte counters, are internally allocated for each ENI object in the Dash pipeline. To support this a new DashMeterCounterContext class is derived from BaseCounterContext that tracks and invokes SAI bulkGetStats for all the meter bucket objects for each ENI object added to the context. Reference - https://github.com/sonic-net/DASH/blob/main/documentation/metering/metering.md --- meta/SaiSerialize.cpp | 8 + meta/sai_serialize.h | 3 + syncd/FlexCounter.cpp | 382 +++++++++++++++++++++++++++++ unittest/syncd/TestFlexCounter.cpp | 247 +++++++++++++++++++ 4 files changed, 640 insertions(+) diff --git a/meta/SaiSerialize.cpp b/meta/SaiSerialize.cpp index 3896519fc..ca528b4ff 100644 --- a/meta/SaiSerialize.cpp +++ b/meta/SaiSerialize.cpp @@ -1096,6 +1096,14 @@ std::string sai_serialize_eni_stat( return sai_serialize_enum(counter, &sai_metadata_enum_sai_eni_stat_t); } +std::string sai_serialize_meter_bucket_entry_stat( + _In_ const sai_meter_bucket_entry_stat_t counter) +{ + SWSS_LOG_ENTER(); + + return sai_serialize_enum(counter, &sai_metadata_enum_sai_meter_bucket_entry_stat_t); +} + std::string sai_serialize_tunnel_stat( _In_ const sai_tunnel_stat_t counter) { diff --git a/meta/sai_serialize.h b/meta/sai_serialize.h index 4c9c51e3d..b1733bf33 100644 --- a/meta/sai_serialize.h +++ b/meta/sai_serialize.h @@ -134,6 +134,9 @@ std::string sai_serialize_buffer_pool_stat( std::string sai_serialize_eni_stat( _In_ const sai_eni_stat_t counter); +std::string sai_serialize_meter_bucket_entry_stat( + _In_ const sai_meter_bucket_entry_stat_t counter); + std::string sai_serialize_tunnel_stat( _In_ const sai_tunnel_stat_t counter); diff --git a/syncd/FlexCounter.cpp b/syncd/FlexCounter.cpp index e77f982f4..f907f4f07 100644 --- a/syncd/FlexCounter.cpp +++ b/syncd/FlexCounter.cpp @@ -27,6 +27,7 @@ static const std::string COUNTER_TYPE_FLOW = "Flow Counter"; static const std::string COUNTER_TYPE_TUNNEL = "Tunnel Counter"; static const std::string COUNTER_TYPE_BUFFER_POOL = "Buffer Pool Counter"; static const std::string COUNTER_TYPE_ENI = "DASH ENI Counter"; +static const std::string COUNTER_TYPE_METER_BUCKET = "DASH Meter Bucket Counter"; static const std::string ATTR_TYPE_QUEUE = "Queue Attribute"; static const std::string ATTR_TYPE_PG = "Priority Group Attribute"; static const std::string ATTR_TYPE_MACSEC_SA = "MACSEC SA Attribute"; @@ -243,6 +244,14 @@ std::string serializeStat( return sai_serialize_eni_stat(stat); } +template <> +std::string serializeStat( + _In_ const sai_meter_bucket_entry_stat_t stat) +{ + SWSS_LOG_ENTER(); + return sai_serialize_meter_bucket_entry_stat(stat); +} + template void deserializeStat( _In_ const char* name, @@ -351,6 +360,15 @@ void deserializeStat( sai_deserialize_eni_stat(name, stat); } +template <> +void deserializeStat( + _In_ const char* name, + _Out_ sai_meter_bucket_entry_stat_t *stat) +{ + SWSS_LOG_ENTER(); + sai_deserialize_meter_bucket_entry_stat(name, stat); +} + template void deserializeAttr( _In_ const std::string& name, @@ -1032,6 +1050,354 @@ class AttrContext : public CounterContext } }; +class DashMeterCounterContext : public BaseCounterContext +{ +public: + DashMeterCounterContext( + _In_ const std::string &name, + _In_ sairedis::SaiInterface *vendor_sai, + _In_ std::string dbCounters): + BaseCounterContext(name), m_dbCounters(dbCounters), m_vendorSai(vendor_sai) + { + SWSS_LOG_ENTER(); + } + + void addObject( + _In_ sai_object_id_t vid, + _In_ sai_object_id_t rid, + _In_ const std::vector &idStrings, + _In_ const std::string &per_object_stats_mode) override + { + SWSS_LOG_ENTER(); + + if (m_switchId == 0UL) + { + m_switchId = m_vendorSai->switchIdQuery(rid); + } + + if (m_meterBucketsPerEni == 0) + { + if (m_initalized) { + // need not repeat these global checks for each object + return; + } + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_DASH_CAPS_MAX_METER_BUCKET_COUNT_PER_ENI; + auto status = m_vendorSai->get(SAI_OBJECT_TYPE_SWITCH, m_switchId, + 1, &attr); + if ((status != SAI_STATUS_SUCCESS) || + (attr.value.u32 == 0)) + { + m_initalized = true; + SWSS_LOG_NOTICE("No meter buckets supported per ENI for %s %s", + m_name.c_str(), sai_serialize_object_id(rid).c_str()); + return; + } + m_meterBucketsPerEni = attr.value.u32; + } + + if (m_supportedMeterCounters.empty()) + { + if (m_initalized) { + // need not repeat these global checks for each object + return; + } + getSupportedMeterCounters(rid, idStrings); + if (m_supportedMeterCounters.empty()) + { + m_initalized = true; + SWSS_LOG_NOTICE("%s %s does not have supported counters", + m_name.c_str(), sai_serialize_object_id(rid).c_str()); + return; + } + if (!checkBulkCapability(vid, rid, m_supportedMeterCounters, m_groupStatsMode)) { + m_initalized = true; + SWSS_LOG_NOTICE("%s %s does not have bulk get support for Dash meter counters", + m_name.c_str(), sai_serialize_object_id(rid).c_str()); + return; + } + } + // add object to flex counter poll + addBulkMeterContext(vid, rid); + m_initalized = true; + } + + void removeObject(_In_ sai_object_id_t vid) override + { + SWSS_LOG_ENTER(); + auto it = m_bulkMeterContexts.find(vid); + if (it == m_bulkMeterContexts.end()) { + return; + } + // delete all meter bucket stats for this object from counters DB + swss::DBConnector db(m_dbCounters, 0); + swss::RedisPipeline pipeline(&db); + swss::Table countersTable(&pipeline, COUNTERS_TABLE, true); + for (const auto& object_key: it->second.object_keys) { + countersTable.del(sai_serialize_meter_bucket_entry(object_key.key.meter_bucket_entry)); + } + // remove from flex counter poll + m_bulkMeterContexts.erase(it); + } + + void collectData(_In_ swss::Table &countersTable) override + { + SWSS_LOG_ENTER(); + for (auto &kv : m_bulkMeterContexts) + { + bulkCollectData(countersTable, kv.second); + } + } + + void runPlugin( + _In_ swss::DBConnector& counters_db, + _In_ const std::vector& argv) override + { + SWSS_LOG_ENTER(); + + if (!hasObject()) + { + return; + } + + for (auto &kv : m_bulkMeterContexts) + { + auto& ctx = kv.second; + std::vector idStrings; + idStrings.reserve(m_meterBucketsPerEni); + + for (uint32_t i = 0; i < m_meterBucketsPerEni; ++i) { + idStrings.push_back(sai_serialize_meter_bucket_entry(ctx.object_keys[i].key.meter_bucket_entry)); + } + std::for_each(m_plugins.begin(), + m_plugins.end(), + [&] (auto &sha) { runRedisScript(counters_db, sha, idStrings, argv); }); + } + } + + bool hasObject() const override + { + SWSS_LOG_ENTER(); + return !m_bulkMeterContexts.empty(); + } + +private: + struct BulkMeterStatsContext + { + sai_object_id_t eni_vid; + std::vector object_keys; + std::vector object_statuses; + std::vector counter_ids; + std::vector counters; + }; + bool bulkCollectData( + _In_ swss::Table &countersTable, + _Inout_ BulkMeterStatsContext &ctx) + { + SWSS_LOG_ENTER(); + + if (ctx.object_keys.size() == 0) + { + return false; + } + + auto statsMode = m_groupStatsMode == SAI_STATS_MODE_READ ? SAI_STATS_MODE_BULK_READ : SAI_STATS_MODE_BULK_READ_AND_CLEAR; + + sai_status_t status = m_vendorSai->bulkGetStats( + SAI_NULL_OBJECT_ID, + m_objectType, + static_cast(ctx.object_keys.size()), + ctx.object_keys.data(), + static_cast(ctx.counter_ids.size()), + reinterpret_cast(ctx.counter_ids.data()), + statsMode, + ctx.object_statuses.data(), + ctx.counters.data()); + + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_WARN("Failed to get meter bulk stats for %s: ENI 0x% " PRIx64 ": %u", m_name.c_str(), ctx.eni_vid, status); + return false; + } + + bool meter_class_hit = false; + std::vector values; + for (size_t i = 0; i < ctx.object_keys.size(); i++) + { + if (SAI_STATUS_SUCCESS != ctx.object_statuses[i]) + { + SWSS_LOG_ERROR("Failed to get meter bulk stats of %s for ENI 0x%" PRIx64 " meter-class %d : %d", + m_name.c_str(), ctx.object_keys[i].key.meter_bucket_entry.eni_id, + ctx.object_keys[i].key.meter_bucket_entry.meter_class, + ctx.object_statuses[i]); + continue; + } + for (size_t j = 0; j < ctx.counter_ids.size(); ++j) { + if (ctx.counters[i * ctx.counter_ids.size() + j] != 0UL) { + meter_class_hit = true; + break; + } + } + // write only non-zero meter classes to COUNTERS_DB + if (!meter_class_hit) { + continue; + } + meter_class_hit = false; + for (size_t j = 0; j < ctx.counter_ids.size(); j++) + { + values.emplace_back(serializeStat(ctx.counter_ids[j]), std::to_string(ctx.counters[i * ctx.counter_ids.size() + j])); + } + countersTable.set(sai_serialize_meter_bucket_entry(ctx.object_keys[i].key.meter_bucket_entry), values, ""); + values.clear(); + } + return true; + } + + bool checkBulkCapability( + _In_ sai_object_id_t vid, + _In_ sai_object_id_t rid, + _In_ const std::vector& counter_ids, + _In_ sai_stats_mode_t stats_mode) + { + SWSS_LOG_ENTER(); + + auto ctx = makeBulkMeterContext(vid, rid); + auto statsMode = stats_mode == SAI_STATS_MODE_READ ? SAI_STATS_MODE_BULK_READ : SAI_STATS_MODE_BULK_READ_AND_CLEAR; + sai_status_t status = m_vendorSai->bulkGetStats( + SAI_NULL_OBJECT_ID, + m_objectType, + static_cast(ctx.object_keys.size()), + ctx.object_keys.data(), + static_cast(ctx.counter_ids.size()), + reinterpret_cast(ctx.counter_ids.data()), + statsMode, + ctx.object_statuses.data(), + ctx.counters.data()); + return status == SAI_STATUS_SUCCESS; + } + + void updateSupportedMeterCounters( + _In_ sai_object_id_t rid, + _In_ const std::vector& counter_ids, + _In_ sai_stats_mode_t stats_mode) + { + SWSS_LOG_ENTER(); + + if (!m_supportedMeterCounters.empty() && !always_check_supported_counters) + { + SWSS_LOG_NOTICE("Ignore checking of supported counters"); + return; + } + querySupportedMeterCounters(rid, counter_ids, stats_mode); + } + + sai_status_t querySupportedMeterCounters( + _In_ sai_object_id_t rid, + _In_ const std::vector& counter_ids, + _In_ sai_stats_mode_t stats_mode) + { + SWSS_LOG_ENTER(); + sai_stat_capability_list_t stats_capability; + stats_capability.count = 0; + stats_capability.list = nullptr; + + /* First call is to check the size needed to allocate */ + sai_status_t status = m_vendorSai->queryStatsCapability(m_switchId, + m_objectType, + &stats_capability); + + /* Second call is for query statistics capability */ + if (status == SAI_STATUS_BUFFER_OVERFLOW) + { + std::vector statCapabilityList(stats_capability.count); + stats_capability.list = statCapabilityList.data(); + status = m_vendorSai->queryStatsCapability(m_switchId, + m_objectType, + &stats_capability); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_INFO("Unable to get %s supported meter counters for %s", + m_name.c_str(), + sai_serialize_object_id(rid).c_str()); + } + else + { + for (auto counter: counter_ids) { + for (auto statCapability: statCapabilityList) + { + auto currentStatModes = statCapability.stat_modes; + if (!(currentStatModes & stats_mode)) + { + continue; + } + + if (counter == static_cast(statCapability.stat_enum)) { + m_supportedMeterCounters.push_back(counter); + } + } + } + } + } + return status; + } + + void getSupportedMeterCounters(sai_object_id_t rid, + const std::vector &idStrings) + { + SWSS_LOG_ENTER(); + std::vector counter_ids; + + for (const auto &str : idStrings) + { + SWSS_LOG_INFO("id string %s", str.c_str()); + sai_meter_bucket_entry_stat_t stat; + deserializeStat(str.c_str(), &stat); + counter_ids.push_back(stat); + } + updateSupportedMeterCounters(rid, counter_ids, m_groupStatsMode); + } + + BulkMeterStatsContext makeBulkMeterContext(sai_object_id_t vid, sai_object_id_t rid) + { + SWSS_LOG_ENTER(); + BulkMeterStatsContext ctx; + + ctx.eni_vid = vid; + sai_object_key_t object_key; + object_key.key.meter_bucket_entry.eni_id = rid; + object_key.key.meter_bucket_entry.switch_id = m_switchId; + for (uint32_t i = 0; i < m_meterBucketsPerEni; ++i) { + object_key.key.meter_bucket_entry.meter_class = i; + ctx.object_keys.push_back(object_key); + } + ctx.object_statuses.resize(ctx.object_keys.size()); + ctx.counter_ids = m_supportedMeterCounters; + ctx.counters.resize(m_supportedMeterCounters.size() * ctx.object_keys.size()); + + return ctx; + } + void addBulkMeterContext(sai_object_id_t vid, sai_object_id_t rid) + { + SWSS_LOG_ENTER(); + auto it = m_bulkMeterContexts.find(vid); + if (it != m_bulkMeterContexts.end()) { + return; + } + m_bulkMeterContexts.emplace(vid, makeBulkMeterContext(vid, rid)); + } + + std::map m_bulkMeterContexts; + std::vector m_supportedMeterCounters; + sai_object_type_t m_objectType = (sai_object_type_t) SAI_OBJECT_TYPE_METER_BUCKET_ENTRY; + std::string m_dbCounters; + sairedis::SaiInterface *m_vendorSai; + sai_stats_mode_t m_groupStatsMode = SAI_STATS_MODE_READ; + sai_object_id_t m_switchId = 0UL; + uint32_t m_meterBucketsPerEni = 0; + bool m_initalized = false; +}; + FlexCounter::FlexCounter( _In_ const std::string& instanceId, _In_ std::shared_ptr vendorSai, @@ -1326,6 +1692,10 @@ std::shared_ptr FlexCounter::createCounterContext( context->always_check_supported_counters = true; return context; } + else if (context_name == COUNTER_TYPE_METER_BUCKET) + { + return std::make_shared(context_name, m_vendorSai.get(), m_dbCounters); + } else if (context_name == ATTR_TYPE_QUEUE) { return std::make_shared>(context_name, SAI_OBJECT_TYPE_QUEUE, m_vendorSai.get(), m_statsMode); @@ -1607,6 +1977,10 @@ void FlexCounter::removeCounter( { getCounterContext(COUNTER_TYPE_ENI)->removeObject(vid); } + if (hasCounterContext(COUNTER_TYPE_METER_BUCKET)) + { + getCounterContext(COUNTER_TYPE_METER_BUCKET)->removeObject(vid); + } } else if (objectType == SAI_OBJECT_TYPE_COUNTER) { @@ -1774,6 +2148,14 @@ void FlexCounter::addCounter( idStrings, ""); } + else if (objectType == (sai_object_type_t)SAI_OBJECT_TYPE_ENI && field == DASH_METER_COUNTER_ID_LIST) + { + getCounterContext(COUNTER_TYPE_METER_BUCKET)->addObject( + vid, + rid, + idStrings, + ""); + } else { SWSS_LOG_ERROR("Object type and field combination is not supported, object type %s, field %s", diff --git a/unittest/syncd/TestFlexCounter.cpp b/unittest/syncd/TestFlexCounter.cpp index 1b79e4d0d..e5d374e17 100644 --- a/unittest/syncd/TestFlexCounter.cpp +++ b/unittest/syncd/TestFlexCounter.cpp @@ -1,4 +1,5 @@ #include "FlexCounter.h" +#include "sai_serialize.h" #include "MockableSaiInterface.h" #include "MockHelper.h" #include "VirtualObjectIdManager.h" @@ -894,3 +895,249 @@ TEST(FlexCounter, counterIdChange) fc.removeCounter(oid1); countersTable.del(toOid(oid1)); } + +using dash_meter_expected_val_t = std::vector>; +constexpr uint32_t DASH_NUM_METER_BUCKETS_PER_ENI = 4094; +using VerifyDashMeterStatsFunc = std::function& counterIdNames, + const dash_meter_expected_val_t& expectedValues)>; + +void testDashMeterAddRemoveCounter( + const std::string& counterIdFieldName, + const std::vector& counterIdNames, + const dash_meter_expected_val_t& expectedValues, + VerifyDashMeterStatsFunc verifyFunc, + bool supportedCounters, + const std::string statsMode = STATS_MODE_READ) +{ + SWSS_LOG_ENTER(); + + FlexCounter fc("test", sai, "COUNTERS_DB"); + + sai_object_type_t object_type = (sai_object_type_t)SAI_OBJECT_TYPE_ENI; + test_syncd::mockVidManagerObjectTypeQuery(object_type); + + const unsigned int numOid = 5; + std::vector object_ids = generateOids(numOid, object_type); + EXPECT_EQ(object_ids.size(), numOid); + + std::vector values; + values.emplace_back(POLL_INTERVAL_FIELD, "1000"); + values.emplace_back(FLEX_COUNTER_STATUS_FIELD, "enable"); + values.emplace_back(STATS_MODE_FIELD, statsMode); + fc.addCounterPlugin(values); + + values.clear(); + values.emplace_back(counterIdFieldName, join(counterIdNames)); + for (auto object_id : object_ids) + { + fc.addCounter(object_id, object_id, values); + } + + if (supportedCounters) + { + EXPECT_EQ(fc.isEmpty(), false); + + usleep(1000*1050); + } + else + { + // No supported counter, this object shall not be queried + EXPECT_EQ(fc.isEmpty(), true); + } + swss::DBConnector db("COUNTERS_DB", 0); + swss::RedisPipeline pipeline(&db); + swss::Table countersTable(&pipeline, COUNTERS_TABLE, false); + + if (supportedCounters) + { + for (size_t i = 0; i < object_ids.size(); i++) + { + verifyFunc(countersTable, object_ids[i], counterIdNames, expectedValues); + } + } + + for (auto object_id : object_ids) + { + fc.removeCounter(object_id); + } + EXPECT_EQ(fc.isEmpty(), true); + + std::vector keys; + countersTable.getKeys(keys); + ASSERT_TRUE(keys.empty()); +} + +void dash_meter_fill_values (uint32_t object_num, uint32_t num_counters, uint64_t* counters, dash_meter_expected_val_t* str_counters) +{ + SWSS_LOG_ENTER(); + + if (object_num % 100 != 0) { + if (counters != nullptr) { + for (uint32_t i = 0; i < num_counters; i++) + { + counters[i] = 0; + } + } + return; + } + if (counters == nullptr) { + str_counters->emplace_back(); + } + for (uint32_t i = 0; i < num_counters; i++) + { + auto value = (object_num * 1000) + (i + 1) * 100; + if (counters != nullptr) { + counters[i] = value; + } else { + str_counters->back().push_back(std::to_string(value)); + } + } +}; + +TEST(FlexCounter, addRemoveDashMeterCounter) +{ + sai->mock_queryStatsCapability = [](sai_object_id_t switch_id, sai_object_type_t object_type, sai_stat_capability_list_t *stats_capability) + { + sai_stat_id_t meter_stats_cap[] = { + SAI_METER_BUCKET_ENTRY_STAT_INBOUND_BYTES, + SAI_METER_BUCKET_ENTRY_STAT_OUTBOUND_BYTES + }; + EXPECT_TRUE(object_type == (sai_object_type_t)SAI_OBJECT_TYPE_METER_BUCKET_ENTRY); + stats_capability->count = sizeof(meter_stats_cap) / sizeof(sai_stat_id_t); + if (stats_capability->list == nullptr) { + return SAI_STATUS_BUFFER_OVERFLOW; + } + for (uint32_t i = 0; i < stats_capability->count; ++i) { + stats_capability->list[i].stat_enum = meter_stats_cap[i]; + stats_capability->list[i].stat_modes = SAI_STATS_MODE_READ; + } + return SAI_STATUS_SUCCESS; + }; + + sai->mock_get = [] (sai_object_type_t objectType, sai_object_id_t objectId, uint32_t attr_count, sai_attribute_t *attr_list) + { + for (uint32_t i = 0; i < attr_count; i++) + { + if (attr_list[i].id == SAI_SWITCH_ATTR_DASH_CAPS_MAX_METER_BUCKET_COUNT_PER_ENI) + { + attr_list[i].value.u32 = DASH_NUM_METER_BUCKETS_PER_ENI; + } + } + return SAI_STATUS_SUCCESS; + }; + + sai->mock_bulkGetStats = [](sai_object_id_t, + sai_object_type_t object_type, + uint32_t object_count, + const sai_object_key_t *object_keys, + uint32_t number_of_counters, + const sai_stat_id_t *counter_ids, + sai_stats_mode_t mode, + sai_status_t *object_status, + uint64_t *counters) + { + EXPECT_TRUE(object_type == (sai_object_type_t)SAI_OBJECT_TYPE_METER_BUCKET_ENTRY); + EXPECT_TRUE(object_count == DASH_NUM_METER_BUCKETS_PER_ENI); + EXPECT_EQ(number_of_counters, 2); + for (uint32_t i = 0; i < object_count; ++i) + { + EXPECT_EQ(object_keys[i].key.meter_bucket_entry.meter_class, i); + dash_meter_fill_values(i, number_of_counters, &(counters[i * number_of_counters]), nullptr); + object_status[i] = SAI_STATUS_SUCCESS; + } + return SAI_STATUS_SUCCESS; + }; + + auto counterVerifyFunc = [] (swss::Table &countersTable, sai_object_id_t eni_id, const std::vector& counterIdNames, const dash_meter_expected_val_t& expectedValues) + { + std::string value; + for (uint32_t i = 0; i < (expectedValues.size()/counterIdNames.size()); i++) + { + auto entry_key = sai_meter_bucket_entry_t {.switch_id = 0, .eni_id = eni_id, .meter_class = i*100}; + auto key = sai_serialize_meter_bucket_entry(entry_key); + for (size_t j = 0; j < 2; ++j) { + countersTable.hget(key, counterIdNames[j], value); + EXPECT_EQ(value, expectedValues[i][j]); + } + } + }; + dash_meter_expected_val_t expectedValues; + + for (uint32_t i = 0; i < DASH_NUM_METER_BUCKETS_PER_ENI; ++i) { + dash_meter_fill_values(i, 2, nullptr, &expectedValues); + } + + testDashMeterAddRemoveCounter( + DASH_METER_COUNTER_ID_LIST, + {"SAI_METER_BUCKET_ENTRY_STAT_OUTBOUND_BYTES", "SAI_METER_BUCKET_ENTRY_STAT_INBOUND_BYTES"}, + expectedValues, + counterVerifyFunc, + true); +} + +TEST(FlexCounter, noSupportedDashMeterCounter) +{ + sai->mock_queryStatsCapability = [](sai_object_id_t switch_id, sai_object_type_t object_type, sai_stat_capability_list_t *stats_capability) { + EXPECT_TRUE(object_type == (sai_object_type_t)SAI_OBJECT_TYPE_METER_BUCKET_ENTRY); + return SAI_STATUS_FAILURE; + }; + auto counterVerifyFunc = [] (swss::Table &countersTable, sai_object_id_t eni_id, const std::vector& counterIdNames, const dash_meter_expected_val_t& expectedValues) + { + }; + dash_meter_expected_val_t expectedValues; + + testDashMeterAddRemoveCounter( + DASH_METER_COUNTER_ID_LIST, + {"SAI_METER_BUCKET_ENTRY_STAT_OUTBOUND_BYTES", "SAI_METER_BUCKET_ENTRY_STAT_INBOUND_BYTES"}, + expectedValues, + counterVerifyFunc, + false); +} + +TEST(FlexCounter, noEniDashMeterCounter) +{ + sai->mock_queryStatsCapability = [](sai_object_id_t switch_id, sai_object_type_t object_type, sai_stat_capability_list_t *stats_capability) + { + sai_stat_id_t meter_stats_cap[] = { + SAI_METER_BUCKET_ENTRY_STAT_INBOUND_BYTES, + SAI_METER_BUCKET_ENTRY_STAT_OUTBOUND_BYTES + }; + EXPECT_TRUE(object_type == (sai_object_type_t)SAI_OBJECT_TYPE_METER_BUCKET_ENTRY); + stats_capability->count = sizeof(meter_stats_cap) / sizeof(sai_stat_id_t); + if (stats_capability->list == nullptr) { + return SAI_STATUS_BUFFER_OVERFLOW; + } + for (uint32_t i = 0; i < stats_capability->count; ++i) { + stats_capability->list[i].stat_enum = meter_stats_cap[i]; + stats_capability->list[i].stat_modes = SAI_STATS_MODE_READ; + } + return SAI_STATUS_SUCCESS; + }; + + sai->mock_get = [] (sai_object_type_t objectType, sai_object_id_t objectId, uint32_t attr_count, sai_attribute_t *attr_list) + { + for (uint32_t i = 0; i < attr_count; i++) + { + if (attr_list[i].id == SAI_SWITCH_ATTR_DASH_CAPS_MAX_METER_BUCKET_COUNT_PER_ENI) + { + attr_list[i].value.u32 = 0; + } + } + return SAI_STATUS_SUCCESS; + }; + + auto counterVerifyFunc = [] (swss::Table &countersTable, sai_object_id_t eni_id, const std::vector& counterIdNames, const dash_meter_expected_val_t& expectedValues) + { + }; + dash_meter_expected_val_t expectedValues; + + testDashMeterAddRemoveCounter( + DASH_METER_COUNTER_ID_LIST, + {"SAI_METER_BUCKET_ENTRY_STAT_OUTBOUND_BYTES", "SAI_METER_BUCKET_ENTRY_STAT_INBOUND_BYTES"}, + expectedValues, + counterVerifyFunc, + false); +} +