unittest/syncd/TestFlexCounter.cpp (1,658 lines of code) (raw):

#include "FlexCounter.h" #include "sai_serialize.h" #include "MockableSaiInterface.h" #include "MockHelper.h" #include "VirtualObjectIdManager.h" #include "NumberOidIndexGenerator.h" #include <string> #include <gtest/gtest.h> using namespace saimeta; using namespace sairedis; using namespace syncd; using namespace std; std::string join(const std::vector<std::string>& input) { SWSS_LOG_ENTER(); if (input.empty()) { return ""; } std::ostringstream ostream; auto iter = input.begin(); ostream << *iter; while (++iter != input.end()) { ostream << "," << *iter; } return ostream.str(); } template <typename T> std::string toOid(T value) { SWSS_LOG_ENTER(); std::ostringstream ostream; ostream << "oid:0x" << std::hex << value; return ostream.str(); } std::shared_ptr<MockableSaiInterface> sai(new MockableSaiInterface()); typedef std::function<void(swss::Table &countersTable, const std::string& key, const std::vector<std::string>& counterIdNames, const std::vector<std::string>& expectedValues)> VerifyStatsFunc; std::vector<sai_object_id_t> generateOids( unsigned int numOid, sai_object_type_t object_type) { SWSS_LOG_ENTER(); std::vector<sai_object_id_t> object_ids; if (!numOid) return object_ids; auto scc = std::make_shared<SwitchConfigContainer>(); for (unsigned int i = 0; i < numOid; i++){ auto hw_info = "asic" + std::to_string(i); scc->insert(std::make_shared<SwitchConfig>(i, hw_info)); } auto vidManager = VirtualObjectIdManager(0, scc, std::make_shared<NumberOidIndexGenerator>()); if (object_type == SAI_OBJECT_TYPE_SWITCH) { for (unsigned int i = 0; i < numOid; i++){ auto hw_info = "asic" + std::to_string(i); object_ids.push_back(vidManager.allocateNewSwitchObjectId(hw_info)); } } else { auto sid = vidManager.allocateNewSwitchObjectId("asic0"); for (unsigned int i = 0; i < numOid; i++){ object_ids.push_back(vidManager.allocateNewObjectId(object_type, sid)); } } return object_ids; } void removeTimeStamp(std::vector<std::string>& keys, swss::Table& countersTable) { SWSS_LOG_ENTER(); auto it = std::find(keys.begin(), keys.end(), "TIME_STAMP"); if (it != keys.end()) { countersTable.del("TIME_STAMP"); keys.erase(it); } } void testAddRemoveCounter( unsigned int numOid, sai_object_type_t object_type, const std::string& counterIdFieldName, const std::vector<std::string>& counterIdNames, const std::vector<std::string>& expectedValues, VerifyStatsFunc verifyFunc, bool autoRemoveDbEntry, const std::string statsMode = STATS_MODE_READ, bool bulkAdd = false, const std::string bulkChunkSize = "", const std::string bulkChunkSizePerCounter = "", bool bulkChunkSizeAfterPort = true, const std::string pluginName = "", bool immediatelyRemoveBulkChunkSizePerCounter = false, bool forceSingleCreate = false) { SWSS_LOG_ENTER(); FlexCounter fc("test", sai, "COUNTERS_DB"); test_syncd::mockVidManagerObjectTypeQuery(object_type); std::vector<sai_object_id_t> object_ids = generateOids(numOid, object_type); EXPECT_EQ(object_ids.size(), numOid); std::vector<swss::FieldValueTuple> values; values.emplace_back(POLL_INTERVAL_FIELD, "1000"); values.emplace_back(FLEX_COUNTER_STATUS_FIELD, "enable"); values.emplace_back(STATS_MODE_FIELD, statsMode); std::vector<swss::FieldValueTuple> fcValues = values; auto &bulkChunkSizeValues = bulkChunkSizeAfterPort ? fcValues : values; if (!bulkChunkSize.empty()) { bulkChunkSizeValues.emplace_back(BULK_CHUNK_SIZE_FIELD, bulkChunkSize); } if (!bulkChunkSizePerCounter.empty()) { bulkChunkSizeValues.emplace_back(BULK_CHUNK_SIZE_PER_PREFIX_FIELD, bulkChunkSizePerCounter); } if (!pluginName.empty()) { values.emplace_back(pluginName, ""); } fc.addCounterPlugin(values); values.clear(); values.emplace_back(counterIdFieldName, join(counterIdNames)); if (bulkAdd) { fc.bulkAddCounter(object_type, object_ids, object_ids, values); } else if (forceSingleCreate) { for (auto object_id : object_ids) { fc.addCounter(object_id, object_id, values); } } else { for (auto object_id : object_ids) { std::vector<sai_object_id_t> tmp_object_ids = {object_id}; fc.bulkAddCounter(object_type, tmp_object_ids, tmp_object_ids, values); } } if (bulkChunkSizeAfterPort) { fc.addCounterPlugin(bulkChunkSizeValues); if (immediatelyRemoveBulkChunkSizePerCounter) { bulkChunkSizeValues.clear(); bulkChunkSizeValues.emplace_back(BULK_CHUNK_SIZE_PER_PREFIX_FIELD, ""); fc.addCounterPlugin(bulkChunkSizeValues); } } if (bulkChunkSizeAfterPort) { fc.addCounterPlugin(bulkChunkSizeValues); if (immediatelyRemoveBulkChunkSizePerCounter) { bulkChunkSizeValues.clear(); bulkChunkSizeValues.emplace_back(BULK_CHUNK_SIZE_PER_PREFIX_FIELD, ""); fc.addCounterPlugin(bulkChunkSizeValues); } } EXPECT_EQ(fc.isEmpty(), false); usleep(1000*1050); swss::DBConnector db("COUNTERS_DB", 0); swss::RedisPipeline pipeline(&db); swss::Table countersTable(&pipeline, COUNTERS_TABLE, false); std::vector<std::string> keys; countersTable.getKeys(keys); // We have a dedicated item for all timestamps for counters using bulk counter polling removeTimeStamp(keys, countersTable); EXPECT_EQ(keys.size(), object_ids.size()); for (size_t i = 0; i < object_ids.size(); i++) { std::string expectedKey = toOid(object_ids[i]); verifyFunc(countersTable, expectedKey, counterIdNames, expectedValues); } for (auto object_id : object_ids) { fc.removeCounter(object_id); if (!autoRemoveDbEntry) { countersTable.del(toOid(object_id)); } } EXPECT_EQ(fc.isEmpty(), true); countersTable.getKeys(keys); removeTimeStamp(keys, countersTable); ASSERT_TRUE(keys.empty()); } TEST(FlexCounter, addRemoveCounter) { sai->mock_getStatsExt = [](sai_object_type_t, sai_object_id_t, uint32_t number_of_counters, const sai_stat_id_t *, sai_stats_mode_t, uint64_t *counters) { for (uint32_t i = 0; i < number_of_counters; i++) { counters[i] = (i + 1) * 100; } return SAI_STATUS_SUCCESS; }; sai->mock_getStats = [](sai_object_type_t, sai_object_id_t, uint32_t number_of_counters, const sai_stat_id_t *, uint64_t *counters) { for (uint32_t i = 0; i < number_of_counters; i++) { counters[i] = (i + 1) * 100; } return SAI_STATUS_SUCCESS; }; sai->mock_queryStatsCapability = [](sai_object_id_t switch_id, sai_object_type_t object_type, sai_stat_capability_list_t *stats_capability) { // For now, just return failure to make test simple, will write a singe test to cover querySupportedCounters return SAI_STATUS_FAILURE; }; sai->mock_bulkGetStats = [](sai_object_id_t, sai_object_type_t, uint32_t, const sai_object_key_t *, uint32_t, const sai_stat_id_t *, sai_stats_mode_t, sai_status_t *, uint64_t *) { return SAI_STATUS_FAILURE; }; auto counterVerifyFunc = [] (swss::Table &countersTable, const std::string& key, const std::vector<std::string>& counterIdNames, const std::vector<std::string>& expectedValues) { std::string value; for (size_t i = 0; i < counterIdNames.size(); i++) { countersTable.hget(key, counterIdNames[i], value); EXPECT_EQ(value, expectedValues[i]); } }; testAddRemoveCounter( 1, SAI_OBJECT_TYPE_COUNTER, FLOW_COUNTER_ID_LIST, {"SAI_COUNTER_STAT_PACKETS", "SAI_COUNTER_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, true); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_COUNTER, FLOW_COUNTER_ID_LIST, {"SAI_COUNTER_STAT_PACKETS", "SAI_COUNTER_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, true, STATS_MODE_READ, true); testAddRemoveCounter( 1, SAI_OBJECT_TYPE_MACSEC_FLOW, MACSEC_FLOW_COUNTER_ID_LIST, {"SAI_MACSEC_FLOW_STAT_CONTROL_PKTS", "SAI_MACSEC_FLOW_STAT_PKTS_UNTAGGED"}, {"100", "200"}, counterVerifyFunc, false); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_MACSEC_FLOW, MACSEC_FLOW_COUNTER_ID_LIST, {"SAI_MACSEC_FLOW_STAT_CONTROL_PKTS", "SAI_MACSEC_FLOW_STAT_PKTS_UNTAGGED"}, {"100", "200"}, counterVerifyFunc, false, STATS_MODE_READ, true); testAddRemoveCounter( 1, SAI_OBJECT_TYPE_MACSEC_SA, MACSEC_SA_COUNTER_ID_LIST, {"SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED", "SAI_MACSEC_SA_STAT_OCTETS_PROTECTED"}, {"100", "200"}, counterVerifyFunc, false); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_MACSEC_SA, MACSEC_SA_COUNTER_ID_LIST, {"SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED", "SAI_MACSEC_SA_STAT_OCTETS_PROTECTED"}, {"100", "200"}, counterVerifyFunc, false, STATS_MODE_READ, true); testAddRemoveCounter( 1, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS"}, {"100", "200"}, counterVerifyFunc, false); testAddRemoveCounter( 1, SAI_OBJECT_TYPE_PORT, PORT_DEBUG_COUNTER_ID_LIST, {"SAI_PORT_STAT_IN_CONFIGURED_DROP_REASONS_0_DROPPED_PKTS", "SAI_PORT_STAT_IN_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS"}, {"100", "200"}, counterVerifyFunc, false); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_PORT, PORT_DEBUG_COUNTER_ID_LIST, {"SAI_PORT_STAT_IN_CONFIGURED_DROP_REASONS_0_DROPPED_PKTS", "SAI_PORT_STAT_IN_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS"}, {"100", "200"}, counterVerifyFunc, false, STATS_MODE_READ, true); bool clearCalled = false; sai->mock_clearStats = [&] (sai_object_type_t object_type, sai_object_id_t object_id, uint32_t number_of_counters, const sai_stat_id_t *counter_ids) { clearCalled = true; return SAI_STATUS_SUCCESS; }; testAddRemoveCounter( 1, SAI_OBJECT_TYPE_QUEUE, QUEUE_COUNTER_ID_LIST, {"SAI_QUEUE_STAT_PACKETS", "SAI_QUEUE_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, false, STATS_MODE_READ_AND_CLEAR); EXPECT_EQ(true, clearCalled); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_QUEUE, QUEUE_COUNTER_ID_LIST, {"SAI_QUEUE_STAT_PACKETS", "SAI_QUEUE_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, false, STATS_MODE_READ_AND_CLEAR, true); EXPECT_EQ(true, clearCalled); testAddRemoveCounter( 1, SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, PG_COUNTER_ID_LIST, {"SAI_INGRESS_PRIORITY_GROUP_STAT_PACKETS", "SAI_INGRESS_PRIORITY_GROUP_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, false); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, PG_COUNTER_ID_LIST, {"SAI_INGRESS_PRIORITY_GROUP_STAT_PACKETS", "SAI_INGRESS_PRIORITY_GROUP_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, false, STATS_MODE_READ, true); testAddRemoveCounter( 1, SAI_OBJECT_TYPE_ROUTER_INTERFACE, RIF_COUNTER_ID_LIST, {"SAI_ROUTER_INTERFACE_STAT_IN_OCTETS", "SAI_ROUTER_INTERFACE_STAT_IN_PACKETS"}, {"100", "200"}, counterVerifyFunc, false); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_ROUTER_INTERFACE, RIF_COUNTER_ID_LIST, {"SAI_ROUTER_INTERFACE_STAT_IN_OCTETS", "SAI_ROUTER_INTERFACE_STAT_IN_PACKETS"}, {"100", "200"}, counterVerifyFunc, false, STATS_MODE_READ, true); testAddRemoveCounter( 1, SAI_OBJECT_TYPE_SWITCH, SWITCH_DEBUG_COUNTER_ID_LIST, {"SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_0_DROPPED_PKTS", "SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS"}, {"100", "200"}, counterVerifyFunc, false); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_SWITCH, SWITCH_DEBUG_COUNTER_ID_LIST, {"SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_0_DROPPED_PKTS", "SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS"}, {"100", "200"}, counterVerifyFunc, false, STATS_MODE_READ, true); testAddRemoveCounter( 1, SAI_OBJECT_TYPE_TUNNEL, TUNNEL_COUNTER_ID_LIST, {"SAI_TUNNEL_STAT_IN_OCTETS", "SAI_TUNNEL_STAT_IN_PACKETS"}, {"100", "200"}, counterVerifyFunc, false); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_TUNNEL, TUNNEL_COUNTER_ID_LIST, {"SAI_TUNNEL_STAT_IN_OCTETS", "SAI_TUNNEL_STAT_IN_PACKETS"}, {"100", "200"}, counterVerifyFunc, false, STATS_MODE_READ, true); testAddRemoveCounter( 1, (sai_object_type_t)SAI_OBJECT_TYPE_ENI, ENI_COUNTER_ID_LIST, {"SAI_ENI_STAT_FLOW_CREATED", "SAI_ENI_STAT_FLOW_CREATE_FAILED", "SAI_ENI_STAT_FLOW_DELETED", "SAI_ENI_STAT_FLOW_DELETE_FAILED"}, {"100", "200", "300", "400"}, counterVerifyFunc, false); clearCalled = false; testAddRemoveCounter( 1, SAI_OBJECT_TYPE_BUFFER_POOL, BUFFER_POOL_COUNTER_ID_LIST, {"SAI_BUFFER_POOL_STAT_CURR_OCCUPANCY_BYTES", "SAI_BUFFER_POOL_STAT_WATERMARK_BYTES"}, {"100", "200"}, counterVerifyFunc, false); EXPECT_EQ(true, clearCalled); 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_QUEUE_ATTR_PAUSE_STATUS) { attr_list[i].value.booldata = false; } } return SAI_STATUS_SUCCESS; }; testAddRemoveCounter( 1, SAI_OBJECT_TYPE_QUEUE, QUEUE_ATTR_ID_LIST, {"SAI_QUEUE_ATTR_PAUSE_STATUS"}, {"false"}, counterVerifyFunc, false); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_QUEUE, QUEUE_ATTR_ID_LIST, {"SAI_QUEUE_ATTR_PAUSE_STATUS"}, {"false"}, counterVerifyFunc, false, STATS_MODE_READ, true); 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_INGRESS_PRIORITY_GROUP_ATTR_PORT) { attr_list[i].value.oid = 1; } } return SAI_STATUS_SUCCESS; }; testAddRemoveCounter( 1, SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, PG_ATTR_ID_LIST, {"SAI_INGRESS_PRIORITY_GROUP_ATTR_PORT"}, {"oid:0x1"}, counterVerifyFunc, false); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, PG_ATTR_ID_LIST, {"SAI_INGRESS_PRIORITY_GROUP_ATTR_PORT"}, {"oid:0x1"}, counterVerifyFunc, false, STATS_MODE_READ, true); 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_MACSEC_SA_ATTR_CONFIGURED_EGRESS_XPN) { attr_list[i].value.u64 = 0; } else if (attr_list[i].id == SAI_MACSEC_SA_ATTR_AN) { attr_list[i].value.u8 = 1; } } return SAI_STATUS_SUCCESS; }; testAddRemoveCounter( 1, SAI_OBJECT_TYPE_MACSEC_SA, MACSEC_SA_ATTR_ID_LIST, {"SAI_MACSEC_SA_ATTR_CONFIGURED_EGRESS_XPN", "SAI_MACSEC_SA_ATTR_AN"}, {"0", "1"}, counterVerifyFunc, false); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_MACSEC_SA, MACSEC_SA_ATTR_ID_LIST, {"SAI_MACSEC_SA_ATTR_CONFIGURED_EGRESS_XPN", "SAI_MACSEC_SA_ATTR_AN"}, {"0", "1"}, counterVerifyFunc, false, STATS_MODE_READ, true); 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_ACL_COUNTER_ATTR_PACKETS) { attr_list[i].value.u64 = 1000; } } return SAI_STATUS_SUCCESS; }; testAddRemoveCounter( 1, SAI_OBJECT_TYPE_ACL_COUNTER, ACL_COUNTER_ATTR_ID_LIST, {"SAI_ACL_COUNTER_ATTR_PACKETS"}, {"1000"}, counterVerifyFunc, false); // Bulk create mode to satisfy the coverage requirement testAddRemoveCounter( 1, SAI_OBJECT_TYPE_ACL_COUNTER, ACL_COUNTER_ATTR_ID_LIST, {"SAI_ACL_COUNTER_ATTR_PACKETS"}, {"1000"}, counterVerifyFunc, false, STATS_MODE_READ, true); testAddRemoveCounter( 1, SAI_OBJECT_TYPE_COUNTER, SRV6_COUNTER_ID_LIST, {"SAI_COUNTER_STAT_PACKETS", "SAI_COUNTER_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, false); testAddRemoveCounter( 1, SAI_OBJECT_TYPE_COUNTER, SRV6_COUNTER_ID_LIST, {"SAI_COUNTER_STAT_PACKETS", "SAI_COUNTER_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, false, STATS_MODE_READ, true); } TEST(FlexCounter, UpdateExistingCounterAddBulk) { sai->mock_bulkGetStats = [](sai_object_id_t, sai_object_type_t, uint32_t, const sai_object_key_t *, uint32_t, const sai_stat_id_t *, sai_stats_mode_t, sai_status_t *, uint64_t *) { return SAI_STATUS_NOT_SUPPORTED; }; std::string value; // Create test data for multiple objects std::vector<sai_object_id_t> vids = generateOids(1, SAI_OBJECT_TYPE_SWITCH); // Virtual IDs std::vector<sai_object_id_t> rids = generateOids(1, SAI_OBJECT_TYPE_SWITCH); // Real IDs std::vector<swss::FieldValueTuple> values; auto fc = std::make_shared<FlexCounter>("TEST_FLEX_COUNTER", sai, "COUNTERS_DB", false); swss::DBConnector db("COUNTERS_DB", 0); swss::Table countersTable(&db, COUNTERS_TABLE); std::vector<std::string> keys; values.clear(); values.emplace_back(POLL_INTERVAL_FIELD, "50"); values.emplace_back(FLEX_COUNTER_STATUS_FIELD, "enable"); fc->addCounterPlugin(values); // Add some counter IDs std::vector<std::string> counterIds = {"SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_2_DROPPED_PKTS"}; values.clear(); values.emplace_back("SWITCH_DEBUG_COUNTER_ID_LIST", join(counterIds)); fc->bulkAddCounter(SAI_OBJECT_TYPE_SWITCH, vids, rids, values); usleep(60*1000); countersTable.getKeys(keys); EXPECT_EQ(keys.size(), size_t(1)); ASSERT_TRUE(countersTable.hget(keys[0], "SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_2_DROPPED_PKTS", value)); // Update the counter IDs values.clear(); counterIds.push_back("SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_3_DROPPED_PKTS"); values.emplace_back("SWITCH_DEBUG_COUNTER_ID_LIST", join(counterIds)); fc->bulkAddCounter(SAI_OBJECT_TYPE_SWITCH, vids, rids, values); usleep(60*1000); keys.clear(); countersTable.getKeys(keys); EXPECT_EQ(keys.size(), size_t(1)); ASSERT_TRUE(countersTable.hget(keys[0], "SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_2_DROPPED_PKTS", value)); ASSERT_TRUE(countersTable.hget(keys[0], "SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_3_DROPPED_PKTS", value)); /* Cleanup */ fc->removeCounterPlugins(); countersTable.del(toOid(vids[0])); fc->removeCounter(vids[0]); } TEST(FlexCounter, queryCounterCapability) { sai->mock_queryStatsCapability = [](sai_object_id_t switch_id, sai_object_type_t object_type, sai_stat_capability_list_t *stats_capability) { if (stats_capability->count == 0) { stats_capability->count = 1; return SAI_STATUS_BUFFER_OVERFLOW; } else { stats_capability->list[0].stat_enum = SAI_PORT_STAT_IF_IN_OCTETS; stats_capability->list[0].stat_modes = SAI_STATS_MODE_READ | SAI_STATS_MODE_READ_AND_CLEAR; return SAI_STATUS_SUCCESS; } }; sai->mock_getStats = [](sai_object_type_t, sai_object_id_t, uint32_t number_of_counters, const sai_stat_id_t *, uint64_t *counters) { for (uint32_t i = 0; i < number_of_counters; i++) { counters[i] = 1000; } return SAI_STATUS_SUCCESS; }; sai->mock_clearStats = [&] (sai_object_type_t object_type, sai_object_id_t object_id, uint32_t number_of_counters, const sai_stat_id_t *counter_ids) { return SAI_STATUS_SUCCESS; }; sai->mock_bulkGetStats = [](sai_object_id_t, sai_object_type_t, uint32_t, const sai_object_key_t *, uint32_t, const sai_stat_id_t *, sai_stats_mode_t, sai_status_t *, uint64_t *) { return SAI_STATUS_FAILURE; }; auto counterVerifyFunc = [] (swss::Table &countersTable, const std::string& key, const std::vector<std::string>& counterIdNames, const std::vector<std::string>& expectedValues) { std::string value; countersTable.hget(key, "SAI_PORT_STAT_IF_IN_OCTETS", value); EXPECT_EQ(value, "1000"); // SAI_PORT_STAT_IF_IN_UCAST_PKTS is not supported, shall not in countersTable bool ret = countersTable.hget(key, "SAI_PORT_STAT_IF_IN_UCAST_PKTS", value); EXPECT_EQ(false, ret); }; testAddRemoveCounter( 1, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS"}, {}, counterVerifyFunc, false); } TEST(FlexCounter, noSupportedCounters) { sai->mock_queryStatsCapability = [](sai_object_id_t switch_id, sai_object_type_t object_type, sai_stat_capability_list_t *stats_capability) { return SAI_STATUS_FAILURE; }; sai->mock_getStats = [](sai_object_type_t, sai_object_id_t, uint32_t number_of_counters, const sai_stat_id_t *, uint64_t *counters) { return SAI_STATUS_FAILURE; }; FlexCounter fc("test", sai, "COUNTERS_DB"); std::vector<swss::FieldValueTuple> values; values.emplace_back(PORT_COUNTER_ID_LIST, "SAI_PORT_STAT_IF_IN_OCTETS,SAI_PORT_STAT_IF_IN_UCAST_PKTS"); test_syncd::mockVidManagerObjectTypeQuery(SAI_OBJECT_TYPE_PORT); fc.addCounter(sai_object_id_t(0x1000000000000), sai_object_id_t(0x1000000000000), values); // No supported counter, this object shall not be queried EXPECT_EQ(fc.isEmpty(), true); std::vector<sai_object_id_t> oids = {0x1000000000000}; fc.bulkAddCounter(SAI_OBJECT_TYPE_PORT, oids, oids, values); EXPECT_EQ(fc.isEmpty(), true); } void testAddRemovePlugin(const std::string& pluginFieldName) { SWSS_LOG_ENTER(); FlexCounter fc("test", sai, "COUNTERS_DB", true); std::vector<swss::FieldValueTuple> values; values.emplace_back(pluginFieldName, "dummy_sha_strings"); fc.addCounterPlugin(values); EXPECT_EQ(fc.isEmpty(), false); fc.removeCounterPlugins(); EXPECT_EQ(fc.isEmpty(), true); } TEST(FlexCounter, addRemoveCounterPlugin) { std::string fields[] = {QUEUE_PLUGIN_FIELD, PG_PLUGIN_FIELD, PORT_PLUGIN_FIELD, RIF_PLUGIN_FIELD, BUFFER_POOL_PLUGIN_FIELD, TUNNEL_PLUGIN_FIELD, FLOW_COUNTER_PLUGIN_FIELD, WRED_QUEUE_PLUGIN_FIELD, WRED_PORT_PLUGIN_FIELD}; for (auto &field : fields) { testAddRemovePlugin(field); } } TEST(FlexCounter, addRemoveCounterForPort) { FlexCounter fc("test", sai, "COUNTERS_DB"); sai_object_id_t counterVid{0x1000000000000}; sai_object_id_t counterRid{0x1000000000000}; std::vector<swss::FieldValueTuple> values; values.emplace_back(PORT_COUNTER_ID_LIST, "SAI_PORT_STAT_IF_IN_OCTETS,SAI_PORT_STAT_IF_IN_ERRORS"); test_syncd::mockVidManagerObjectTypeQuery(SAI_OBJECT_TYPE_PORT); sai->mock_getStats = [](sai_object_type_t, sai_object_id_t, uint32_t number_of_counters, const sai_stat_id_t *ids, uint64_t *counters) { for (uint32_t i = 0; i < number_of_counters; i++) { if (ids[i] == SAI_PORT_STAT_IF_IN_OCTETS) { counters[i] = 100; } else if (ids[i] == SAI_PORT_STAT_IF_IN_ERRORS) { counters[i] = 200; } else { return SAI_STATUS_FAILURE; } } return SAI_STATUS_SUCCESS; }; fc.addCounter(counterVid, counterRid, values); EXPECT_EQ(fc.isEmpty(), false); values.clear(); values.emplace_back(POLL_INTERVAL_FIELD, "1000"); values.emplace_back(FLEX_COUNTER_STATUS_FIELD, "enable"); values.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ); fc.addCounterPlugin(values); usleep(1000*1000); swss::DBConnector db("COUNTERS_DB", 0); swss::RedisPipeline pipeline(&db); swss::Table countersTable(&pipeline, COUNTERS_TABLE, false); std::vector<std::string> keys; countersTable.getKeys(keys); EXPECT_EQ(keys.size(), size_t(1)); std::string expectedKey = toOid(counterVid); EXPECT_EQ(keys[0], expectedKey); std::string value; countersTable.hget(expectedKey, "SAI_PORT_STAT_IF_IN_OCTETS", value); EXPECT_EQ(value, "100"); countersTable.hget(expectedKey, "SAI_PORT_STAT_IF_IN_ERRORS", value); EXPECT_EQ(value, "200"); fc.removeCounter(counterVid); EXPECT_EQ(fc.isEmpty(), true); countersTable.del(expectedKey); countersTable.getKeys(keys); ASSERT_TRUE(keys.empty()); // Test again with queryStatsCapability support sai->mock_queryStatsCapability = [](sai_object_id_t, sai_object_type_t, sai_stat_capability_list_t *capability) { if (capability->count < 2) { capability->count = 2; return SAI_STATUS_BUFFER_OVERFLOW; } capability->list[0].stat_enum = SAI_PORT_STAT_IF_IN_OCTETS; capability->list[0].stat_modes = SAI_STATS_MODE_READ | SAI_STATS_MODE_READ_AND_CLEAR; capability->list[1].stat_enum = SAI_PORT_STAT_IF_IN_ERRORS; capability->list[1].stat_modes = SAI_STATS_MODE_READ | SAI_STATS_MODE_READ_AND_CLEAR; return SAI_STATUS_SUCCESS; }; values.clear(); values.emplace_back(PORT_COUNTER_ID_LIST, "SAI_PORT_STAT_IF_IN_OCTETS,SAI_PORT_STAT_IF_IN_ERRORS"); fc.addCounter(counterVid, counterRid, values); EXPECT_EQ(fc.isEmpty(), false); usleep(1000*2000); countersTable.hget(expectedKey, "SAI_PORT_STAT_IF_IN_OCTETS", value); EXPECT_EQ(value, "100"); countersTable.hget(expectedKey, "SAI_PORT_STAT_IF_IN_ERRORS", value); EXPECT_EQ(value, "200"); fc.removeCounter(counterVid); EXPECT_EQ(fc.isEmpty(), true); countersTable.del(expectedKey); countersTable.getKeys(keys); ASSERT_TRUE(keys.empty()); } TEST(FlexCounter, bulkCounter) { sai->mock_getStatsExt = [&](sai_object_type_t, sai_object_id_t, uint32_t number_of_counters, const sai_stat_id_t *, sai_stats_mode_t, uint64_t *counters) { for (uint32_t i = 0; i < number_of_counters; i++) { counters[i] = (i + 1) * 10; } return SAI_STATUS_SUCCESS; }; sai->mock_getStats = [&](sai_object_type_t, sai_object_id_t, uint32_t number_of_counters, const sai_stat_id_t *, uint64_t *counters) { for (uint32_t i = 0; i < number_of_counters; i++) { counters[i] = (i + 1) * 10; } return SAI_STATUS_SUCCESS; }; int counterOffset = 0; int capabilities = 0; sai->mock_queryStatsCapability = [&](sai_object_id_t switch_id, sai_object_type_t object_type, sai_stat_capability_list_t *stats_capability) { // Assume all counters are in range [currentOffset, currentOffset + 100] if (stats_capability->count == 0) { stats_capability->count = 100; return SAI_STATUS_BUFFER_OVERFLOW; } else { for (int i = 0; i < 100; i++) { stats_capability->list[i].stat_enum = i + counterOffset; if (capabilities == 0) { stats_capability->list[i].stat_modes = (SAI_STATS_MODE_READ|SAI_STATS_MODE_BULK_READ|SAI_STATS_MODE_READ_AND_CLEAR|SAI_STATS_MODE_BULK_READ_AND_CLEAR|SAI_STATS_MODE_BULK_CLEAR); } else { stats_capability->list[i].stat_modes = capabilities; } } return SAI_STATUS_SUCCESS; } }; bool clearCalled = false; sai->mock_bulkGetStats = [&](sai_object_id_t, sai_object_type_t, 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(mode == SAI_STATS_MODE_BULK_READ_AND_CLEAR || mode == SAI_STATS_MODE_BULK_READ); if (mode == SAI_STATS_MODE_BULK_READ_AND_CLEAR) { clearCalled = true; } for (uint32_t i = 0; i < object_count; i++) { object_status[i] = SAI_STATUS_SUCCESS; for (uint32_t j = 0; j < number_of_counters; j++) { counters[i * number_of_counters + j] = (j + 1) * 100; } } return SAI_STATUS_SUCCESS; }; auto counterVerifyFunc = [] (swss::Table &countersTable, const std::string& key, const std::vector<std::string>& counterIdNames, const std::vector<std::string>& expectedValues) { std::string value; for (size_t i = 0; i < counterIdNames.size(); i++) { countersTable.hget(key, counterIdNames[i], value); ASSERT_EQ(value, expectedValues[i]); } }; testAddRemoveCounter( 2, SAI_OBJECT_TYPE_COUNTER, FLOW_COUNTER_ID_LIST, {"SAI_COUNTER_STAT_PACKETS", "SAI_COUNTER_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, true); testAddRemoveCounter( 2, SAI_OBJECT_TYPE_MACSEC_FLOW, MACSEC_FLOW_COUNTER_ID_LIST, {"SAI_MACSEC_FLOW_STAT_CONTROL_PKTS", "SAI_MACSEC_FLOW_STAT_PKTS_UNTAGGED"}, {"100", "200"}, counterVerifyFunc, false); testAddRemoveCounter( 2, SAI_OBJECT_TYPE_MACSEC_SA, MACSEC_SA_COUNTER_ID_LIST, {"SAI_MACSEC_SA_STAT_OCTETS_ENCRYPTED", "SAI_MACSEC_SA_STAT_OCTETS_PROTECTED"}, {"100", "200"}, counterVerifyFunc, false); testAddRemoveCounter( 2, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS"}, {"100", "200"}, counterVerifyFunc, false); counterOffset = SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE; testAddRemoveCounter( 2, SAI_OBJECT_TYPE_PORT, PORT_DEBUG_COUNTER_ID_LIST, {"SAI_PORT_STAT_IN_CONFIGURED_DROP_REASONS_0_DROPPED_PKTS", "SAI_PORT_STAT_IN_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS"}, {"100", "200"}, counterVerifyFunc, false); counterOffset = 0; testAddRemoveCounter( 2, SAI_OBJECT_TYPE_QUEUE, QUEUE_COUNTER_ID_LIST, {"SAI_QUEUE_STAT_PACKETS", "SAI_QUEUE_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, false, STATS_MODE_READ_AND_CLEAR); EXPECT_EQ(true, clearCalled); testAddRemoveCounter( 2, SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP, PG_COUNTER_ID_LIST, {"SAI_INGRESS_PRIORITY_GROUP_STAT_PACKETS", "SAI_INGRESS_PRIORITY_GROUP_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, false); testAddRemoveCounter( 2, SAI_OBJECT_TYPE_ROUTER_INTERFACE, RIF_COUNTER_ID_LIST, {"SAI_ROUTER_INTERFACE_STAT_IN_OCTETS", "SAI_ROUTER_INTERFACE_STAT_IN_PACKETS"}, {"100", "200"}, counterVerifyFunc, false); counterOffset = SAI_SWITCH_STAT_IN_DROP_REASON_RANGE_BASE; testAddRemoveCounter( 2, SAI_OBJECT_TYPE_SWITCH, SWITCH_DEBUG_COUNTER_ID_LIST, {"SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_0_DROPPED_PKTS", "SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS"}, {"100", "200"}, counterVerifyFunc, false); counterOffset = 0; testAddRemoveCounter( 2, SAI_OBJECT_TYPE_TUNNEL, TUNNEL_COUNTER_ID_LIST, {"SAI_TUNNEL_STAT_IN_OCTETS", "SAI_TUNNEL_STAT_IN_PACKETS"}, {"100", "200"}, counterVerifyFunc, false); testAddRemoveCounter( 2, (sai_object_type_t)SAI_OBJECT_TYPE_ENI, ENI_COUNTER_ID_LIST, {"SAI_ENI_STAT_FLOW_CREATED", "SAI_ENI_STAT_FLOW_CREATE_FAILED", "SAI_ENI_STAT_FLOW_DELETED", "SAI_ENI_STAT_FLOW_DELETE_FAILED"}, {"100", "200", "300", "400"}, counterVerifyFunc, false); clearCalled = false; capabilities = (SAI_STATS_MODE_READ|SAI_STATS_MODE_READ_AND_CLEAR); testAddRemoveCounter( 2, SAI_OBJECT_TYPE_BUFFER_POOL, BUFFER_POOL_COUNTER_ID_LIST, {"SAI_BUFFER_POOL_STAT_CURR_OCCUPANCY_BYTES", "SAI_BUFFER_POOL_STAT_WATERMARK_BYTES"}, {"10", "20"}, counterVerifyFunc, false); // buffer pool stats does not support bulk EXPECT_EQ(false, clearCalled); capabilities = (SAI_STATS_MODE_READ|SAI_STATS_MODE_BULK_READ); testAddRemoveCounter( 2, SAI_OBJECT_TYPE_POLICER, POLICER_COUNTER_ID_LIST, {"SAI_POLICER_STAT_PACKETS", "SAI_POLICER_STAT_ATTR_BYTES", "SAI_POLICER_STAT_GREEN_PACKETS", "SAI_POLICER_STAT_GREEN_BYTES", "SAI_POLICER_STAT_YELLOW_PACKETS", "SAI_POLICER_STAT_YELLOW_BYTES", "SAI_POLICER_STAT_RED_PACKETS", "SAI_POLICER_STAT_RED_BYTES"}, {"100", "200", "300", "400", "500", "600", "700", "800"}, counterVerifyFunc, false); testAddRemoveCounter( 2, SAI_OBJECT_TYPE_COUNTER, SRV6_COUNTER_ID_LIST, {"SAI_COUNTER_STAT_PACKETS", "SAI_COUNTER_STAT_BYTES"}, {"100", "200"}, counterVerifyFunc, false); } TEST(FlexCounter, bulkChunksize) { /* * Test logic * 1. Generate counter values and store them whenever the bulk get stat is called after initialization * 2. Convert stored counter values to string when the verify function is called * and verify whether the database content aligns with the stored values * 3. Verify whether values of all counter IDs of all objects have been generated * 4. Verify whether the bulk chunk size is correct * 5. Simulate bulk-unsupported counter IDs which should be fetched by single call * with both per counter bulk size supported or not * * A counter can be polled in initialization phase and runtime * For each test, it is expected to call bulk for "initialCheckCount" times during initialization. * In this stage, the mock function just return succeed for failure to indicate whether bulk poll is supported * but it does not provide a counter value for further check * * The calls to bulk starting from "initialCheckCount+1" are treated as runtime calls. * The counter values objects are generated as following: * - a counterSeed maintains the current counter value to return * - If the counterValuesMap[object_id][counter_id] exists, returns it as the counter's value * - Otherwise, it's the first time the (object, counter ID) is polled * - return the current value of counterSeed as the counter's value * - store the counter's value into counterValuesMap * - increase the counterSeed * When the test finishes, the counterSeed should equal (number_of_objects * number_of_bulk_supported_counters) * And all integer < counterSeed should be returned to one and only one (object, counter ID) tuple, * which can be verified by the verify function. * * For the bulk-unsupported counters, getStatExt will be called to poll counters, and return counter_id * OID as the counter's value */ bool noBulkCapabilityOnly = false; std::set<sai_stat_id_t> bulkUnsupportedCounters; std::set<sai_stat_id_t> allCounters = { SAI_PORT_STAT_IF_IN_OCTETS, SAI_PORT_STAT_IF_IN_UCAST_PKTS, SAI_PORT_STAT_IF_OUT_QLEN, SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES, SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES }; sai->mock_queryStatsCapability = [&](sai_object_id_t switch_id, sai_object_type_t object_type, sai_stat_capability_list_t *stats_capability) { std::set<sai_stat_id_t> &counterCapabilities = noBulkCapabilityOnly ? bulkUnsupportedCounters : allCounters; if (stats_capability->count == 0) { stats_capability->count = static_cast<uint32_t>(counterCapabilities.size()); return SAI_STATUS_BUFFER_OVERFLOW; } else { int i = 0; for (auto stat: counterCapabilities) { stats_capability->list[i].stat_enum = stat; stats_capability->list[i].stat_modes = noBulkCapabilityOnly ? SAI_STATS_MODE_READ : (SAI_STATS_MODE_READ|SAI_STATS_MODE_BULK_READ); i++; } return SAI_STATUS_SUCCESS; } }; // Map of number from {oid: {counter_id: counter value}} std::map<sai_object_id_t, std::map<sai_stat_id_t, sai_uint64_t>> counterValuesMap; // Map of string from {oid: {counter_id: counter value}} std::map<std::string, std::map<std::string, std::string>> expectedValuesMap; std::set<std::string> allCounterIds = { "SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES" }; std::set<std::string> allObjectIds; auto generateExpectedValues = [&]() { std::set<sai_uint64_t> allCounterValueSet; for (const auto &oidRef : counterValuesMap) { auto &expected = expectedValuesMap[toOid(oidRef.first)]; std::set<std::string> localAllCounterIds = allCounterIds; for (const auto &counters : oidRef.second) { // No duplicate counter value EXPECT_EQ(allCounterValueSet.find(counters.second), allCounterValueSet.end()); allCounterValueSet.insert(counters.second); // For each object, no unexpected counter ID const auto &counterId = sai_serialize_port_stat((sai_port_stat_t)counters.first); EXPECT_TRUE(localAllCounterIds.find(counterId) != localAllCounterIds.end()); localAllCounterIds.erase(counterId); expected[counterId] = to_string(counters.second); } // For each object, all expected counters are generated EXPECT_TRUE(localAllCounterIds.empty()); } }; // Bulk-unsupported counter IDs should be polled using single call (getStatExt) std::set<sai_object_id_t> bulkUnsupportedObjectIds; bool forceSingleCall = false; // <oid, <counter id, counter value>> auto _getStatsExt = [&](sai_object_type_t, sai_object_id_t oid, uint32_t number_of_counters, const sai_stat_id_t *counter_ids, sai_stats_mode_t, uint64_t *counters) { for (auto i = 0u; i < number_of_counters; i++) { if (forceSingleCall || (bulkUnsupportedCounters.find(counter_ids[i]) != bulkUnsupportedCounters.end() && (bulkUnsupportedObjectIds.empty() || bulkUnsupportedObjectIds.find(oid) != bulkUnsupportedObjectIds.end()))) { // avoid counter_id == 0 which causes the same counter value (0) for all objects counters[i] = (1 + counter_ids[i]) * (uint64_t)oid; if (counterValuesMap.find(oid) == counterValuesMap.end()) { counterValuesMap[oid] = {}; } counterValuesMap[oid][counter_ids[i]] = counters[i]; } } return SAI_STATUS_SUCCESS; }; sai->mock_getStats = [&](sai_object_type_t type, sai_object_id_t oid, uint32_t number_of_counters, const sai_stat_id_t *counter_ids, uint64_t *counters) { return _getStatsExt(type, oid, number_of_counters, counter_ids, SAI_STATS_MODE_READ, counters); }; sai->mock_getStatsExt = [&](sai_object_type_t type, sai_object_id_t oid, uint32_t number_of_counters, const sai_stat_id_t *counter_ids, sai_stats_mode_t mode, uint64_t *counters) { return _getStatsExt(type, oid, number_of_counters, counter_ids, mode, counters); }; std::vector<std::vector<sai_stat_id_t>> counterRecord; std::vector<std::vector<uint64_t>> valueRecord; sai_uint64_t counterSeed = 0; // non zero unifiedBulkChunkSize indicates all counter IDs share the same bulk chunk size uint32_t unifiedBulkChunkSize = 0; int32_t initialCheckCount; int32_t partialSupportingBulkObjectFactor; sai->mock_bulkGetStats = [&](sai_object_id_t, sai_object_type_t, 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(mode == SAI_STATS_MODE_BULK_READ); std::vector<sai_stat_id_t> record; std::vector<uint64_t> value; if (initialCheckCount-- > 0) { allObjectIds.insert(toOid(object_keys[0].key.object_id)); // This call is to check whether bulk counter polling is supported during initialization if (!bulkUnsupportedCounters.empty()) { // Simulate counters that are not supported being polled in bulk mode for (auto i = 0u; i < number_of_counters; i++) { if (bulkUnsupportedCounters.find(counter_ids[i]) != bulkUnsupportedCounters.end()) { if (partialSupportingBulkObjectFactor != 0) { for(auto j = 0u; j < object_count; j++) { if (j % partialSupportingBulkObjectFactor == 0) { bulkUnsupportedObjectIds.insert(object_keys[j].key.object_id); } } } return SAI_STATUS_FAILURE; } } } return SAI_STATUS_SUCCESS; } EXPECT_TRUE(!forceSingleCall); for (uint32_t i = 0; i < object_count; i++) { object_status[i] = SAI_STATUS_SUCCESS; auto &counterMap = counterValuesMap[object_keys[i].key.object_id]; for (uint32_t j = 0; j < number_of_counters; j++) { const auto &searchRef = counterMap.find(counter_ids[j]); if (searchRef == counterMap.end()) { counterMap[counter_ids[j]] = ++counterSeed; } counters[i * number_of_counters + j] = counterMap[counter_ids[j]]; record.emplace_back(counter_ids[j]); value.emplace_back(counterSeed); if (unifiedBulkChunkSize > 0) { if (object_count != unifiedBulkChunkSize) { EXPECT_EQ(object_count, unifiedBulkChunkSize); } continue; } switch (counter_ids[j]) { case SAI_PORT_STAT_IF_IN_OCTETS: case SAI_PORT_STAT_IF_IN_UCAST_PKTS: // default chunk size 2, object number 6, object count 6 / 2 = 3 EXPECT_EQ(object_count, 3); break; case SAI_PORT_STAT_IF_OUT_QLEN: // queue length chunk size 0, object number 6, object count 6 EXPECT_EQ(object_count, 6); break; case SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES: case SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES: // FEC chunk size 2, object number 6, object count 6 / 3 = 2 EXPECT_EQ(object_count, 2); default: break; } } } return SAI_STATUS_SUCCESS; }; auto counterVerifyFunc = [&] (swss::Table &countersTable, const std::string& key, const std::vector<std::string>& counterIdNames, const std::vector<std::string>&) { std::string value; if (expectedValuesMap.empty()) { generateExpectedValues(); } auto const &searchRef = expectedValuesMap.find(key); ASSERT_TRUE(searchRef != expectedValuesMap.end()); auto &oidCounters = searchRef->second; for (auto const &counter : counterIdNames) { countersTable.hget(key, counter, value); EXPECT_EQ(value, oidCounters[counter]); oidCounters.erase(counter); } EXPECT_TRUE(oidCounters.empty()); expectedValuesMap.erase(searchRef); allObjectIds.erase(key); }; // create ports first and then set bulk chunk size + per counter bulk chunk size initialCheckCount = 6; testAddRemoveCounter( 6, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES"}, {}, counterVerifyFunc, false, STATS_MODE_READ, false, "3", "SAI_PORT_STAT_IF_OUT_QLEN:0;SAI_PORT_STAT_IF_IN_FEC:2"); EXPECT_TRUE(allObjectIds.empty()); // set bulk chunk size + per counter bulk chunk size first and then create ports initialCheckCount = 6; testAddRemoveCounter( 6, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES"}, {}, counterVerifyFunc, false, STATS_MODE_READ, false, "3", "SAI_PORT_STAT_IF_OUT_QLEN:0;SAI_PORT_STAT_IF_IN_FEC:2", false, PORT_PLUGIN_FIELD, false, true); EXPECT_TRUE(allObjectIds.empty()); // Remove per counter bulk chunk size after initializing it // This is to cover the scenario of removing per counter bulk chunk size filed // All counters share a unified bulk chunk size unifiedBulkChunkSize = 3; initialCheckCount = 6; testAddRemoveCounter( 6, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES"}, {}, counterVerifyFunc, false, STATS_MODE_READ, false, "3", "SAI_PORT_STAT_IF_OUT_QLEN:0;SAI_PORT_STAT_IF_IN_FEC:2", true, "", true); EXPECT_TRUE(allObjectIds.empty()); unifiedBulkChunkSize = 0; // add ports counters in bulk mode first and then set bulk chunk size + per counter bulk chunk size initialCheckCount = 3; testAddRemoveCounter( 6, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES"}, {}, counterVerifyFunc, false, STATS_MODE_READ, true, "3", "SAI_PORT_STAT_IF_OUT_QLEN:0;SAI_PORT_STAT_IF_IN_FEC:2"); EXPECT_TRUE(allObjectIds.empty()); // set bulk chunk size + per counter bulk chunk size first and then add ports counters in bulk mode initialCheckCount = 3; testAddRemoveCounter( 6, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES"}, {}, counterVerifyFunc, false, STATS_MODE_READ, true, "3", "SAI_PORT_STAT_IF_OUT_QLEN:0;SAI_PORT_STAT_IF_IN_FEC:2", false, PORT_PLUGIN_FIELD); EXPECT_TRUE(allObjectIds.empty()); // add ports counters in bulk mode with some bulk-unsupported counters first and then set bulk chunk size + per counter bulk chunk size // all counters will be polled using single call in runtime forceSingleCall = true; initialCheckCount = 1; // check bulk for all counter IDs altogether initialCheckCount += 6; // for bulk unsupported counter prefix, check bulk again for each objects bulkUnsupportedCounters = { SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES, SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES }; testAddRemoveCounter( 6, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES"}, {}, counterVerifyFunc, false, STATS_MODE_READ, true, "3", "SAI_PORT_STAT_IF_OUT_QLEN:0;SAI_PORT_STAT_IF_IN_FEC:2"); EXPECT_TRUE(allObjectIds.empty()); forceSingleCall = false; forceSingleCall = true; noBulkCapabilityOnly = true; initialCheckCount = 0; // check bulk for all counter IDs altogether bulkUnsupportedCounters = { SAI_PORT_STAT_IF_IN_OCTETS, SAI_PORT_STAT_IF_IN_UCAST_PKTS, SAI_PORT_STAT_IF_OUT_QLEN, SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES, SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES }; testAddRemoveCounter( 6, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES"}, {}, counterVerifyFunc, false, STATS_MODE_READ, true); EXPECT_TRUE(allObjectIds.empty()); bulkUnsupportedCounters.clear(); noBulkCapabilityOnly = false; forceSingleCall = false; // set bulk chunk size + per counter bulk chunk size first and then add ports counters in bulk mode with some bulk-unsupported counters // All bulk-unsupported counters are polled using single call and all the rest counters are polled using bulk call // For each OID, it will be in both m_bulkContexts and m_objectIdsMap initialCheckCount = 3; // check bulk for 3 prefixes initialCheckCount += 6; // for bulk unsupported counter prefix, check bulk again for each objects testAddRemoveCounter( 6, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES"}, {}, counterVerifyFunc, false, STATS_MODE_READ, true, "3", "SAI_PORT_STAT_IF_OUT_QLEN:0;SAI_PORT_STAT_IF_IN_FEC:2", false, PORT_PLUGIN_FIELD); EXPECT_TRUE(allObjectIds.empty()); // set bulk chunk size + per counter bulk chunk size first and then add ports counters in bulk mode with some bulk-unsupported counters // All bulk-unsupported counters are polled using single call and all the rest counters are polled using bulk call // For each OID, it will be in both m_bulkContexts and m_objectIdsMap initialCheckCount = 3; // check bulk for 3 prefixes initialCheckCount += 6; // for bulk unsupported counter prefix, check bulk again for each objects partialSupportingBulkObjectFactor = 2; testAddRemoveCounter( 6, SAI_OBJECT_TYPE_PORT, PORT_COUNTER_ID_LIST, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS", "SAI_PORT_STAT_IF_OUT_QLEN", "SAI_PORT_STAT_IF_IN_FEC_CORRECTABLE_FRAMES", "SAI_PORT_STAT_IF_IN_FEC_NOT_CORRECTABLE_FRAMES"}, {}, counterVerifyFunc, false, STATS_MODE_READ, true, "3", "SAI_PORT_STAT_IF_OUT_QLEN:0;SAI_PORT_STAT_IF_IN_FEC:2", false, PORT_PLUGIN_FIELD); EXPECT_TRUE(allObjectIds.empty()); } TEST(FlexCounter, counterIdChange) { sai->mock_queryStatsCapability = [](sai_object_id_t, sai_object_type_t, sai_stat_capability_list_t *capability) { return SAI_STATUS_FAILURE; }; sai->mock_getStats = [&](sai_object_type_t, sai_object_id_t, uint32_t number_of_counters, const sai_stat_id_t *, uint64_t *counters) { for (uint32_t i = 0; i < number_of_counters; i++) { counters[i] = (i + 1) * 10; } return SAI_STATUS_SUCCESS; }; sai->mock_bulkGetStats = [&](sai_object_id_t, sai_object_type_t, 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) { for (uint32_t i = 0; i < number_of_counters; i++) { switch(counter_ids[i]) { case SAI_PORT_STAT_IF_IN_OCTETS: case SAI_PORT_STAT_IF_IN_UCAST_PKTS: break; default: return SAI_STATUS_NOT_SUPPORTED; } } for (uint32_t i = 0; i < object_count; i++) { object_status[i] = SAI_STATUS_SUCCESS; for (uint32_t j = 0; j < number_of_counters; j++) { counters[i * number_of_counters + j] = (j + 1) * 100; } } return SAI_STATUS_SUCCESS; }; auto counterVerifyFunc = [] (swss::Table &countersTable, const std::string& key, const std::vector<std::string>& counterIdNames, const std::vector<std::string>& expectedValues) { std::string value; for (size_t i = 0; i < counterIdNames.size(); i++) { countersTable.hget(key, counterIdNames[i], value); ASSERT_EQ(value, expectedValues[i]); } }; FlexCounter fc("test", sai, "COUNTERS_DB"); test_syncd::mockVidManagerObjectTypeQuery(SAI_OBJECT_TYPE_PORT); std::vector<swss::FieldValueTuple> values; values.emplace_back(POLL_INTERVAL_FIELD, "1000"); values.emplace_back(FLEX_COUNTER_STATUS_FIELD, "enable"); values.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ); fc.addCounterPlugin(values); values.clear(); values.emplace_back(PORT_COUNTER_ID_LIST, "SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS,SAI_PORT_STAT_IF_IN_DISCARDS"); sai_object_id_t oid{0x1000000000000}; fc.addCounter(oid, oid, values); usleep(1000*1050); swss::DBConnector db("COUNTERS_DB", 0); swss::RedisPipeline pipeline(&db); swss::Table countersTable(&pipeline, COUNTERS_TABLE, false); std::vector<std::string> keys; countersTable.getKeys(keys); EXPECT_EQ(keys.size(),1); std::string expectedKey = toOid(oid); counterVerifyFunc(countersTable, expectedKey, {"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS", "SAI_PORT_STAT_IF_IN_DISCARDS"}, {"10", "20"}); // not support bulk to support bulk values.clear(); values.emplace_back(PORT_COUNTER_ID_LIST, "SAI_PORT_STAT_IF_IN_OCTETS,SAI_PORT_STAT_IF_IN_UCAST_PKTS"); fc.addCounter(oid, oid, values); usleep(1000*1050); counterVerifyFunc(countersTable, expectedKey, {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS"}, {"100", "200"}); // support bulk but counter id changes values.clear(); values.emplace_back(PORT_COUNTER_ID_LIST, "SAI_PORT_STAT_IF_IN_OCTETS"); fc.addCounter(oid, oid, values); usleep(1000*1050); counterVerifyFunc(countersTable, expectedKey, {"SAI_PORT_STAT_IF_IN_OCTETS"}, {"100"}); // support bulk with different counter id sai_object_id_t oid1{0x1000000000001}; values.clear(); values.emplace_back(PORT_COUNTER_ID_LIST, "SAI_PORT_STAT_IF_IN_OCTETS,SAI_PORT_STAT_IF_IN_UCAST_PKTS"); fc.addCounter(oid1, oid1, values); usleep(1000*1050); counterVerifyFunc(countersTable, toOid(oid1), {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS"}, {"100", "200"}); // support bulk to not support bulk values.clear(); values.emplace_back(PORT_COUNTER_ID_LIST, "SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS,SAI_PORT_STAT_IF_IN_UCAST_PKTS"); fc.addCounter(oid, oid, values); usleep(1000*1050); counterVerifyFunc(countersTable, expectedKey, {"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS"}, {"10", "20"}); // not support bulk but counter id changes values.clear(); values.emplace_back(PORT_COUNTER_ID_LIST, "SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS,SAI_PORT_STAT_IF_IN_DISCARDS"); fc.addCounter(oid, oid, values); usleep(1000*1050); counterVerifyFunc(countersTable, expectedKey, {"SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS", "SAI_PORT_STAT_IF_IN_DISCARDS"}, {"10", "20"}); // verify oid1 is still using bulk counterVerifyFunc(countersTable, toOid(oid1), {"SAI_PORT_STAT_IF_IN_OCTETS", "SAI_PORT_STAT_IF_IN_UCAST_PKTS"}, {"100", "200"}); fc.removeCounter(oid); countersTable.del(expectedKey); fc.removeCounter(oid1); countersTable.del(toOid(oid1)); } using dash_meter_expected_val_t = std::vector<std::vector<std::string>>; constexpr uint32_t DASH_NUM_METER_BUCKETS_PER_ENI = 4094; using VerifyDashMeterStatsFunc = std::function<void(swss::Table &countersTable, sai_object_id_t eni_id, const std::vector<std::string>& counterIdNames, const dash_meter_expected_val_t& expectedValues)>; void testDashMeterAddRemoveCounter( const std::string& counterIdFieldName, const std::vector<std::string>& 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<sai_object_id_t> object_ids = generateOids(numOid, object_type); EXPECT_EQ(object_ids.size(), numOid); std::vector<swss::FieldValueTuple> 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<std::string> keys; countersTable.getKeys(keys); removeTimeStamp(keys, countersTable); 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<std::string>& 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<std::string>& 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<std::string>& 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); }