diff --git a/meta/SaiSerialize.cpp b/meta/SaiSerialize.cpp index c07880062..b1b2ed3cb 100644 --- a/meta/SaiSerialize.cpp +++ b/meta/SaiSerialize.cpp @@ -1007,6 +1007,14 @@ std::string sai_serialize_tunnel_stat( return sai_serialize_enum(counter, &sai_metadata_enum_sai_tunnel_stat_t); } +std::string sai_serialize_counter_stat( + _In_ const sai_counter_stat_t counter) +{ + SWSS_LOG_ENTER(); + + return sai_serialize_enum(counter, &sai_metadata_enum_sai_counter_stat_t); +} + std::string sai_serialize_queue_attr( _In_ const sai_queue_attr_t attr) { diff --git a/meta/sai_serialize.h b/meta/sai_serialize.h index 2471609c8..69b1f482e 100644 --- a/meta/sai_serialize.h +++ b/meta/sai_serialize.h @@ -122,6 +122,9 @@ std::string sai_serialize_buffer_pool_stat( std::string sai_serialize_tunnel_stat( _In_ const sai_tunnel_stat_t counter); +std::string sai_serialize_counter_stat( + _In_ const sai_counter_stat_t counter); + std::string sai_serialize_queue_attr( _In_ const sai_queue_attr_t attr); diff --git a/syncd/FlexCounter.cpp b/syncd/FlexCounter.cpp index 30852a6e0..7b54afc0e 100644 --- a/syncd/FlexCounter.cpp +++ b/syncd/FlexCounter.cpp @@ -10,6 +10,7 @@ #include using namespace syncd; +using namespace std; #define MUTEX std::unique_lock _lock(m_mtx); #define MUTEX_UNLOCK _lock.unlock(); @@ -18,6 +19,7 @@ FlexCounter::FlexCounter( _In_ const std::string& instanceId, _In_ std::shared_ptr vendorSai, _In_ const std::string& dbCounters): + m_readyToPoll(false), m_pollInterval(0), m_instanceId(instanceId), m_vendorSai(vendorSai), @@ -133,6 +135,16 @@ FlexCounter::TunnelCounterIds::TunnelCounterIds( // empty intentionally } +FlexCounter::FlowCounterIds::FlowCounterIds( + _In_ sai_object_id_t counter, + _In_ const std::vector &flowCounters): + counterId(counter), + flowCounterIds(flowCounters) +{ + SWSS_LOG_ENTER(); + // empty intentionally +} + void FlexCounter::setPollInterval( _In_ uint32_t pollInterval) { @@ -649,6 +661,44 @@ void FlexCounter::setTunnelCounterList( addCollectCountersHandler(TUNNEL_COUNTER_ID_LIST, &FlexCounter::collectTunnelCounters); } +void FlexCounter::setFlowCounterList( + _In_ sai_object_id_t counterVid, + _In_ sai_object_id_t counterRid, + _In_ const std::vector& counterIds) +{ + SWSS_LOG_ENTER(); + + updateSupportedFlowCounters(counterRid, counterIds); + + std::vector supportedIds; + for (auto &counter : counterIds) + { + if (m_supportedFlowCounters.count(counter) != 0) + { + supportedIds.push_back(counter); + } + } + + if (supportedIds.empty()) + { + SWSS_LOG_NOTICE("Flow counter %s does not have supported counters", sai_serialize_object_id(counterRid).c_str()); + return; + } + + auto it = m_flowCounterIdsMap.find(counterVid); + if (it != m_flowCounterIdsMap.end()) + { + it->second->flowCounterIds = supportedIds; + return; + } + + auto flowCounterIds = std::make_shared(counterRid, supportedIds); + + m_flowCounterIdsMap.emplace(counterVid, flowCounterIds); + + addCollectCountersHandler(FLOW_COUNTER_ID_LIST, &FlexCounter::collectFlowCounters); +} + void FlexCounter::removePort( _In_ sai_object_id_t portVid) { @@ -824,6 +874,38 @@ void FlexCounter::removeAclCounter( } } +void FlexCounter::removeFlowCounter( + _In_ sai_object_id_t counterVid) +{ + SWSS_LOG_ENTER(); + + auto it = m_flowCounterIdsMap.find(counterVid); + + if (it == m_flowCounterIdsMap.end()) + { + SWSS_LOG_NOTICE("Trying to remove nonexisting flow counter from Id 0x%" PRIx64, counterVid); + return; + } + + swss::DBConnector db(m_dbCounters, 0); + swss::RedisPipeline pipeline(&db); + swss::Table countersTable(&pipeline, COUNTERS_TABLE, false); + swss::Table ratesTable(&pipeline, RATES_TABLE, false); + + // Remove counter and rate entries from COUNTER DB to avoid resource leak + std::string counterVidStr = sai_serialize_object_id(counterVid); + countersTable.del(counterVidStr); + ratesTable.del(counterVidStr); + ratesTable.del(counterVidStr + ":TRAP"); + + m_flowCounterIdsMap.erase(it); + + if (m_flowCounterIdsMap.empty()) + { + removeCollectCountersHandler(FLOW_COUNTER_ID_LIST); + } +} + void FlexCounter::removeRif( _In_ sai_object_id_t rifVid) { @@ -924,7 +1006,8 @@ void FlexCounter::checkPluginRegistered( m_queuePlugins.find(sha) != m_queuePlugins.end() || m_priorityGroupPlugins.find(sha) != m_priorityGroupPlugins.end() || m_bufferPoolPlugins.find(sha) != m_bufferPoolPlugins.end() || - m_tunnelPlugins.find(sha) != m_tunnelPlugins.end() + m_tunnelPlugins.find(sha) != m_tunnelPlugins.end() || + m_flowCounterPlugins.find(sha) != m_flowCounterPlugins.end() ) { SWSS_LOG_ERROR("Plugin %s already registered", sha.c_str()); @@ -967,6 +1050,18 @@ void FlexCounter::addQueueCounterPlugin( SWSS_LOG_NOTICE("Queue counters plugin %s registered", sha.c_str()); } +void FlexCounter::addFlowCounterPlugin( + _In_ const std::string& sha) +{ + SWSS_LOG_ENTER(); + + checkPluginRegistered(sha); + + m_flowCounterPlugins.insert(sha); + + SWSS_LOG_NOTICE("Flow counters plugin %s registered", sha.c_str()); +} + void FlexCounter::addPriorityGroupCounterPlugin( _In_ const std::string& sha) { @@ -1015,6 +1110,7 @@ void FlexCounter::removeCounterPlugins() m_priorityGroupPlugins.clear(); m_bufferPoolPlugins.clear(); m_tunnelPlugins.clear(); + m_flowCounterPlugins.clear(); m_isDiscarded = true; } @@ -1089,6 +1185,13 @@ void FlexCounter::addCounterPlugin( addTunnelCounterPlugin(sha); } } + else if (field == FLOW_COUNTER_PLUGIN_FIELD) + { + for (auto& sha: shaStrings) + { + addFlowCounterPlugin(sha); + } + } else { SWSS_LOG_ERROR("Field is not supported %s", field.c_str()); @@ -1096,7 +1199,7 @@ void FlexCounter::addCounterPlugin( } // notify thread to start polling - m_pollCond.notify_all(); + notifyPoll(); } bool FlexCounter::isEmpty() @@ -1130,7 +1233,8 @@ bool FlexCounter::allIdsEmpty() const m_switchDebugCounterIdsMap.empty() && m_macsecSAAttrIdsMap.empty() && m_aclCounterAttrIdsMap.empty() && - m_tunnelCounterIdsMap.empty(); + m_tunnelCounterIdsMap.empty() && + m_flowCounterIdsMap.empty(); } bool FlexCounter::allPluginsEmpty() const @@ -1142,7 +1246,8 @@ bool FlexCounter::allPluginsEmpty() const m_portPlugins.empty() && m_rifPlugins.empty() && m_bufferPoolPlugins.empty() && - m_tunnelPlugins.empty(); + m_tunnelPlugins.empty() && + m_flowCounterPlugins.empty(); } bool FlexCounter::isPortCounterSupported(sai_port_stat_t counter) const @@ -1542,6 +1647,50 @@ void FlexCounter::collectSwitchDebugCounters( } } +void FlexCounter::collectFlowCounters( + _In_ swss::Table &countersTable) +{ + SWSS_LOG_ENTER(); + + // Collect stats for every registered flow counter + for (const auto &kv: m_flowCounterIdsMap) + { + const auto &counterVid = kv.first; + const auto &counterRId = kv.second->counterId; + const auto &counterIds = kv.second->flowCounterIds; + + std::vector stats(counterIds.size()); + + // Get flow counter stats + sai_status_t status = m_vendorSai->getStatsExt( + SAI_OBJECT_TYPE_COUNTER, + counterRId, + static_cast(counterIds.size()), + (const sai_stat_id_t *)counterIds.data(), + SAI_STATS_MODE_READ, + stats.data()); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get stats of flow counter 0x%" PRIx64 ": %d", counterRId, status); + continue; + } + + // Push all counter values to a single vector + std::vector values; + + for (size_t i = 0; i != counterIds.size(); i++) + { + values.emplace_back(sai_serialize_counter_stat(counterIds[i]), std::to_string(stats[i])); + } + + // Write counters to DB + std::string counterVidStr = sai_serialize_object_id(counterVid); + + countersTable.set(counterVidStr, values, ""); + } +} + void FlexCounter::collectPriorityGroupAttrs( _In_ swss::Table &countersTable) { @@ -1903,6 +2052,20 @@ void FlexCounter::runPlugins( runRedisScript(counters_db, sha, queueList, argv); } + std::vector flowCounterList; + + flowCounterList.reserve(m_flowCounterIdsMap.size()); + + for (const auto& kv : m_flowCounterIdsMap) + { + flowCounterList.push_back(sai_serialize_object_id(kv.first)); + } + + for (const auto& sha : m_flowCounterPlugins) + { + runRedisScript(counters_db, sha, flowCounterList, argv); + } + std::vector priorityGroupList; priorityGroupList.reserve(m_priorityGroupCounterIdsMap.size()); @@ -1984,10 +2147,7 @@ void FlexCounter::flexCounterThreadRunFunction() MUTEX_UNLOCK; // explicit unlock // nothing to collect, wait until notified - - std::unique_lock lk(m_mtxSleep); - - m_pollCond.wait(lk); // wait on mutex + waitPoll(); } } @@ -2015,7 +2175,7 @@ void FlexCounter::endFlexCounterThread(void) m_runFlexCounterThread = false; - m_pollCond.notify_all(); + notifyPoll(); m_cvSleep.notify_all(); @@ -2463,6 +2623,43 @@ void FlexCounter::getSupportedRifCounters( } } +void FlexCounter::updateSupportedFlowCounters( + _In_ sai_object_id_t counterRid, + _In_ const std::vector &counterIds) +{ + SWSS_LOG_ENTER(); + + if (!m_supportedFlowCounters.empty()) + { + return; + } + + uint64_t value; + for (auto &counter : counterIds) + { + sai_status_t status = m_vendorSai->getStatsExt( + SAI_OBJECT_TYPE_COUNTER, + counterRid, + 1, + (const sai_stat_id_t *)&counter, + SAI_STATS_MODE_READ, + &value); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("%s: counter %s is not supported on flow counter %s, rv: %s", + m_instanceId.c_str(), + sai_serialize_counter_stat(counter).c_str(), + sai_serialize_object_id(counterRid).c_str(), + sai_serialize_status(status).c_str()); + + continue; + } + + m_supportedFlowCounters.insert(counter); + } +} + void FlexCounter::updateSupportedRifCounters( _In_ sai_object_id_t rifRid) { @@ -2722,6 +2919,10 @@ void FlexCounter::removeCounter( { removeTunnel(vid); } + else if (objectType == SAI_OBJECT_TYPE_COUNTER) + { + removeFlowCounter(vid); + } else { SWSS_LOG_ERROR("Object type for removal not supported, %s", @@ -2882,6 +3083,19 @@ void FlexCounter::addCounter( setAclCounterAttrList(vid, rid, aclCounterIds); } + else if (objectType == SAI_OBJECT_TYPE_COUNTER && field == FLOW_COUNTER_ID_LIST) + { + std::vector counterStatIds; + + for (const auto &str : idStrings) + { + sai_counter_stat_t stat; + sai_deserialize_counter_stat(str.c_str(), &stat); + counterStatIds.push_back(stat); + } + + setFlowCounterList(vid, rid, counterStatIds); + } else if (objectType == SAI_OBJECT_TYPE_BUFFER_POOL && field == BUFFER_POOL_COUNTER_ID_LIST) { counterIds = idStrings; @@ -2928,5 +3142,21 @@ void FlexCounter::addCounter( } // notify thread to start polling + notifyPoll(); +} + +void FlexCounter::waitPoll() +{ + SWSS_LOG_ENTER(); + std::unique_lock lk(m_mtxSleep); + m_pollCond.wait(lk, [&](){return m_readyToPoll;}); + m_readyToPoll = false; +} + +void FlexCounter::notifyPoll() +{ + SWSS_LOG_ENTER(); + std::unique_lock lk(m_mtxSleep); + m_readyToPoll = true; m_pollCond.notify_all(); } diff --git a/syncd/FlexCounter.h b/syncd/FlexCounter.h index e8e88fa91..fad045485 100644 --- a/syncd/FlexCounter.h +++ b/syncd/FlexCounter.h @@ -80,6 +80,10 @@ namespace syncd void addTunnelCounterPlugin( _In_ const std::string& sha); + + void addFlowCounterPlugin( + _In_ const std::string& sha); + private: void checkPluginRegistered( @@ -121,6 +125,9 @@ namespace syncd void removeTunnel( _In_ sai_object_id_t tunnelVid); + void removeFlowCounter( + _In_ sai_object_id_t counterVid); + private: // set counter list void setPortCounterList( @@ -164,6 +171,11 @@ namespace syncd _In_ sai_object_id_t tunnelRid, _In_ const std::vector &counterIds); + void setFlowCounterList( + _In_ sai_object_id_t counterVid, + _In_ sai_object_id_t counterRid, + _In_ const std::vector& counterIds); + private: // set attr list void setQueueAttrList( @@ -269,6 +281,10 @@ namespace syncd _In_ sai_object_id_t priorityGroupRid, _In_ const std::vector &counterIds); + void updateSupportedFlowCounters( + _In_ sai_object_id_t counterRid, + _In_ const std::vector &counterIds); + std::vector saiCheckSupportedSwitchDebugCounters( _In_ sai_object_id_t switchRid, _In_ const std::vector &counterIds); @@ -390,6 +406,16 @@ namespace syncd std::vector m_tunnelCounterIds; }; + struct FlowCounterIds + { + FlowCounterIds( + _In_ sai_object_id_t counterId, + _In_ const std::vector &flowCounterIds); + + sai_object_id_t counterId; + std::vector flowCounterIds; + }; + private: void collectCounters( @@ -437,6 +463,9 @@ namespace syncd void collectTunnelCounters( _In_ swss::Table &countersTable); + void collectFlowCounters( + _In_ swss::Table &countersTable); + private: // collect attributes void collectQueueAttrs( @@ -460,6 +489,11 @@ namespace syncd void removeCollectCountersHandler( _In_ const std::string& key); + private: + void waitPoll(); + + void notifyPoll(); + private: // plugins std::set m_queuePlugins; @@ -468,6 +502,7 @@ namespace syncd std::set m_priorityGroupPlugins; std::set m_bufferPoolPlugins; std::set m_tunnelPlugins; + std::set m_flowCounterPlugins; private: // supported counters @@ -477,6 +512,7 @@ namespace syncd std::set m_supportedRifCounters; std::set m_supportedBufferPoolCounters; std::set m_supportedTunnelCounters; + std::set m_supportedFlowCounters; private: // registered VID maps @@ -488,6 +524,8 @@ namespace syncd std::map> m_bufferPoolCounterIdsMap; std::map> m_switchDebugCounterIdsMap; std::map> m_tunnelCounterIdsMap; + std::map> m_flowCounterIdsMap; + std::map> m_queueAttrIdsMap; std::map> m_priorityGroupAttrIdsMap; std::map> m_macsecSAAttrIdsMap; @@ -507,6 +545,8 @@ namespace syncd std::condition_variable m_pollCond; + bool m_readyToPoll; + uint32_t m_pollInterval; std::string m_instanceId; diff --git a/unittest/meta/TestSaiSerialize.cpp b/unittest/meta/TestSaiSerialize.cpp index 2fc26d937..8a8ceca29 100644 --- a/unittest/meta/TestSaiSerialize.cpp +++ b/unittest/meta/TestSaiSerialize.cpp @@ -338,6 +338,11 @@ TEST(SaiSerialize, sai_serialize_tunnel_stat) EXPECT_EQ(sai_serialize_tunnel_stat(SAI_TUNNEL_STAT_IN_OCTETS), "SAI_TUNNEL_STAT_IN_OCTETS"); } +TEST(SaiSerialize, sai_serialize_counter_stat) +{ + EXPECT_EQ(sai_serialize_counter_stat(SAI_COUNTER_STAT_PACKETS), "SAI_COUNTER_STAT_PACKETS"); +} + TEST(SaiSerialize, sai_serialize_queue_attr) { EXPECT_EQ(sai_serialize_queue_attr(SAI_QUEUE_ATTR_TYPE), "SAI_QUEUE_ATTR_TYPE"); diff --git a/unittest/syncd/Makefile.am b/unittest/syncd/Makefile.am index 8a76873df..e4f2f2661 100644 --- a/unittest/syncd/Makefile.am +++ b/unittest/syncd/Makefile.am @@ -1,11 +1,17 @@ -AM_CXXFLAGS = $(SAIINC) -I$(top_srcdir)/syncd -I$(top_srcdir)/lib -I$(top_srcdir)/vslib +AM_CXXFLAGS = $(SAIINC) -I$(top_srcdir)/syncd -I$(top_srcdir)/lib -I$(top_srcdir)/vslib -I$(top_srcdir)/meta bin_PROGRAMS = tests LDADD_GTEST = -L/usr/src/gtest -lgtest -lgtest_main tests_SOURCES = main.cpp \ - TestCommandLineOptions.cpp + ../../meta/DummySaiInterface.cpp \ + MockableSaiInterface.cpp \ + MockHelper.cpp \ + TestCommandLineOptions.cpp \ + TestFlexCounter.cpp tests_CXXFLAGS = $(DBGFLAGS) $(AM_CXXFLAGS) $(CXXFLAGS_COMMON) -tests_LDADD = $(LDADD_GTEST) $(top_srcdir)/syncd/libSyncd.a -lswsscommon -lpthread -L$(top_srcdir)/lib/.libs -lsairedis -L$(top_srcdir)/meta/.libs -lsaimetadata -lsaimeta -lzmq $(CODE_COVERAGE_LIBS) +tests_LDADD = $(LDADD_GTEST) $(top_srcdir)/syncd/libSyncd.a -lhiredis -lswsscommon -lpthread -L$(top_srcdir)/lib/.libs -lsairedis -L$(top_srcdir)/meta/.libs -lsaimetadata -lsaimeta -lzmq $(CODE_COVERAGE_LIBS) + +TESTS = tests diff --git a/unittest/syncd/MockHelper.cpp b/unittest/syncd/MockHelper.cpp new file mode 100644 index 000000000..929df8620 --- /dev/null +++ b/unittest/syncd/MockHelper.cpp @@ -0,0 +1,20 @@ +#include "VidManager.h" +#include "swss/dbconnector.h" +#include "swss/table.h" + +namespace test_syncd { + sai_object_type_t mock_objectTypeQuery_result; + void mockVidManagerObjectTypeQuery(sai_object_type_t mock_result) + { + SWSS_LOG_ENTER(); + mock_objectTypeQuery_result = mock_result; + } +} + +namespace syncd { + sai_object_type_t VidManager::objectTypeQuery(_In_ sai_object_id_t objectId) + { + SWSS_LOG_ENTER(); + return test_syncd::mock_objectTypeQuery_result; + } +} diff --git a/unittest/syncd/MockHelper.h b/unittest/syncd/MockHelper.h new file mode 100644 index 000000000..21e56c74a --- /dev/null +++ b/unittest/syncd/MockHelper.h @@ -0,0 +1,5 @@ +#pragma once + +namespace test_syncd { + void mockVidManagerObjectTypeQuery(sai_object_type_t); +} \ No newline at end of file diff --git a/unittest/syncd/MockableSaiInterface.cpp b/unittest/syncd/MockableSaiInterface.cpp new file mode 100644 index 000000000..30b52a2d3 --- /dev/null +++ b/unittest/syncd/MockableSaiInterface.cpp @@ -0,0 +1,298 @@ +#include "MockableSaiInterface.h" +#include "swss/logger.h" + + +MockableSaiInterface::MockableSaiInterface() +{ + SWSS_LOG_ENTER(); +} + +MockableSaiInterface::~MockableSaiInterface() +{ + SWSS_LOG_ENTER(); +} + +sai_status_t MockableSaiInterface::initialize( + _In_ uint64_t flags, + _In_ const sai_service_method_table_t *service_method_table) +{ + SWSS_LOG_ENTER(); + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::uninitialize() +{ + SWSS_LOG_ENTER(); + return SAI_STATUS_SUCCESS; +} + + +sai_status_t MockableSaiInterface::create( + _In_ sai_object_type_t objectType, + _Out_ sai_object_id_t* objectId, + _In_ sai_object_id_t switchId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + if (mock_create) + { + return mock_create(objectType, objectId, switchId, attr_count, attr_list); + } + + return SAI_STATUS_SUCCESS; +} + + +sai_status_t MockableSaiInterface::remove( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + if (mock_remove) + { + return mock_remove(objectType, objectId); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::set( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) +{ + SWSS_LOG_ENTER(); + if (mock_set) + { + return mock_set(objectType, objectId, attr); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::get( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) +{ + SWSS_LOG_ENTER(); + if (mock_get) + { + return mock_get(objectType, objectId, attr_count, attr_list); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::bulkCreate( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t switch_id, + _In_ uint32_t object_count, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_object_id_t *object_id, + _Out_ sai_status_t *object_statuses) +{ + SWSS_LOG_ENTER(); + if (mock_bulkCreate) + { + return mock_bulkCreate(object_type, switch_id, object_count, attr_count, attr_list, mode, object_id, object_statuses); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::bulkRemove( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + SWSS_LOG_ENTER(); + if (mock_bulkRemove) + { + return mock_bulkRemove(object_type, object_count, object_id, mode, object_statuses); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::bulkSet( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) +{ + SWSS_LOG_ENTER(); + if (mock_bulkSet) + { + return mock_bulkSet(object_type, object_count, object_id, attr_list, mode, object_statuses); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::getStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _Out_ uint64_t *counters) +{ + SWSS_LOG_ENTER(); + if (mock_getStats) + { + return mock_getStats(object_type, object_id, number_of_counters, counter_ids, counters); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::queryStatsCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _Inout_ sai_stat_capability_list_t *stats_capability) +{ + SWSS_LOG_ENTER(); + if (mock_queryStatsCapability) + { + return mock_queryStatsCapability(switch_id, object_type, stats_capability); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::getStatsExt( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) +{ + SWSS_LOG_ENTER(); + if (mock_getStatsExt) + { + return mock_getStatsExt(object_type, object_id, number_of_counters, counter_ids, mode, counters); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::clearStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids) +{ + SWSS_LOG_ENTER(); + if (mock_clearStats) + { + return mock_clearStats(object_type, object_id, number_of_counters, counter_ids); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::flushFdbEntries( + _In_ sai_object_id_t switchId, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList) +{ + SWSS_LOG_ENTER(); + if (mock_flushFdbEntries) + { + return mock_flushFdbEntries(switchId, attrCount, attrList); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::objectTypeGetAvailability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList, + _Out_ uint64_t *count) +{ + SWSS_LOG_ENTER(); + if (mock_objectTypeGetAvailability) + { + return mock_objectTypeGetAvailability(switchId, objectType, attrCount, attrList, count); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::queryAttributeCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Out_ sai_attr_capability_t *capability) +{ + SWSS_LOG_ENTER(); + if (mock_queryAttributeCapability) + { + return mock_queryAttributeCapability(switch_id, object_type, attr_id, capability); + } + + return SAI_STATUS_SUCCESS; +} + +sai_status_t MockableSaiInterface::queryAattributeEnumValuesCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Inout_ sai_s32_list_t *enum_values_capability) +{ + SWSS_LOG_ENTER(); + if (mock_queryAattributeEnumValuesCapability) + { + return mock_queryAattributeEnumValuesCapability(switch_id, object_type, attr_id, enum_values_capability); + } + + return SAI_STATUS_SUCCESS; +} + +sai_object_type_t MockableSaiInterface::objectTypeQuery( + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + if (mock_objectTypeQuery) + { + return mock_objectTypeQuery(objectId); + } + + return SAI_OBJECT_TYPE_NULL; +} + +sai_object_id_t MockableSaiInterface::switchIdQuery( + _In_ sai_object_id_t objectId) +{ + SWSS_LOG_ENTER(); + if (mock_switchIdQuery) + { + return mock_switchIdQuery(objectId); + } + + return 0; +} + +sai_status_t MockableSaiInterface::logSet( + _In_ sai_api_t api, + _In_ sai_log_level_t log_level) +{ + SWSS_LOG_ENTER(); + if (mock_logSet) + { + return mock_logSet(api, log_level); + } + + return SAI_STATUS_SUCCESS; +} diff --git a/unittest/syncd/MockableSaiInterface.h b/unittest/syncd/MockableSaiInterface.h new file mode 100644 index 000000000..536c45830 --- /dev/null +++ b/unittest/syncd/MockableSaiInterface.h @@ -0,0 +1,178 @@ +#pragma once + +#include + +#include "DummySaiInterface.h" + +class MockableSaiInterface: public saimeta::DummySaiInterface +{ + public: + + MockableSaiInterface(); + + virtual ~MockableSaiInterface(); + + public: + + virtual sai_status_t initialize( + _In_ uint64_t flags, + _In_ const sai_service_method_table_t *service_method_table) override; + virtual sai_status_t uninitialize(void) override; + + public: // SAI interface overrides + + virtual sai_status_t create( + _In_ sai_object_type_t objectType, + _Out_ sai_object_id_t* objectId, + _In_ sai_object_id_t switchId, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) override; + + std::function mock_create; + + virtual sai_status_t remove( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId) override; + + std::function mock_remove; + + virtual sai_status_t set( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ const sai_attribute_t *attr) override; + + std::function mock_set; + + virtual sai_status_t get( + _In_ sai_object_type_t objectType, + _In_ sai_object_id_t objectId, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) override; + + std::function mock_get; + + public: // bulk QUAD oid + + virtual sai_status_t bulkCreate( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t switch_id, + _In_ uint32_t object_count, + _In_ const uint32_t *attr_count, + _In_ const sai_attribute_t **attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_object_id_t *object_id, + _Out_ sai_status_t *object_statuses) override; + + std::function mock_bulkCreate; + + virtual sai_status_t bulkRemove( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) override; + + std::function mock_bulkRemove; + + virtual sai_status_t bulkSet( + _In_ sai_object_type_t object_type, + _In_ uint32_t object_count, + _In_ const sai_object_id_t *object_id, + _In_ const sai_attribute_t *attr_list, + _In_ sai_bulk_op_error_mode_t mode, + _Out_ sai_status_t *object_statuses) override; + + std::function mock_bulkSet; + + public: // stats API + + virtual sai_status_t getStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _Out_ uint64_t *counters) override; + + std::function mock_getStats; + + virtual sai_status_t queryStatsCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _Inout_ sai_stat_capability_list_t *stats_capability) override; + + std::function mock_queryStatsCapability; + + virtual sai_status_t getStatsExt( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids, + _In_ sai_stats_mode_t mode, + _Out_ uint64_t *counters) override; + + std::function mock_getStatsExt; + + virtual sai_status_t clearStats( + _In_ sai_object_type_t object_type, + _In_ sai_object_id_t object_id, + _In_ uint32_t number_of_counters, + _In_ const sai_stat_id_t *counter_ids) override; + + std::function mock_clearStats; + + + public: // non QUAD API + + virtual sai_status_t flushFdbEntries( + _In_ sai_object_id_t switchId, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList) override; + + std::function mock_flushFdbEntries; + + public: // SAI API + + virtual sai_status_t objectTypeGetAvailability( + _In_ sai_object_id_t switchId, + _In_ sai_object_type_t objectType, + _In_ uint32_t attrCount, + _In_ const sai_attribute_t *attrList, + _Out_ uint64_t *count) override; + + std::function mock_objectTypeGetAvailability; + + + virtual sai_status_t queryAttributeCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Out_ sai_attr_capability_t *capability) override; + + std::function mock_queryAttributeCapability; + + + virtual sai_status_t queryAattributeEnumValuesCapability( + _In_ sai_object_id_t switch_id, + _In_ sai_object_type_t object_type, + _In_ sai_attr_id_t attr_id, + _Inout_ sai_s32_list_t *enum_values_capability) override; + + std::function mock_queryAattributeEnumValuesCapability; + + virtual sai_object_type_t objectTypeQuery( + _In_ sai_object_id_t objectId) override; + + std::function mock_objectTypeQuery; + + + virtual sai_object_id_t switchIdQuery( + _In_ sai_object_id_t objectId) override; + + std::function mock_switchIdQuery; + + virtual sai_status_t logSet( + _In_ sai_api_t api, + _In_ sai_log_level_t log_level) override; + + std::function mock_logSet; +}; diff --git a/unittest/syncd/TestFlexCounter.cpp b/unittest/syncd/TestFlexCounter.cpp new file mode 100644 index 000000000..a98483d08 --- /dev/null +++ b/unittest/syncd/TestFlexCounter.cpp @@ -0,0 +1,77 @@ +#include "FlexCounter.h" +#include "MockableSaiInterface.h" +#include "MockHelper.h" + +#include + + +using namespace syncd; +using namespace std; + +TEST(FlexCounter, addRemoveCounterForFlowCounter) +{ + std::shared_ptr sai(new MockableSaiInterface()); + FlexCounter fc("test", sai, "COUNTERS_DB"); + + sai_object_id_t counterVid{100}; + sai_object_id_t counterRid{100}; + std::vector values; + values.emplace_back(FLOW_COUNTER_ID_LIST, "SAI_COUNTER_STAT_PACKETS,SAI_COUNTER_STAT_BYTES"); + + test_syncd::mockVidManagerObjectTypeQuery(SAI_OBJECT_TYPE_COUNTER); + 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; + }; + + fc.addCounter(counterVid, counterRid, values); + EXPECT_EQ(fc.isEmpty(), false); + + values.clear(); + values.emplace_back(POLL_INTERVAL_FIELD, "1000"); + fc.addCounterPlugin(values); + + values.clear(); + values.emplace_back(FLEX_COUNTER_STATUS_FIELD, "enable"); + 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 keys; + countersTable.getKeys(keys); + EXPECT_EQ(keys.size(), size_t(1)); + EXPECT_EQ(keys[0], "oid:0x64"); + + std::string value; + countersTable.hget("oid:0x64", "SAI_COUNTER_STAT_PACKETS", value); + EXPECT_EQ(value, "100"); + countersTable.hget("oid:0x64", "SAI_COUNTER_STAT_BYTES", value); + EXPECT_EQ(value, "200"); + + fc.removeCounter(counterVid); + EXPECT_EQ(fc.isEmpty(), true); + countersTable.getKeys(keys); + + ASSERT_TRUE(keys.empty()); + +} + +TEST(FlexCounter, addRemoveCounterPluginForFlowCounter) +{ + std::shared_ptr sai(new MockableSaiInterface()); + FlexCounter fc("test", sai, "COUNTERS_DB"); + + std::vector values; + values.emplace_back(FLOW_COUNTER_PLUGIN_FIELD, "dummy_sha_strings"); + fc.addCounterPlugin(values); + EXPECT_EQ(fc.isEmpty(), false); + + fc.removeCounterPlugins(); + EXPECT_EQ(fc.isEmpty(), true); +}