From ed783e1f583111133cbb2e6797bb48ef0c345bea Mon Sep 17 00:00:00 2001 From: Junchao-Mellanox <57339448+Junchao-Mellanox@users.noreply.github.com> Date: Wed, 1 Dec 2021 09:54:52 +0800 Subject: [PATCH] [orchagent] Add trap flow counter support (#1951) * Add trap flow counter support. See HLD: Azure/SONiC#858 * Flow counters are usually used for debugging, troubleshooting and performance enhancement processes. Host interface trap counter can get number of received traps per Trap ID. --- orchagent/Makefile.am | 5 +- orchagent/copporch.cpp | 258 ++++++++++-- orchagent/copporch.h | 39 +- .../flex_counter/flex_counter_manager.cpp | 7 +- orchagent/flex_counter/flex_counter_manager.h | 5 +- .../flex_counter/flow_counter_handler.cpp | 49 +++ orchagent/flex_counter/flow_counter_handler.h | 16 + orchagent/flexcounterorch.cpp | 17 + orchagent/flexcounterorch.h | 3 + orchagent/orchdaemon.cpp | 5 +- orchagent/saihelper.cpp | 15 +- orchagent/trap_rates.lua | 67 +++ tests/mock_tests/Makefile.am | 2 +- tests/test_flex_counters.py | 382 ++++++++++++++---- 14 files changed, 736 insertions(+), 134 deletions(-) create mode 100644 orchagent/flex_counter/flow_counter_handler.cpp create mode 100644 orchagent/flex_counter/flow_counter_handler.h create mode 100644 orchagent/trap_rates.lua diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 47de03726677..7225917e4da9 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -25,7 +25,8 @@ dist_swss_DATA = \ watermark_pg.lua \ watermark_bufferpool.lua \ lagids.lua \ - tunnel_rates.lua + tunnel_rates.lua \ + trap_rates.lua bin_PROGRAMS = orchagent routeresync orchagent_restart_check @@ -92,7 +93,7 @@ orchagent_SOURCES = \ srv6orch.cpp \ response_publisher.cpp -orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp +orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp flex_counter/flow_counter_handler.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp orchagent_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) diff --git a/orchagent/copporch.cpp b/orchagent/copporch.cpp index 6b1321b5bbfb..d193e215c34e 100644 --- a/orchagent/copporch.cpp +++ b/orchagent/copporch.cpp @@ -1,8 +1,14 @@ #include "sai.h" #include "copporch.h" #include "portsorch.h" +#include "flexcounterorch.h" #include "tokenize.h" #include "logger.h" +#include "sai_serialize.h" +#include "schema.h" +#include "directory.h" +#include "flow_counter_handler.h" +#include "timer.h" #include #include @@ -18,8 +24,11 @@ extern sai_switch_api_t* sai_switch_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; +extern Directory gDirectory; extern bool gIsNatSupported; +#define FLEX_COUNTER_UPD_INTERVAL 1 + static map policer_meter_map = { {"packets", SAI_METER_TYPE_PACKETS}, {"bytes", SAI_METER_TYPE_BYTES} @@ -82,6 +91,21 @@ static map trap_id_map = { {"bfdv6_micro", SAI_HOSTIF_TRAP_TYPE_BFDV6_MICRO} }; + +std::string get_trap_name_by_type(sai_hostif_trap_type_t trap_type) +{ + static map trap_name_to_id_map; + if (trap_name_to_id_map.empty()) + { + for (const auto &kv : trap_id_map) + { + trap_name_to_id_map.emplace(kv.second, kv.first); + } + } + + return trap_name_to_id_map.at(trap_type); +} + static map packet_action_map = { {"drop", SAI_PACKET_ACTION_DROP}, {"forward", SAI_PACKET_ACTION_FORWARD}, @@ -97,11 +121,23 @@ const string default_trap_group = "default"; const vector default_trap_ids = { SAI_HOSTIF_TRAP_TYPE_TTL_ERROR }; +const uint HOSTIF_TRAP_COUNTER_POLLING_INTERVAL_MS = 10000; CoppOrch::CoppOrch(DBConnector* db, string tableName) : - Orch(db, tableName) + Orch(db, tableName), + m_counter_db(std::shared_ptr(new DBConnector("COUNTERS_DB", 0))), + m_flex_db(std::shared_ptr(new DBConnector("FLEX_COUNTER_DB", 0))), + m_asic_db(std::shared_ptr(new DBConnector("ASIC_DB", 0))), + m_counter_table(std::unique_ptr(new Table(m_counter_db.get(), COUNTERS_TRAP_NAME_MAP))), + m_vidToRidTable(std::unique_ptr
(new Table(m_asic_db.get(), "VIDTORID"))), + m_flex_counter_group_table(std::unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_GROUP_TABLE))), + m_trap_counter_manager(HOSTIF_TRAP_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, HOSTIF_TRAP_COUNTER_POLLING_INTERVAL_MS, false) { SWSS_LOG_ENTER(); + auto intervT = timespec { .tv_sec = FLEX_COUNTER_UPD_INTERVAL , .tv_nsec = 0 }; + m_FlexCounterUpdTimer = new SelectableTimer(intervT); + auto executorT = new ExecutableTimer(m_FlexCounterUpdTimer, this, "FLEX_COUNTER_UPD_TIMER"); + Orch::addExecutor(executorT); initDefaultHostIntfTable(); initDefaultTrapGroup(); @@ -321,6 +357,8 @@ bool CoppOrch::applyAttributesToTrapIds(sai_object_id_t trap_group_id, } m_syncdTrapIds[trap_id].trap_group_obj = trap_group_id; m_syncdTrapIds[trap_id].trap_obj = hostif_trap_id; + m_syncdTrapIds[trap_id].trap_type = trap_id; + bindTrapCounter(hostif_trap_id, trap_id); } return true; } @@ -706,6 +744,35 @@ void CoppOrch::doTask(Consumer &consumer) } } +void CoppOrch::doTask(SelectableTimer &timer) +{ + SWSS_LOG_ENTER(); + + string value; + for (auto it = m_pendingAddToFlexCntr.begin(); it != m_pendingAddToFlexCntr.end(); ) + { + const auto id = sai_serialize_object_id(it->first); + if (m_vidToRidTable->hget("", id, value)) + { + SWSS_LOG_INFO("Registering %s, id %s", it->second.c_str(), id.c_str()); + + std::unordered_set counter_stats; + FlowCounterHandler::getGenericCounterStatIdList(counter_stats); + m_trap_counter_manager.setCounterIdList(it->first, CounterType::HOSTIF_TRAP, counter_stats); + it = m_pendingAddToFlexCntr.erase(it); + } + else + { + ++it; + } + } + + if (m_pendingAddToFlexCntr.empty()) + { + m_FlexCounterUpdTimer->stop(); + } +} + void CoppOrch::getTrapAddandRemoveList(string trap_group_name, vector &trap_ids, vector &add_trap_ids, @@ -777,17 +844,9 @@ bool CoppOrch::trapGroupProcessTrapIdChange (string trap_group_name, { if (m_syncdTrapIds.find(i)!= m_syncdTrapIds.end()) { - sai_status_t sai_status = sai_hostif_api->remove_hostif_trap( - m_syncdTrapIds[i].trap_obj); - if (sai_status != SAI_STATUS_SUCCESS) + if (!removeTrap(m_syncdTrapIds[i].trap_obj)) { - SWSS_LOG_ERROR("Failed to remove trap object %" PRId64 "", - m_syncdTrapIds[i].trap_obj); - task_process_status handle_status = handleSaiRemoveStatus(SAI_API_HOSTIF, sai_status); - if (handle_status != task_success) - { - return parseHandleSaiStatusFailure(handle_status); - } + return false; } } } @@ -830,17 +889,9 @@ bool CoppOrch::trapGroupProcessTrapIdChange (string trap_group_name, */ if (m_syncdTrapIds[i].trap_group_obj == m_trap_group_map[trap_group_name]) { - sai_status_t sai_status = sai_hostif_api->remove_hostif_trap( - m_syncdTrapIds[i].trap_obj); - if (sai_status != SAI_STATUS_SUCCESS) + if (!removeTrap(m_syncdTrapIds[i].trap_obj)) { - SWSS_LOG_ERROR("Failed to remove trap object %" PRId64 "", - m_syncdTrapIds[i].trap_obj); - task_process_status handle_status = handleSaiRemoveStatus(SAI_API_HOSTIF, sai_status); - if (handle_status != task_success) - { - return parseHandleSaiStatusFailure(handle_status); - } + return false; } m_syncdTrapIds.erase(i); } @@ -882,15 +933,9 @@ bool CoppOrch::processTrapGroupDel (string trap_group_name) if (it.second.trap_group_obj == m_trap_group_map[trap_group_name]) { trap_ids_to_reset.push_back(it.first); - sai_status_t sai_status = sai_hostif_api->remove_hostif_trap(it.second.trap_obj); - if (sai_status != SAI_STATUS_SUCCESS) + if (!removeTrap(it.second.trap_obj)) { - SWSS_LOG_ERROR("Failed to remove trap object %" PRId64 "", it.second.trap_obj); - task_process_status handle_status = handleSaiRemoveStatus(SAI_API_HOSTIF, sai_status); - if (handle_status != task_success) - { - return parseHandleSaiStatusFailure(handle_status); - } + return false; } } } @@ -1096,3 +1141,158 @@ bool CoppOrch::trapGroupUpdatePolicer (string trap_group_name, } return true; } + +void CoppOrch::initTrapRatePlugin() +{ + if (m_trap_rate_plugin_loaded) + { + return; + } + + std::string trapRatePluginName = "trap_rates.lua"; + try + { + std::string trapLuaScript = swss::loadLuaScript(trapRatePluginName); + std::string trapSha = swss::loadRedisScript(m_counter_db.get(), trapLuaScript); + + vector fieldValues; + fieldValues.emplace_back(FLOW_COUNTER_PLUGIN_FIELD, trapSha); + fieldValues.emplace_back(STATS_MODE_FIELD, STATS_MODE_READ); + m_flex_counter_group_table->set(HOSTIF_TRAP_COUNTER_FLEX_COUNTER_GROUP, fieldValues); + } + catch (const runtime_error &e) + { + SWSS_LOG_ERROR("Trap flex counter groups were not set successfully: %s", e.what()); + } + m_trap_rate_plugin_loaded = true; +} + +bool CoppOrch::removeTrap(sai_object_id_t hostif_trap_id) +{ + unbindTrapCounter(hostif_trap_id); + + sai_status_t sai_status = sai_hostif_api->remove_hostif_trap(hostif_trap_id); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove trap object %" PRId64 "", + hostif_trap_id); + task_process_status handle_status = handleSaiRemoveStatus(SAI_API_HOSTIF, sai_status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + return true; +} + +bool CoppOrch::bindTrapCounter(sai_object_id_t hostif_trap_id, sai_hostif_trap_type_t trap_type) +{ + auto flex_counters_orch = gDirectory.get(); + + if (!flex_counters_orch || !flex_counters_orch->getHostIfTrapCounterState()) + { + return false; + } + + if (m_trap_obj_name_map.count(hostif_trap_id) > 0) + { + return true; + } + + initTrapRatePlugin(); + + // Create generic counter + sai_object_id_t counter_id; + if (!FlowCounterHandler::createGenericCounter(counter_id)) + { + return false; + } + + // Bind generic counter to trap + sai_attribute_t trap_attr; + trap_attr.id = SAI_HOSTIF_TRAP_ATTR_COUNTER_ID; + trap_attr.value.oid = counter_id; + sai_status_t sai_status = sai_hostif_api->set_hostif_trap_attribute(hostif_trap_id, &trap_attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Failed to bind trap %" PRId64 " to counter %" PRId64 "", hostif_trap_id, counter_id); + return false; + } + + // Update COUNTERS_TRAP_NAME_MAP + auto trap_name = get_trap_name_by_type(trap_type); + vector nameMapFvs; + nameMapFvs.emplace_back(trap_name, sai_serialize_object_id(counter_id)); + m_counter_table->set("", nameMapFvs); + + auto was_empty = m_pendingAddToFlexCntr.empty(); + m_pendingAddToFlexCntr[counter_id] = trap_name; + + if (was_empty) + { + m_FlexCounterUpdTimer->start(); + } + + m_trap_obj_name_map.emplace(hostif_trap_id, trap_name); + return true; +} + +void CoppOrch::unbindTrapCounter(sai_object_id_t hostif_trap_id) +{ + auto iter = m_trap_obj_name_map.find(hostif_trap_id); + if (iter == m_trap_obj_name_map.end()) + { + return; + } + + std::string counter_oid_str; + m_counter_table->hget("", iter->second, counter_oid_str); + + // Clear FLEX_COUNTER table + sai_object_id_t counter_id; + sai_deserialize_object_id(counter_oid_str, counter_id); + auto update_iter = m_pendingAddToFlexCntr.find(counter_id); + if (update_iter == m_pendingAddToFlexCntr.end()) + { + m_trap_counter_manager.clearCounterIdList(counter_id); + } + else + { + m_pendingAddToFlexCntr.erase(update_iter); + } + + // Remove trap from COUNTERS_TRAP_NAME_MAP + m_counter_table->hdel("", iter->second); + + // Unbind generic counter to trap + sai_attribute_t trap_attr; + trap_attr.id = SAI_HOSTIF_TRAP_ATTR_COUNTER_ID; + trap_attr.value.oid = SAI_NULL_OBJECT_ID; + sai_status_t sai_status = sai_hostif_api->set_hostif_trap_attribute(hostif_trap_id, &trap_attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to unbind trap %" PRId64 " to counter %" PRId64 "", hostif_trap_id, counter_id); + } + + // Remove generic counter + FlowCounterHandler::removeGenericCounter(counter_id); + + m_trap_obj_name_map.erase(iter); +} + +void CoppOrch::generateHostIfTrapCounterIdList() +{ + for (const auto &kv : m_syncdTrapIds) + { + bindTrapCounter(kv.second.trap_obj, kv.second.trap_type); + } +} + +void CoppOrch::clearHostIfTrapCounterIdList() +{ + for (const auto &kv : m_syncdTrapIds) + { + unbindTrapCounter(kv.second.trap_obj); + } +} diff --git a/orchagent/copporch.h b/orchagent/copporch.h index 4794cfd2a648..096979ebb8e1 100644 --- a/orchagent/copporch.h +++ b/orchagent/copporch.h @@ -3,7 +3,17 @@ #include #include +#include +#include "dbconnector.h" #include "orch.h" +#include "flex_counter_manager.h" +#include "producertable.h" +#include "table.h" +#include "selectabletimer.h" + +using namespace swss; + +#define HOSTIF_TRAP_COUNTER_FLEX_COUNTER_GROUP "HOSTIF_TRAP_FLOW_COUNTER" // trap fields const std::string copp_trap_id_list = "trap_ids"; @@ -33,6 +43,7 @@ struct copp_trap_objects { sai_object_id_t trap_obj; sai_object_id_t trap_group_obj; + sai_hostif_trap_type_t trap_type; }; /* TrapGroupPolicerTable: trap group ID, policer ID */ @@ -45,11 +56,15 @@ typedef std::map TrapGroupHostIfMap; typedef std::map TrapIdHostIfTableMap; /* Trap group to trap ID attributes */ typedef std::map TrapGroupTrapIdAttribs; +/* Trap OID to trap name*/ +typedef std::map TrapObjectTrapNameMap; class CoppOrch : public Orch { public: CoppOrch(swss::DBConnector* db, std::string tableName); + void generateHostIfTrapCounterIdList(); + void clearHostIfTrapCounterIdList(); protected: object_map m_trap_group_map; @@ -59,10 +74,26 @@ class CoppOrch : public Orch TrapGroupHostIfMap m_trap_group_hostif_map; TrapIdHostIfTableMap m_trapid_hostif_table_map; TrapGroupTrapIdAttribs m_trap_group_trap_id_attrs; + TrapObjectTrapNameMap m_trap_obj_name_map; + std::map m_pendingAddToFlexCntr; + + std::shared_ptr m_counter_db; + std::shared_ptr m_flex_db; + std::shared_ptr m_asic_db; + std::unique_ptr
m_counter_table; + std::unique_ptr
m_vidToRidTable; + std::unique_ptr m_flex_counter_group_table; + + FlexCounterManager m_trap_counter_manager; + + bool m_trap_rate_plugin_loaded = false; + + SelectableTimer* m_FlexCounterUpdTimer = nullptr; void initDefaultHostIntfTable(); void initDefaultTrapGroup(); void initDefaultTrapIds(); + void initTrapRatePlugin(); task_process_status processCoppRule(Consumer& consumer); bool isValidList(std::vector &trap_id_list, std::vector &all_items) const; @@ -82,7 +113,7 @@ class CoppOrch : public Orch std::vector &add_trap_ids, std::vector &rem_trap_ids); - void getTrapIdsFromTrapGroup (sai_object_id_t trap_group_obj, + void getTrapIdsFromTrapGroup (sai_object_id_t trap_group_obj, std::vector &trap_ids); bool trapGroupProcessTrapIdChange (std::string trap_group_name, @@ -99,7 +130,13 @@ class CoppOrch : public Orch bool trapGroupUpdatePolicer (std::string trap_group_name, std::vector &policer_attribs); + bool removeTrap(sai_object_id_t hostif_trap_id); + + bool bindTrapCounter(sai_object_id_t hostif_trap_id, sai_hostif_trap_type_t trap_type); + void unbindTrapCounter(sai_object_id_t hostif_trap_id); + virtual void doTask(Consumer& consumer); + void doTask(swss::SelectableTimer&) override; }; #endif /* SWSS_COPPORCH_H */ diff --git a/orchagent/flex_counter/flex_counter_manager.cpp b/orchagent/flex_counter/flex_counter_manager.cpp index 86048c26014d..71731e84d3fb 100644 --- a/orchagent/flex_counter/flex_counter_manager.cpp +++ b/orchagent/flex_counter/flex_counter_manager.cpp @@ -43,6 +43,7 @@ const unordered_map FlexCounterManager::counter_id_field_lo { CounterType::MACSEC_FLOW, MACSEC_FLOW_COUNTER_ID_LIST }, { CounterType::ACL_COUNTER, ACL_COUNTER_ATTR_ID_LIST }, { CounterType::TUNNEL, TUNNEL_COUNTER_ID_LIST }, + { CounterType::HOSTIF_TRAP, FLOW_COUNTER_ID_LIST }, }; FlexManagerDirectory g_FlexManagerDirectory; @@ -57,19 +58,19 @@ FlexCounterManager *FlexManagerDirectory::createFlexCounterManager(const string& { if (stats_mode != m_managers[group_name]->getStatsMode()) { - SWSS_LOG_ERROR("Stats mode mismatch with already created flex counter manager %s", + SWSS_LOG_ERROR("Stats mode mismatch with already created flex counter manager %s", group_name.c_str()); return NULL; } if (polling_interval != m_managers[group_name]->getPollingInterval()) { - SWSS_LOG_ERROR("Polling interval mismatch with already created flex counter manager %s", + SWSS_LOG_ERROR("Polling interval mismatch with already created flex counter manager %s", group_name.c_str()); return NULL; } if (enabled != m_managers[group_name]->getEnabled()) { - SWSS_LOG_ERROR("Enabled field mismatch with already created flex counter manager %s", + SWSS_LOG_ERROR("Enabled field mismatch with already created flex counter manager %s", group_name.c_str()); return NULL; } diff --git a/orchagent/flex_counter/flex_counter_manager.h b/orchagent/flex_counter/flex_counter_manager.h index 250586ab9847..6e80feb8fba8 100644 --- a/orchagent/flex_counter/flex_counter_manager.h +++ b/orchagent/flex_counter/flex_counter_manager.h @@ -30,6 +30,7 @@ enum class CounterType MACSEC_FLOW, ACL_COUNTER, TUNNEL, + HOSTIF_TRAP, }; // FlexCounterManager allows users to manage a group of flex counters. @@ -47,7 +48,7 @@ class FlexCounterManager const bool enabled, swss::FieldValueTuple fv_plugin = std::make_pair("","")); - FlexCounterManager() + FlexCounterManager() {} FlexCounterManager(const FlexCounterManager&) = delete; @@ -114,7 +115,7 @@ class FlexManagerDirectory { public: FlexCounterManager* createFlexCounterManager(const std::string& group_name, const StatsMode stats_mode, - const uint polling_interval, const bool enabled, + const uint polling_interval, const bool enabled, swss::FieldValueTuple fv_plugin = std::make_pair("","")); private: std::unordered_map m_managers; diff --git a/orchagent/flex_counter/flow_counter_handler.cpp b/orchagent/flex_counter/flow_counter_handler.cpp new file mode 100644 index 000000000000..89f621fe7ba7 --- /dev/null +++ b/orchagent/flex_counter/flow_counter_handler.cpp @@ -0,0 +1,49 @@ +#include +#include +#include "flow_counter_handler.h" +#include "logger.h" +#include "sai_serialize.h" + +extern sai_object_id_t gSwitchId; +extern sai_counter_api_t* sai_counter_api; + +const std::vector generic_counter_stat_ids = +{ + SAI_COUNTER_STAT_PACKETS, + SAI_COUNTER_STAT_BYTES, +}; + +bool FlowCounterHandler::createGenericCounter(sai_object_id_t &counter_id) +{ + sai_attribute_t counter_attr; + counter_attr.id = SAI_COUNTER_ATTR_TYPE; + counter_attr.value.s32 = SAI_COUNTER_TYPE_REGULAR; + sai_status_t sai_status = sai_counter_api->create_counter(&counter_id, gSwitchId, 1, &counter_attr); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Failed to create generic counter"); + return false; + } + + return true; +} + +bool FlowCounterHandler::removeGenericCounter(sai_object_id_t counter_id) +{ + sai_status_t sai_status = sai_counter_api->remove_counter(counter_id); + if (sai_status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove generic counter: %" PRId64 "", counter_id); + return false; + } + + return true; +} + +void FlowCounterHandler::getGenericCounterStatIdList(std::unordered_set& counter_stats) +{ + for (const auto& it: generic_counter_stat_ids) + { + counter_stats.emplace(sai_serialize_counter_stat(it)); + } +} diff --git a/orchagent/flex_counter/flow_counter_handler.h b/orchagent/flex_counter/flow_counter_handler.h new file mode 100644 index 000000000000..1b6a8bbe2a93 --- /dev/null +++ b/orchagent/flex_counter/flow_counter_handler.h @@ -0,0 +1,16 @@ +#pragma once + +#include +#include + +extern "C" { +#include "sai.h" +} + +class FlowCounterHandler +{ +public: + static bool createGenericCounter(sai_object_id_t &counter_id); + static bool removeGenericCounter(sai_object_id_t counter_id); + static void getGenericCounterStatIdList(std::unordered_set& counter_stats); +}; diff --git a/orchagent/flexcounterorch.cpp b/orchagent/flexcounterorch.cpp index dea2fcd0a375..dc14998774ff 100644 --- a/orchagent/flexcounterorch.cpp +++ b/orchagent/flexcounterorch.cpp @@ -9,6 +9,7 @@ #include "flexcounterorch.h" #include "debugcounterorch.h" #include "directory.h" +#include "copporch.h" extern sai_port_api_t *sai_port_api; @@ -17,6 +18,7 @@ extern FabricPortsOrch *gFabricPortsOrch; extern IntfsOrch *gIntfsOrch; extern BufferOrch *gBufferOrch; extern Directory gDirectory; +extern CoppOrch *gCoppOrch; #define BUFFER_POOL_WATERMARK_KEY "BUFFER_POOL_WATERMARK" #define PORT_KEY "PORT" @@ -26,6 +28,7 @@ extern Directory gDirectory; #define RIF_KEY "RIF" #define ACL_KEY "ACL" #define TUNNEL_KEY "TUNNEL" +#define FLOW_CNT_TRAP_KEY "FLOW_CNT_TRAP" unordered_map flexCounterGroupMap = { @@ -43,6 +46,7 @@ unordered_map flexCounterGroupMap = {"DEBUG_COUNTER", DEBUG_COUNTER_FLEX_COUNTER_GROUP}, {"ACL", ACL_COUNTER_FLEX_COUNTER_GROUP}, {"TUNNEL", TUNNEL_STAT_COUNTER_FLEX_COUNTER_GROUP}, + {FLOW_CNT_TRAP_KEY, HOSTIF_TRAP_COUNTER_FLEX_COUNTER_GROUP}, }; @@ -158,6 +162,19 @@ void FlexCounterOrch::doTask(Consumer &consumer) { vxlan_tunnel_orch->generateTunnelCounterMap(); } + if (gCoppOrch && (key == FLOW_CNT_TRAP_KEY)) + { + if (value == "enable") + { + m_hostif_trap_counter_enabled = true; + gCoppOrch->generateHostIfTrapCounterIdList(); + } + else if (value == "disable") + { + gCoppOrch->clearHostIfTrapCounterIdList(); + m_hostif_trap_counter_enabled = false; + } + } vector fieldValues; fieldValues.emplace_back(FLEX_COUNTER_STATUS_FIELD, value); m_flexCounterGroupTable->set(flexCounterGroupMap[key], fieldValues); diff --git a/orchagent/flexcounterorch.h b/orchagent/flexcounterorch.h index 9ae7e90aadc6..ceb8187506f9 100644 --- a/orchagent/flexcounterorch.h +++ b/orchagent/flexcounterorch.h @@ -18,13 +18,16 @@ class FlexCounterOrch: public Orch virtual ~FlexCounterOrch(void); bool getPortCountersState() const; bool getPortBufferDropCountersState() const; + bool getHostIfTrapCounterState() const {return m_hostif_trap_counter_enabled;} bool bake() override; + private: std::shared_ptr m_flexCounterDb = nullptr; std::shared_ptr m_flexCounterGroupTable = nullptr; bool m_port_counter_enabled = false; bool m_port_buffer_drop_counter_enabled = false; + bool m_hostif_trap_counter_enabled = false; Table m_flexCounterConfigTable; }; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 14e4d8aa777c..0fee695c4e18 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -47,6 +47,7 @@ NatOrch *gNatOrch; MlagOrch *gMlagOrch; IsoGrpOrch *gIsoGrpOrch; MACsecOrch *gMacsecOrch; +CoppOrch *gCoppOrch; BfdOrch *gBfdOrch; Srv6Orch *gSrv6Orch; @@ -182,7 +183,7 @@ bool OrchDaemon::init() gNhgOrch = new NhgOrch(m_applDb, APP_NEXTHOP_GROUP_TABLE_NAME); gCbfNhgOrch = new CbfNhgOrch(m_applDb, APP_CLASS_BASED_NEXT_HOP_GROUP_TABLE_NAME); - CoppOrch *copp_orch = new CoppOrch(m_applDb, APP_COPP_TABLE_NAME); + gCoppOrch = new CoppOrch(m_applDb, APP_COPP_TABLE_NAME); TunnelDecapOrch *tunnel_decap_orch = new TunnelDecapOrch(m_applDb, APP_TUNNEL_DECAP_TABLE_NAME); VxlanTunnelOrch *vxlan_tunnel_orch = new VxlanTunnelOrch(m_stateDb, m_applDb, APP_VXLAN_TUNNEL_TABLE_NAME); @@ -321,7 +322,7 @@ bool OrchDaemon::init() * when iterating ConsumerMap. This is ensured implicitly by the order of keys in ordered map. * For cases when Orch has to process tables in specific order, like PortsOrch during warm start, it has to override Orch::doTask() */ - m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, copp_orch, qos_orch, wm_orch, policer_orch, tunnel_decap_orch, sflow_orch, debug_counter_orch, gMacsecOrch, gBfdOrch, gSrv6Orch}; + m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, qos_orch, wm_orch, policer_orch, tunnel_decap_orch, sflow_orch, debug_counter_orch, gMacsecOrch, gBfdOrch, gSrv6Orch}; bool initialize_dtel = false; if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 90578dc8a94a..8db9676f394f 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -68,6 +68,7 @@ sai_system_port_api_t* sai_system_port_api; sai_macsec_api_t* sai_macsec_api; sai_srv6_api_t** sai_srv6_api;; sai_l2mc_group_api_t* sai_l2mc_group_api; +sai_counter_api_t* sai_counter_api; sai_bfd_api_t* sai_bfd_api; extern sai_object_id_t gSwitchId; @@ -85,15 +86,15 @@ static map hardware_access_map = map gProfileMap; -sai_status_t mdio_read(uint64_t platform_context, - uint32_t mdio_addr, uint32_t reg_addr, +sai_status_t mdio_read(uint64_t platform_context, + uint32_t mdio_addr, uint32_t reg_addr, uint32_t number_of_registers, uint32_t *data) { return SAI_STATUS_NOT_IMPLEMENTED; } -sai_status_t mdio_write(uint64_t platform_context, - uint32_t mdio_addr, uint32_t reg_addr, +sai_status_t mdio_write(uint64_t platform_context, + uint32_t mdio_addr, uint32_t reg_addr, uint32_t number_of_registers, uint32_t *data) { return SAI_STATUS_NOT_IMPLEMENTED; @@ -194,6 +195,7 @@ void initSaiApi() sai_api_query(SAI_API_MACSEC, (void **)&sai_macsec_api); sai_api_query(SAI_API_SRV6, (void **)&sai_srv6_api); sai_api_query(SAI_API_L2MC_GROUP, (void **)&sai_l2mc_group_api); + sai_api_query(SAI_API_COUNTER, (void **)&sai_counter_api); sai_api_query(SAI_API_BFD, (void **)&sai_bfd_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); @@ -229,6 +231,7 @@ void initSaiApi() sai_log_set(SAI_API_MACSEC, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_SRV6, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_L2MC_GROUP, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_COUNTER, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BFD, SAI_LOG_LEVEL_NOTICE); } @@ -452,8 +455,8 @@ sai_status_t initSaiPhyApi(swss::gearbox_phy_t *phy) { SWSS_LOG_ERROR("BOX: Failed to get firmware major version:%d rtn:%d", phy->phy_id, status); return status; - } - else + } + else { phy->firmware_major_version = string(attr.value.chardata); } diff --git a/orchagent/trap_rates.lua b/orchagent/trap_rates.lua new file mode 100644 index 000000000000..69b9c5cd3f75 --- /dev/null +++ b/orchagent/trap_rates.lua @@ -0,0 +1,67 @@ +-- KEYS - generic counter IDs +-- ARGV[1] - counters db index +-- ARGV[2] - counters table name +-- ARGV[3] - poll time interval +-- return log + +local logtable = {} + +local function logit(msg) + logtable[#logtable+1] = tostring(msg) +end + +local counters_db = ARGV[1] +local counters_table_name = ARGV[2] +local rates_table_name = "RATES" + +-- Get configuration +redis.call('SELECT', counters_db) +local smooth_interval = redis.call('HGET', rates_table_name .. ':' .. 'TRAP', 'TRAP_SMOOTH_INTERVAL') +local alpha = redis.call('HGET', rates_table_name .. ':' .. 'TRAP', 'TRAP_ALPHA') +if not alpha then + logit("Alpha is not defined") + return logtable +end +local one_minus_alpha = 1.0 - alpha +local delta = tonumber(ARGV[3]) + +logit(alpha) +logit(one_minus_alpha) +logit(delta) + +local n = table.getn(KEYS) +for i = 1, n do + local state_table = rates_table_name .. ':' .. KEYS[i] .. ':' .. 'TRAP' + local initialized = redis.call('HGET', state_table, 'INIT_DONE') + logit(initialized) + + -- Get new COUNTERS values + local in_pkts = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_COUNTER_STAT_PACKETS') + + if initialized == 'DONE' or initialized == 'COUNTERS_LAST' then + -- Get old COUNTERS values + local in_pkts_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_COUNTER_STAT_PACKETS_last') + + -- Calculate new rates values + local rx_pps_new = (in_pkts - in_pkts_last) / delta * 1000 + + if initialized == "DONE" then + -- Get old rates values + local rx_pps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS') + + -- Smooth the rates values and store them in DB + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS', alpha*rx_pps_new + one_minus_alpha*rx_pps_old) + else + -- Store unsmoothed initial rates values in DB + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS', rx_pps_new) + redis.call('HSET', state_table, 'INIT_DONE', 'DONE') + end + else + redis.call('HSET', state_table, 'INIT_DONE', 'COUNTERS_LAST') + end + + -- Set old COUNTERS values + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_COUNTER_STAT_PACKETS_last', in_pkts) +end + +return logtable diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 1e4fd1903ed9..51df17f7299f 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -87,7 +87,7 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/bfdorch.cpp \ $(top_srcdir)/orchagent/srv6orch.cpp -tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp +tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp $(FLEX_CTR_DIR)/flow_counter_handler.cpp tests_SOURCES += $(DEBUG_CTR_DIR)/debug_counter.cpp $(DEBUG_CTR_DIR)/drop_counter.cpp tests_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) diff --git a/tests/test_flex_counters.py b/tests/test_flex_counters.py index 60a3a129d176..ea950af7c16c 100644 --- a/tests/test_flex_counters.py +++ b/tests/test_flex_counters.py @@ -1,48 +1,67 @@ import time import pytest -# Counter keys on ConfigDB -PORT_KEY = "PORT" -QUEUE_KEY = "QUEUE" -RIF_KEY = "RIF" -BUFFER_POOL_WATERMARK_KEY = "BUFFER_POOL_WATERMARK" -PORT_BUFFER_DROP_KEY = "PORT_BUFFER_DROP" -PG_WATERMARK_KEY = "PG_WATERMARK" -ACL_KEY = "ACL" -TUNNEL_KEY = "TUNNEL" - -# Counter stats on FlexCountersDB -PORT_STAT = "PORT_STAT_COUNTER" -QUEUE_STAT = "QUEUE_STAT_COUNTER" -RIF_STAT = "RIF_STAT_COUNTER" -BUFFER_POOL_WATERMARK_STAT = "BUFFER_POOL_WATERMARK_STAT_COUNTER" -PORT_BUFFER_DROP_STAT = "PORT_BUFFER_DROP_STAT" -PG_WATERMARK_STAT = "PG_WATERMARK_STAT_COUNTER" -ACL_STAT = "ACL_STAT_COUNTER" -TUNNEL_STAT = "TUNNEL_STAT_COUNTER" - -# Counter maps on CountersDB -PORT_MAP = "COUNTERS_PORT_NAME_MAP" -QUEUE_MAP = "COUNTERS_QUEUE_NAME_MAP" -RIF_MAP = "COUNTERS_RIF_NAME_MAP" -BUFFER_POOL_WATERMARK_MAP = "COUNTERS_BUFFER_POOL_NAME_MAP" -PORT_BUFFER_DROP_MAP = "COUNTERS_PORT_NAME_MAP" -PG_WATERMARK_MAP = "COUNTERS_PG_NAME_MAP" -ACL_MAP = "ACL_COUNTER_RULE_MAP" -TUNNEL_MAP = "COUNTERS_TUNNEL_NAME_MAP" +from swsscommon import swsscommon TUNNEL_TYPE_MAP = "COUNTERS_TUNNEL_TYPE_MAP" NUMBER_OF_RETRIES = 10 CPU_PORT_OID = "0x0" -counter_type_dict = {"port_counter":[PORT_KEY, PORT_STAT, PORT_MAP], - "queue_counter":[QUEUE_KEY, QUEUE_STAT, QUEUE_MAP], - "rif_counter":[RIF_KEY, RIF_STAT, RIF_MAP], - "buffer_pool_watermark_counter":[BUFFER_POOL_WATERMARK_KEY, BUFFER_POOL_WATERMARK_STAT, BUFFER_POOL_WATERMARK_MAP], - "port_buffer_drop_counter":[PORT_BUFFER_DROP_KEY, PORT_BUFFER_DROP_STAT, PORT_BUFFER_DROP_MAP], - "pg_watermark_counter":[PG_WATERMARK_KEY, PG_WATERMARK_STAT, PG_WATERMARK_MAP], - "acl_counter":[ACL_KEY, ACL_STAT, ACL_MAP], - "vxlan_tunnel_counter":[TUNNEL_KEY, TUNNEL_STAT, TUNNEL_MAP]} +counter_group_meta = { + 'port_counter': { + 'key': 'PORT', + 'group_name': 'PORT_STAT_COUNTER', + 'name_map': 'COUNTERS_PORT_NAME_MAP', + 'post_test': 'post_port_counter_test', + }, + 'queue_counter': { + 'key': 'QUEUE', + 'group_name': 'QUEUE_STAT_COUNTER', + 'name_map': 'COUNTERS_QUEUE_NAME_MAP', + }, + 'rif_counter': { + 'key': 'RIF', + 'group_name': 'RIF_STAT_COUNTER', + 'name_map': 'COUNTERS_RIF_NAME_MAP', + 'pre_test': 'pre_rif_counter_test', + 'post_test': 'post_rif_counter_test', + }, + 'buffer_pool_watermark_counter': { + 'key': 'BUFFER_POOL_WATERMARK', + 'group_name': 'BUFFER_POOL_WATERMARK_STAT_COUNTER', + 'name_map': 'COUNTERS_BUFFER_POOL_NAME_MAP', + }, + 'port_buffer_drop_counter': { + 'key': 'PORT_BUFFER_DROP', + 'group_name': 'PORT_BUFFER_DROP_STAT', + 'name_map': 'COUNTERS_PORT_NAME_MAP', + }, + 'pg_watermark_counter': { + 'key': 'PG_WATERMARK', + 'group_name': 'PG_WATERMARK_STAT_COUNTER', + 'name_map': 'COUNTERS_PG_NAME_MAP', + }, + 'trap_flow_counter': { + 'key': 'FLOW_CNT_TRAP', + 'group_name': 'HOSTIF_TRAP_FLOW_COUNTER', + 'name_map': 'COUNTERS_TRAP_NAME_MAP', + 'post_test': 'post_trap_flow_counter_test', + }, + 'tunnel_counter': { + 'key': 'TUNNEL', + 'group_name': 'TUNNEL_STAT_COUNTER', + 'name_map': 'COUNTERS_TUNNEL_NAME_MAP', + 'pre_test': 'pre_vxlan_tunnel_counter_test', + 'post_test': 'post_vxlan_tunnel_counter_test', + }, + 'acl_counter': { + 'key': 'ACL', + 'group_name': 'ACL_STAT_COUNTER', + 'name_map': 'ACL_COUNTER_RULE_MAP', + 'pre_test': 'pre_acl_tunnel_counter_test', + 'post_test': 'post_acl_tunnel_counter_test', + } +} class TestFlexCounters(object): @@ -51,6 +70,7 @@ def setup_dbs(self, dvs): self.config_db = dvs.get_config_db() self.flex_db = dvs.get_flex_db() self.counters_db = dvs.get_counters_db() + self.app_db = dvs.get_app_db() def wait_for_table(self, table): for retry in range(NUMBER_OF_RETRIES): @@ -62,6 +82,16 @@ def wait_for_table(self, table): assert False, str(table) + " not created in Counters DB" + def wait_for_table_empty(self, table): + for retry in range(NUMBER_OF_RETRIES): + counters_keys = self.counters_db.db_connection.hgetall(table) + if len(counters_keys) == 0: + return + else: + time.sleep(1) + + assert False, str(table) + " is still in Counters DB" + def wait_for_id_list(self, stat, name, oid): for retry in range(NUMBER_OF_RETRIES): id_list = self.flex_db.db_connection.hgetall("FLEX_COUNTER_TABLE:" + stat + ":" + oid).items() @@ -72,6 +102,27 @@ def wait_for_id_list(self, stat, name, oid): assert False, "No ID list for counter " + str(name) + def wait_for_id_list_remove(self, stat, name, oid): + for retry in range(NUMBER_OF_RETRIES): + id_list = self.flex_db.db_connection.hgetall("FLEX_COUNTER_TABLE:" + stat + ":" + oid).items() + if len(id_list) == 0: + return + else: + time.sleep(1) + + assert False, "ID list for counter " + str(name) + " is still there" + + def wait_for_interval_set(self, group, interval): + interval_value = None + for retry in range(NUMBER_OF_RETRIES): + interval_value = self.flex_db.db_connection.hget("FLEX_COUNTER_GROUP_TABLE:" + group, 'POLL_INTERVAL') + if interval_value == interval: + return + else: + time.sleep(1) + + assert False, "Polling interval is not applied to FLEX_COUNTER_GROUP_TABLE for group {}, expect={}, actual={}".format(group, interval, interval_value) + def verify_no_flex_counters_tables(self, counter_stat): counters_stat_keys = self.flex_db.get_keys("FLEX_COUNTER_TABLE:" + counter_stat) assert len(counters_stat_keys) == 0, "FLEX_COUNTER_TABLE:" + str(counter_stat) + " tables exist before enabling the flex counter group" @@ -92,26 +143,34 @@ def verify_flex_counters_populated(self, map, stat): oid = counter_entry[1] self.wait_for_id_list(stat, name, oid) - def verify_tunnel_type_vxlan(self, name_map, type_map): - counters_keys = self.counters_db.db_connection.hgetall(name_map) + def verify_tunnel_type_vxlan(self, meta_data, type_map): + counters_keys = self.counters_db.db_connection.hgetall(meta_data['name_map']) for counter_entry in counters_keys.items(): oid = counter_entry[1] fvs = self.counters_db.get_entry(type_map, "") assert fvs != {} assert fvs.get(oid) == "SAI_TUNNEL_TYPE_VXLAN" - def verify_only_phy_ports_created(self): - port_counters_keys = self.counters_db.db_connection.hgetall(PORT_MAP) - port_counters_stat_keys = self.flex_db.get_keys("FLEX_COUNTER_TABLE:" + PORT_STAT) + def verify_only_phy_ports_created(self, meta_data): + port_counters_keys = self.counters_db.db_connection.hgetall(meta_data['name_map']) + port_counters_stat_keys = self.flex_db.get_keys("FLEX_COUNTER_TABLE:" + meta_data['group_name']) for port_stat in port_counters_stat_keys: assert port_stat in dict(port_counters_keys.items()).values(), "Non PHY port created on PORT_STAT_COUNTER group: {}".format(port_stat) - def enable_flex_counter_group(self, group, map): - group_stats_entry = {"FLEX_COUNTER_STATUS": "enable"} + def set_flex_counter_group_status(self, group, map, status='enable'): + group_stats_entry = {"FLEX_COUNTER_STATUS": status} self.config_db.create_entry("FLEX_COUNTER_TABLE", group, group_stats_entry) - self.wait_for_table(map) + if status == 'enable': + self.wait_for_table(map) + else: + self.wait_for_table_empty(map) - @pytest.mark.parametrize("counter_type", counter_type_dict.keys()) + def set_flex_counter_group_interval(self, key, group, interval): + group_stats_entry = {"POLL_INTERVAL": interval} + self.config_db.create_entry("FLEX_COUNTER_TABLE", key, group_stats_entry) + self.wait_for_interval_set(group, interval) + + @pytest.mark.parametrize("counter_type", counter_group_meta.keys()) def test_flex_counters(self, dvs, counter_type): """ The test will check there are no flex counters tables on FlexCounter DB when the counters are disabled. @@ -119,50 +178,197 @@ def test_flex_counters(self, dvs, counter_type): For some counter types the MAPS on COUNTERS DB will be created as well after enabling the counter group, this will be also verified on this test. """ self.setup_dbs(dvs) - counter_key = counter_type_dict[counter_type][0] - counter_stat = counter_type_dict[counter_type][1] - counter_map = counter_type_dict[counter_type][2] + meta_data = counter_group_meta[counter_type] + counter_key = meta_data['key'] + counter_stat = meta_data['group_name'] + counter_map = meta_data['name_map'] + pre_test = meta_data.get('pre_test') + post_test = meta_data.get('post_test') self.verify_no_flex_counters_tables(counter_stat) - if counter_type == "rif_counter": - self.config_db.db_connection.hset('INTERFACE|Ethernet0', "NULL", "NULL") - self.config_db.db_connection.hset('INTERFACE|Ethernet0|192.168.0.1/24', "NULL", "NULL") - elif counter_type == "acl_counter": - self.config_db.create_entry('ACL_TABLE', 'DATAACL', - { - 'STAGE': 'INGRESS', - 'PORTS': 'Ethernet0', - 'TYPE': 'L3' - } - ) - self.config_db.create_entry('ACL_RULE', 'DATAACL|RULE0', - { - 'ETHER_TYPE': '2048', - 'PACKET_ACTION': 'FORWARD', - 'PRIORITY': '9999' - } - ) - - if counter_type == "vxlan_tunnel_counter": - self.config_db.db_connection.hset("VLAN|Vlan10", "vlanid", "10") - self.config_db.db_connection.hset("VXLAN_TUNNEL|vtep1", "src_ip", "1.1.1.1") - self.config_db.db_connection.hset("VXLAN_TUNNEL_MAP|vtep1|map_100_Vlan10", "vlan", "Vlan10") - self.config_db.db_connection.hset("VXLAN_TUNNEL_MAP|vtep1|map_100_Vlan10", "vni", "100") - - self.enable_flex_counter_group(counter_key, counter_map) + if pre_test: + cb = getattr(self, pre_test) + cb(meta_data) + + self.set_flex_counter_group_status(counter_key, counter_map) self.verify_flex_counters_populated(counter_map, counter_stat) + self.set_flex_counter_group_interval(counter_key, counter_stat, '2500') + + if post_test: + cb = getattr(self, post_test) + cb(meta_data) + + def pre_rif_counter_test(self, meta_data): + self.config_db.db_connection.hset('INTERFACE|Ethernet0', "NULL", "NULL") + self.config_db.db_connection.hset('INTERFACE|Ethernet0|192.168.0.1/24', "NULL", "NULL") + + def pre_vxlan_tunnel_counter_test(self, meta_data): + self.config_db.db_connection.hset("VLAN|Vlan10", "vlanid", "10") + self.config_db.db_connection.hset("VXLAN_TUNNEL|vtep1", "src_ip", "1.1.1.1") + self.config_db.db_connection.hset("VXLAN_TUNNEL_MAP|vtep1|map_100_Vlan10", "vlan", "Vlan10") + self.config_db.db_connection.hset("VXLAN_TUNNEL_MAP|vtep1|map_100_Vlan10", "vni", "100") + + def pre_acl_tunnel_counter_test(self, meta_data): + self.config_db.create_entry('ACL_TABLE', 'DATAACL', + { + 'STAGE': 'INGRESS', + 'PORTS': 'Ethernet0', + 'TYPE': 'L3' + } + ) + self.config_db.create_entry('ACL_RULE', 'DATAACL|RULE0', + { + 'ETHER_TYPE': '2048', + 'PACKET_ACTION': 'FORWARD', + 'PRIORITY': '9999' + } + ) + + def post_rif_counter_test(self, meta_data): + self.config_db.db_connection.hdel('INTERFACE|Ethernet0|192.168.0.1/24', "NULL") + + def post_port_counter_test(self, meta_data): + self.verify_only_phy_ports_created(meta_data) + + def post_trap_flow_counter_test(self, meta_data): + """Post verification for test_flex_counters for trap_flow_counter. Steps: + 1. Disable test_flex_counters + 2. Verify name map and counter ID list are cleared + 3. Clear trap ids to avoid affecting further test cases + + Args: + meta_data (object): flex counter meta data + """ + counters_keys = self.counters_db.db_connection.hgetall(meta_data['name_map']) + self.set_flex_counter_group_status(meta_data['key'], meta_data['group_name'], 'disable') + + for counter_entry in counters_keys.items(): + self.wait_for_id_list_remove(meta_data['group_name'], counter_entry[0], counter_entry[1]) + self.wait_for_table_empty(meta_data['name_map']) + + def post_vxlan_tunnel_counter_test(self, meta_data): + self.verify_tunnel_type_vxlan(meta_data, TUNNEL_TYPE_MAP) + self.config_db.delete_entry("VLAN","Vlan10") + self.config_db.delete_entry("VLAN_TUNNEL","vtep1") + self.config_db.delete_entry("VLAN_TUNNEL_MAP","vtep1|map_100_Vlan10") + self.verify_no_flex_counters_tables_after_delete(meta_data['group_name']) + + def post_acl_tunnel_counter_test(self, meta_data): + self.config_db.delete_entry('ACL_RULE', 'DATAACL|RULE0') + self.config_db.delete_entry('ACL_TABLE', 'DATAACL') + + def test_add_remove_trap(self, dvs): + """Test steps: + 1. Enable trap_flow_counter + 2. Remove a COPP trap + 3. Verify counter is automatically unbind + 4. Add the COPP trap back + 5. Verify counter is added back + + Args: + dvs (object): virtual switch object + """ + self.setup_dbs(dvs) + meta_data = counter_group_meta['trap_flow_counter'] + self.set_flex_counter_group_status(meta_data['key'], meta_data['name_map']) + + removed_trap = None + changed_group = None + trap_ids = None + copp_groups = self.app_db.db_connection.keys('COPP_TABLE:*') + for copp_group in copp_groups: + trap_ids = self.app_db.db_connection.hget(copp_group, 'trap_ids') + if trap_ids and ',' in trap_ids: + trap_ids = [x.strip() for x in trap_ids.split(',')] + removed_trap = trap_ids.pop() + changed_group = copp_group.split(':')[1] + break + + if not removed_trap: + pytest.skip('There is not copp group with more than 1 traps, skip rest of the test') + + oid = None + for _ in range(NUMBER_OF_RETRIES): + counters_keys = self.counters_db.db_connection.hgetall(meta_data['name_map']) + if removed_trap in counters_keys: + oid = counters_keys[removed_trap] + break + else: + time.sleep(1) + + assert oid, 'trap counter is not created for {}'.format(removed_trap) + self.wait_for_id_list(meta_data['group_name'], removed_trap, oid) + + app_copp_table = swsscommon.ProducerStateTable(self.app_db.db_connection, 'COPP_TABLE') + app_copp_table.set(changed_group, [('trap_ids', ','.join(trap_ids))]) + self.wait_for_id_list_remove(meta_data['group_name'], removed_trap, oid) + counters_keys = self.counters_db.db_connection.hgetall(meta_data['name_map']) + assert removed_trap not in counters_keys + + trap_ids.append(removed_trap) + app_copp_table.set(changed_group, [('trap_ids', ','.join(trap_ids))]) + + oid = None + for _ in range(NUMBER_OF_RETRIES): + counters_keys = self.counters_db.db_connection.hgetall(meta_data['name_map']) + if removed_trap in counters_keys: + oid = counters_keys[removed_trap] + break + else: + time.sleep(1) + + assert oid, 'Add trap {}, but trap counter is not created'.format(removed_trap) + self.wait_for_id_list(meta_data['group_name'], removed_trap, oid) + self.set_flex_counter_group_status(meta_data['key'], meta_data['group_name'], 'disable') + + def test_remove_trap_group(self, dvs): + """Remove trap group and verify that all related trap counters are removed + + Args: + dvs (object): virtual switch object + """ + self.setup_dbs(dvs) + meta_data = counter_group_meta['trap_flow_counter'] + self.set_flex_counter_group_status(meta_data['key'], meta_data['name_map']) + + removed_group = None + trap_ids = None + copp_groups = self.app_db.db_connection.keys('COPP_TABLE:*') + for copp_group in copp_groups: + trap_ids = self.app_db.db_connection.hget(copp_group, 'trap_ids') + if trap_ids and trap_ids.strip(): + removed_group = copp_group.split(':')[1] + break + + if not removed_group: + pytest.skip('There is not copp group with at least 1 traps, skip rest of the test') + + trap_ids = [x.strip() for x in trap_ids.split(',')] + for _ in range(NUMBER_OF_RETRIES): + counters_keys = self.counters_db.db_connection.hgetall(meta_data['name_map']) + found = True + for trap_id in trap_ids: + if trap_id not in counters_keys: + found = False + break + if found: + break + else: + time.sleep(1) + + assert found, 'Not all trap id found in name map' + for trap_id in trap_ids: + self.wait_for_id_list(meta_data['group_name'], trap_id, counters_keys[trap_id]) + + app_copp_table = swsscommon.ProducerStateTable(self.app_db.db_connection, 'COPP_TABLE') + app_copp_table._del(removed_group) + + for trap_id in trap_ids: + self.wait_for_id_list_remove(meta_data['group_name'], trap_id, counters_keys[trap_id]) + + counters_keys = self.counters_db.db_connection.hgetall(meta_data['name_map']) + for trap_id in trap_ids: + assert trap_id not in counters_keys - if counter_type == "port_counter": - self.verify_only_phy_ports_created() - elif counter_type == "rif_counter": - self.config_db.db_connection.hdel('INTERFACE|Ethernet0|192.168.0.1/24', "NULL") - elif counter_type == "acl_counter": - self.config_db.delete_entry('ACL_RULE', 'DATAACL|RULE0') - self.config_db.delete_entry('ACL_TABLE', 'DATAACL') - elif counter_type == "vxlan_tunnel_counter": - self.verify_tunnel_type_vxlan(counter_map, TUNNEL_TYPE_MAP) - self.config_db.delete_entry("VLAN","Vlan10") - self.config_db.delete_entry("VLAN_TUNNEL","vtep1") - self.config_db.delete_entry("VLAN_TUNNEL_MAP","vtep1|map_100_Vlan10") - self.verify_no_flex_counters_tables_after_delete(counter_stat) + self.set_flex_counter_group_status(meta_data['key'], meta_data['group_name'], 'disable')