diff --git a/cfgmgr/buffermgrd.cpp b/cfgmgr/buffermgrd.cpp index ac59b87bd8..9bce96f19c 100644 --- a/cfgmgr/buffermgrd.cpp +++ b/cfgmgr/buffermgrd.cpp @@ -78,8 +78,8 @@ int main(int argc, char **argv) CFG_PORT_CABLE_LEN_TABLE_NAME, }; - DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector stateDb("STATE_DB", 0); BufferMgr buffmgr(&cfgDb, &stateDb, pg_lookup_file, cfg_buffer_tables); diff --git a/cfgmgr/intfmgrd.cpp b/cfgmgr/intfmgrd.cpp index 891373dcc7..d184b66e4a 100644 --- a/cfgmgr/intfmgrd.cpp +++ b/cfgmgr/intfmgrd.cpp @@ -48,9 +48,9 @@ int main(int argc, char **argv) CFG_VLAN_SUB_INTF_TABLE_NAME, }; - DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector appDb("APPL_DB", 0); + DBConnector stateDb("STATE_DB", 0); IntfMgr intfmgr(&cfgDb, &appDb, &stateDb, cfg_intf_tables); diff --git a/cfgmgr/nbrmgrd.cpp b/cfgmgr/nbrmgrd.cpp index fc2ed2158d..d9b6829036 100644 --- a/cfgmgr/nbrmgrd.cpp +++ b/cfgmgr/nbrmgrd.cpp @@ -49,9 +49,9 @@ int main(int argc, char **argv) CFG_NEIGH_TABLE_NAME, }; - DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector appDb("APPL_DB", 0); + DBConnector stateDb("STATE_DB", 0); NbrMgr nbrmgr(&cfgDb, &appDb, &stateDb, cfg_nbr_tables); diff --git a/cfgmgr/portmgrd.cpp b/cfgmgr/portmgrd.cpp index d96ac4e962..b0f0c887dd 100644 --- a/cfgmgr/portmgrd.cpp +++ b/cfgmgr/portmgrd.cpp @@ -44,9 +44,9 @@ int main(int argc, char **argv) CFG_PORT_TABLE_NAME, }; - DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector appDb("APPL_DB", 0); + DBConnector stateDb("STATE_DB", 0); PortMgr portmgr(&cfgDb, &appDb, &stateDb, cfg_port_tables); diff --git a/cfgmgr/sflowmgrd.cpp b/cfgmgr/sflowmgrd.cpp index 343f0ead0a..0436ad5f00 100644 --- a/cfgmgr/sflowmgrd.cpp +++ b/cfgmgr/sflowmgrd.cpp @@ -46,8 +46,8 @@ int main(int argc, char **argv) CFG_PORT_TABLE_NAME }; - DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector appDb("APPL_DB", 0); SflowMgr sflowmgr(&cfgDb, &appDb, cfg_sflow_tables); diff --git a/cfgmgr/teammgrd.cpp b/cfgmgr/teammgrd.cpp index bac429e0a3..6217a0a21a 100644 --- a/cfgmgr/teammgrd.cpp +++ b/cfgmgr/teammgrd.cpp @@ -26,9 +26,9 @@ int main(int argc, char **argv) try { - DBConnector conf_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector app_db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector state_db(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector conf_db("CONFIG_DB", 0); + DBConnector app_db("APPL_DB", 0); + DBConnector state_db("STATE_DB", 0); WarmStart::initialize("teammgrd", "teamd"); WarmStart::checkWarmStart("teammgrd", "teamd"); diff --git a/cfgmgr/vlanmgrd.cpp b/cfgmgr/vlanmgrd.cpp index a2bafe0284..88e4745758 100644 --- a/cfgmgr/vlanmgrd.cpp +++ b/cfgmgr/vlanmgrd.cpp @@ -53,9 +53,9 @@ int main(int argc, char **argv) CFG_VLAN_MEMBER_TABLE_NAME, }; - DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector appDb("APPL_DB", 0); + DBConnector stateDb("STATE_DB", 0); WarmStart::initialize("vlanmgrd", "swss"); WarmStart::checkWarmStart("vlanmgrd", "swss"); diff --git a/cfgmgr/vrfmgrd.cpp b/cfgmgr/vrfmgrd.cpp index dc01641002..6a347896b3 100644 --- a/cfgmgr/vrfmgrd.cpp +++ b/cfgmgr/vrfmgrd.cpp @@ -45,9 +45,9 @@ int main(int argc, char **argv) CFG_VNET_TABLE_NAME, }; - DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector appDb("APPL_DB", 0); + DBConnector stateDb("STATE_DB", 0); VrfMgr vrfmgr(&cfgDb, &appDb, &stateDb, cfg_vrf_tables); diff --git a/cfgmgr/vxlanmgrd.cpp b/cfgmgr/vxlanmgrd.cpp index 3acd88bdfe..8e86cfbe49 100644 --- a/cfgmgr/vxlanmgrd.cpp +++ b/cfgmgr/vxlanmgrd.cpp @@ -45,9 +45,9 @@ int main(int argc, char **argv) try { - DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector appDb("APPL_DB", 0); + DBConnector stateDb("STATE_DB", 0); vector cfg_vnet_tables = { CFG_VNET_TABLE_NAME, diff --git a/configure.ac b/configure.ac index 44071996d5..04899b0249 100644 --- a/configure.ac +++ b/configure.ac @@ -36,10 +36,6 @@ AM_CONDITIONAL(DEBUG, test x$debug = xtrue) CFLAGS_COMMON="-std=c++14 -Wall -fPIC -Wno-write-strings -I/usr/include/libnl3 -I/usr/include/swss" -AM_CONDITIONAL(sonic_asic_platform_barefoot, test x$CONFIGURED_PLATFORM = xbarefoot) -AM_COND_IF([sonic_asic_platform_barefoot], - [CFLAGS_COMMON+=" -I/opt/bfn/install/include/switchsai"]) - CFLAGS_COMMON+=" -Werror" CFLAGS_COMMON+=" -Wno-reorder" CFLAGS_COMMON+=" -Wcast-align" diff --git a/fpmsyncd/fpmsyncd.cpp b/fpmsyncd/fpmsyncd.cpp index 8040a7ecb4..59427bf1fa 100644 --- a/fpmsyncd/fpmsyncd.cpp +++ b/fpmsyncd/fpmsyncd.cpp @@ -47,11 +47,11 @@ static bool eoiuFlagsSet(Table &bgpStateTable) int main(int argc, char **argv) { swss::Logger::linkToDbNative("fpmsyncd"); - DBConnector db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector db("APPL_DB", 0); RedisPipeline pipeline(&db); RouteSync sync(&pipeline); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector stateDb("STATE_DB", 0); Table bgpStateTable(&stateDb, STATE_BGP_TABLE_NAME); NetDispatcher::getInstance().registerMessageHandler(RTM_NEWROUTE, &sync); diff --git a/neighsyncd/neighsyncd.cpp b/neighsyncd/neighsyncd.cpp index ad3f9bc065..c14380fb3a 100644 --- a/neighsyncd/neighsyncd.cpp +++ b/neighsyncd/neighsyncd.cpp @@ -15,9 +15,9 @@ int main(int argc, char **argv) { Logger::linkToDbNative("neighsyncd"); - DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector appDb("APPL_DB", 0); RedisPipeline pipelineAppDB(&appDb); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector stateDb("STATE_DB", 0); NeighSync sync(&pipelineAppDB, &stateDb); diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 429d82e5fe..79cfe99f6d 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart -I flex_counter +INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart -I flex_counter -I debug_counter CFLAGS_SAI = -I /usr/include/sai @@ -54,9 +54,11 @@ orchagent_SOURCES = \ watermarkorch.cpp \ policerorch.cpp \ sfloworch.cpp \ - chassisorch.cpp + chassisorch.cpp \ + debugcounterorch.cpp orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp +orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp orchagent_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) orchagent_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index c3d5fd49a8..b12644efd2 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -22,7 +22,7 @@ bool AclOrch::m_bCollectCounters = true; sai_uint32_t AclRule::m_minPriority = 0; sai_uint32_t AclRule::m_maxPriority = 0; -swss::DBConnector AclOrch::m_db(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); +swss::DBConnector AclOrch::m_db("COUNTERS_DB", 0); swss::Table AclOrch::m_countersTable(&m_db, "COUNTERS"); extern sai_acl_api_t* sai_acl_api; @@ -559,7 +559,7 @@ bool AclRule::isActionSupported(sai_acl_entry_attr_t action) const const auto* pTable = m_pAclOrch->getTableByOid(m_tableOid); if (pTable == nullptr) { - SWSS_LOG_THROW("ACL table does not exist for oid %lu", m_tableOid); + SWSS_LOG_THROW("ACL table does not exist for oid %" PRIu64, m_tableOid); } return m_pAclOrch->isAclActionSupported(pTable->stage, action_type); } diff --git a/orchagent/bufferorch.cpp b/orchagent/bufferorch.cpp index ee1de1899d..5465d6a6bd 100644 --- a/orchagent/bufferorch.cpp +++ b/orchagent/bufferorch.cpp @@ -36,10 +36,10 @@ type_map BufferOrch::m_buffer_type_maps = { BufferOrch::BufferOrch(DBConnector *db, vector &tableNames) : Orch(db, tableNames), - m_flexCounterDb(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_flexCounterDb(new DBConnector("FLEX_COUNTER_DB", 0)), m_flexCounterTable(new ProducerTable(m_flexCounterDb.get(), FLEX_COUNTER_TABLE)), m_flexCounterGroupTable(new ProducerTable(m_flexCounterDb.get(), FLEX_COUNTER_GROUP_TABLE)), - m_countersDb(new DBConnector(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_countersDb(new DBConnector("COUNTERS_DB", 0)), m_countersDbRedisClient(m_countersDb.get()) { SWSS_LOG_ENTER(); diff --git a/orchagent/countercheckorch.cpp b/orchagent/countercheckorch.cpp index 68edb69e20..097a3a93ff 100644 --- a/orchagent/countercheckorch.cpp +++ b/orchagent/countercheckorch.cpp @@ -24,7 +24,7 @@ CounterCheckOrch& CounterCheckOrch::getInstance(DBConnector *db) CounterCheckOrch::CounterCheckOrch(DBConnector *db, vector &tableNames): Orch(db, tableNames), - m_countersDb(new DBConnector(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_countersDb(new DBConnector("COUNTERS_DB", 0)), m_countersTable(new Table(m_countersDb.get(), COUNTERS_TABLE)) { SWSS_LOG_ENTER(); diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index 03dcea26dd..537d637063 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -151,7 +151,7 @@ const map crmUsedCntsTableMap = CrmOrch::CrmOrch(DBConnector *db, string tableName): Orch(db, tableName), - m_countersDb(new DBConnector(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_countersDb(new DBConnector("COUNTERS_DB", 0)), m_countersCrmTable(new Table(m_countersDb.get(), COUNTERS_CRM_TABLE)), m_timer(new SelectableTimer(timespec { .tv_sec = CRM_POLLING_INTERVAL_DEFAULT, .tv_nsec = 0 })) { diff --git a/orchagent/debug_counter/debug_counter.cpp b/orchagent/debug_counter/debug_counter.cpp new file mode 100644 index 0000000000..8e01052e6e --- /dev/null +++ b/orchagent/debug_counter/debug_counter.cpp @@ -0,0 +1,112 @@ +#include "debug_counter.h" +#include "drop_counter.h" + +#include +#include +#include +#include +#include +#include "rediscommand.h" +#include +#include "logger.h" + +using std::runtime_error; +using std::string; +using std::unique_ptr; +using std::unordered_map; +using std::unordered_set; +using std::vector; +using swss::FieldValueTuple; + +extern sai_object_id_t gSwitchId; +extern sai_debug_counter_api_t *sai_debug_counter_api; + +// Set of supported attributes to support easy look-up. +const unordered_set DebugCounter::supported_debug_counter_attributes = +{ + COUNTER_ALIAS, + COUNTER_TYPE, + COUNTER_DESCRIPTION, + COUNTER_GROUP +}; + +const std::unordered_map DebugCounter::debug_counter_type_lookup = +{ + { PORT_INGRESS_DROPS, SAI_DEBUG_COUNTER_TYPE_PORT_IN_DROP_REASONS }, + { PORT_EGRESS_DROPS, SAI_DEBUG_COUNTER_TYPE_PORT_OUT_DROP_REASONS }, + { SWITCH_INGRESS_DROPS, SAI_DEBUG_COUNTER_TYPE_SWITCH_IN_DROP_REASONS }, + { SWITCH_EGRESS_DROPS, SAI_DEBUG_COUNTER_TYPE_SWITCH_OUT_DROP_REASONS } +}; + +// It is expected that derived types populate any relevant fields and +// initialize the counter in the SAI. +// +// If counter_type is not a member of debug_counter_type_lookup then this +// constructor will throw a runtime error. +DebugCounter::DebugCounter( + const string& counter_name, + const string& counter_type) + : name(counter_name) +{ + SWSS_LOG_ENTER(); + + auto counter_type_it = debug_counter_type_lookup.find(counter_type); + if (counter_type_it == debug_counter_type_lookup.end()) { + SWSS_LOG_ERROR("Failed to initialize debug counter of type '%s'", + counter_type.c_str()); + throw runtime_error("Failed to initialize debug counter"); + } + type = counter_type_it->first; +} + +// It is expected that derived types delete the counter from the SAI. +DebugCounter::~DebugCounter() +{ + SWSS_LOG_ENTER(); +} + +void DebugCounter::serializeDebugCounterType(sai_attribute_t& type_attribute) +{ + SWSS_LOG_ENTER(); + + sai_debug_counter_type_t sai_counter_type = debug_counter_type_lookup.at(type); + type_attribute.id = SAI_DEBUG_COUNTER_ATTR_TYPE; + type_attribute.value.s32 = sai_counter_type; + + SWSS_LOG_DEBUG("Serializing debug counter of type '%s'", type.c_str()); +} + +// addDebugCounterToSAI creates a new debug counter object in the SAI given a list of debug counter attributes. +// +// If the SAI returns an error then this method will throw a runtime error. +// +// Behavior is undefined if num_attributes is not equal to the number of +// attributes in debug_counter_attributes. +void DebugCounter::addDebugCounterToSAI(const int num_attributes, const sai_attribute_t *debug_counter_attributes) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_DEBUG("Adding debug counter '%s' to SAI", name.c_str()); + sai_object_id_t debug_counter_id; + if (sai_debug_counter_api->create_debug_counter(&debug_counter_id, + gSwitchId, + num_attributes, + debug_counter_attributes) != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed to create debug counter '%s'", name.c_str()); + throw std::runtime_error("Failed to create debug counter"); + } + + SWSS_LOG_DEBUG("Created debug counter '%s' with OID=%lu", name.c_str(), debug_counter_id); + counter_id = debug_counter_id; +} + +void DebugCounter::removeDebugCounterFromSAI() +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_DEBUG("Removing debug counter '%s' from SAI", name.c_str()); + if (sai_debug_counter_api->remove_debug_counter(counter_id) != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed to remove debug counter '%s'", name.c_str()); + throw std::runtime_error("Failed to remove debug counter"); + } +} diff --git a/orchagent/debug_counter/debug_counter.h b/orchagent/debug_counter/debug_counter.h new file mode 100644 index 0000000000..27dec20920 --- /dev/null +++ b/orchagent/debug_counter/debug_counter.h @@ -0,0 +1,64 @@ +#ifndef SWSS_UTIL_DEBUG_COUNTER_H_ +#define SWSS_UTIL_DEBUG_COUNTER_H_ + +#include +#include +#include + +extern "C" { +#include "sai.h" +} + +// Supported debug counter attributes. +#define COUNTER_ALIAS "alias" +#define COUNTER_TYPE "type" +#define COUNTER_DESCRIPTION "desc" +#define COUNTER_GROUP "group" + +// Supported debug counter types. +#define PORT_INGRESS_DROPS "PORT_INGRESS_DROPS" +#define PORT_EGRESS_DROPS "PORT_EGRESS_DROPS" +#define SWITCH_INGRESS_DROPS "SWITCH_INGRESS_DROPS" +#define SWITCH_EGRESS_DROPS "SWITCH_EGRESS_DROPS" + +// DebugCounter represents a SAI debug counter object. +class DebugCounter +{ + public: + DebugCounter(const std::string& counter_name, const std::string& counter_type) noexcept(false); + DebugCounter(const DebugCounter&) = delete; + DebugCounter& operator=(const DebugCounter&) = delete; + virtual ~DebugCounter(); + + std::string getCounterName() const { return name; } + std::string getCounterType() const { return type; } + + virtual std::string getDebugCounterSAIStat() const noexcept(false) = 0; + + static const std::unordered_set& getSupportedDebugCounterAttributes() + { + return supported_debug_counter_attributes; + } + + // TODO: We should try to neatly abstract this like we've done for the isValid methods in DropCounter. + static const std::unordered_map& getDebugCounterTypeLookup() + { + return debug_counter_type_lookup; + } + + protected: + // These methods are intended to help with initialization. Dervied types will most likely + // need to define additional helper methods to serialize additional fields (see DropCounter for example). + void serializeDebugCounterType(sai_attribute_t& type_attribute); + void addDebugCounterToSAI(int num_attrs, const sai_attribute_t *counter_attrs) noexcept(false); + void removeDebugCounterFromSAI() noexcept(false); + + std::string name; + std::string type; + sai_object_id_t counter_id = 0; + + static const std::unordered_set supported_debug_counter_attributes; + static const std::unordered_map debug_counter_type_lookup; +}; + +#endif // _SWSS_UTIL_DEBUG_COUNTER_H_ diff --git a/orchagent/debug_counter/drop_counter.cpp b/orchagent/debug_counter/drop_counter.cpp new file mode 100644 index 0000000000..aab9c4b547 --- /dev/null +++ b/orchagent/debug_counter/drop_counter.cpp @@ -0,0 +1,372 @@ +#include "drop_counter.h" + +#include "logger.h" +#include "sai_serialize.h" + +using std::runtime_error; +using std::string; +using std::unordered_map; +using std::unordered_set; +using std::vector; + +extern sai_object_id_t gSwitchId; +extern sai_debug_counter_api_t *sai_debug_counter_api; + +const unordered_map DropCounter::ingress_drop_reason_lookup = +{ + { L2_ANY, SAI_IN_DROP_REASON_L2_ANY }, + { SMAC_MULTICAST, SAI_IN_DROP_REASON_SMAC_MULTICAST }, + { SMAC_EQUALS_DMAC, SAI_IN_DROP_REASON_SMAC_EQUALS_DMAC }, + { DMAC_RESERVED, SAI_IN_DROP_REASON_DMAC_RESERVED }, + { VLAN_TAG_NOT_ALLOWED, SAI_IN_DROP_REASON_VLAN_TAG_NOT_ALLOWED }, + { INGRESS_VLAN_FILTER, SAI_IN_DROP_REASON_INGRESS_VLAN_FILTER }, + { INGRESS_STP_FILTER, SAI_IN_DROP_REASON_INGRESS_STP_FILTER }, + { FDB_UC_DISCARD, SAI_IN_DROP_REASON_FDB_UC_DISCARD }, + { FDB_MC_DISCARD, SAI_IN_DROP_REASON_FDB_MC_DISCARD }, + { L2_LOOPBACK_FILTER, SAI_IN_DROP_REASON_L2_LOOPBACK_FILTER }, + { EXCEEDS_L2_MTU, SAI_IN_DROP_REASON_EXCEEDS_L2_MTU }, + { L3_ANY, SAI_IN_DROP_REASON_L3_ANY }, + { EXCEEDS_L3_MTU, SAI_IN_DROP_REASON_EXCEEDS_L3_MTU }, + { TTL, SAI_IN_DROP_REASON_TTL }, + { L3_LOOPBACK_FILTER, SAI_IN_DROP_REASON_L3_LOOPBACK_FILTER }, + { NON_ROUTABLE, SAI_IN_DROP_REASON_NON_ROUTABLE }, + { NO_L3_HEADER, SAI_IN_DROP_REASON_NO_L3_HEADER }, + { IP_HEADER_ERROR, SAI_IN_DROP_REASON_IP_HEADER_ERROR }, + { UC_DIP_MC_DMAC, SAI_IN_DROP_REASON_UC_DIP_MC_DMAC }, + { DIP_LOOPBACK, SAI_IN_DROP_REASON_DIP_LOOPBACK }, + { SIP_LOOPBACK, SAI_IN_DROP_REASON_SIP_LOOPBACK }, + { SIP_MC, SAI_IN_DROP_REASON_SIP_MC }, + { SIP_CLASS_E, SAI_IN_DROP_REASON_SIP_CLASS_E }, + { SIP_UNSPECIFIED, SAI_IN_DROP_REASON_SIP_UNSPECIFIED }, + { MC_DMAC_MISMATCH, SAI_IN_DROP_REASON_MC_DMAC_MISMATCH }, + { SIP_EQUALS_DIP, SAI_IN_DROP_REASON_SIP_EQUALS_DIP }, + { SIP_BC, SAI_IN_DROP_REASON_SIP_BC }, + { DIP_LOCAL, SAI_IN_DROP_REASON_DIP_LOCAL }, + { DIP_LINK_LOCAL, SAI_IN_DROP_REASON_DIP_LINK_LOCAL }, + { SIP_LINK_LOCAL, SAI_IN_DROP_REASON_SIP_LINK_LOCAL }, + { IPV6_MC_SCOPE0, SAI_IN_DROP_REASON_IPV6_MC_SCOPE0 }, + { IPV6_MC_SCOPE1, SAI_IN_DROP_REASON_IPV6_MC_SCOPE1 }, + { IRIF_DISABLED, SAI_IN_DROP_REASON_IRIF_DISABLED }, + { ERIF_DISABLED, SAI_IN_DROP_REASON_ERIF_DISABLED }, + { LPM4_MISS, SAI_IN_DROP_REASON_LPM4_MISS }, + { LPM6_MISS, SAI_IN_DROP_REASON_LPM6_MISS }, + { BLACKHOLE_ROUTE, SAI_IN_DROP_REASON_BLACKHOLE_ROUTE }, + { BLACKHOLE_ARP, SAI_IN_DROP_REASON_BLACKHOLE_ARP }, + { UNRESOLVED_NEXT_HOP, SAI_IN_DROP_REASON_UNRESOLVED_NEXT_HOP }, + { L3_EGRESS_LINK_DOWN, SAI_IN_DROP_REASON_L3_EGRESS_LINK_DOWN }, + { DECAP_ERROR, SAI_IN_DROP_REASON_DECAP_ERROR }, + { ACL_ANY, SAI_IN_DROP_REASON_ACL_ANY}, + { ACL_INGRESS_PORT, SAI_IN_DROP_REASON_ACL_INGRESS_PORT }, + { ACL_INGRESS_LAG, SAI_IN_DROP_REASON_ACL_INGRESS_LAG }, + { ACL_INGRESS_VLAN, SAI_IN_DROP_REASON_ACL_INGRESS_VLAN }, + { ACL_INGRESS_RIF, SAI_IN_DROP_REASON_ACL_INGRESS_RIF }, + { ACL_INGRESS_SWITCH, SAI_IN_DROP_REASON_ACL_INGRESS_SWITCH }, + { ACL_EGRESS_PORT, SAI_IN_DROP_REASON_ACL_EGRESS_PORT }, + { ACL_EGRESS_LAG, SAI_IN_DROP_REASON_ACL_EGRESS_LAG }, + { ACL_EGRESS_VLAN, SAI_IN_DROP_REASON_ACL_EGRESS_VLAN }, + { ACL_EGRESS_RIF, SAI_IN_DROP_REASON_ACL_EGRESS_RIF }, + { ACL_EGRESS_SWITCH, SAI_IN_DROP_REASON_ACL_EGRESS_SWITCH } +}; + +const unordered_map DropCounter::egress_drop_reason_lookup = +{ + { L2_ANY, SAI_OUT_DROP_REASON_L2_ANY }, + { EGRESS_VLAN_FILTER, SAI_OUT_DROP_REASON_EGRESS_VLAN_FILTER }, + { L3_ANY, SAI_OUT_DROP_REASON_L3_ANY }, + { L3_EGRESS_LINK_DOWN, SAI_OUT_DROP_REASON_L3_EGRESS_LINK_DOWN }, +}; + +// Need to allocate enough space for the SAI to report the drop reasons, 100 +// gives us plenty of space for both ingress and egress drop reasons. +const uint32_t maxDropReasons = 100; + +// If initialization fails, this constructor will throw a runtime error. +DropCounter::DropCounter(const string& counter_name, const string& counter_type, const unordered_set& drop_reasons) + : DebugCounter(counter_name, counter_type), drop_reasons(drop_reasons) +{ + SWSS_LOG_ENTER(); + initializeDropCounterInSAI(); +} + +DropCounter::~DropCounter() +{ + SWSS_LOG_ENTER(); + try + { + DebugCounter::removeDebugCounterFromSAI(); + } + catch (const std::runtime_error& e) + { + SWSS_LOG_ERROR("Failed to remove drop counter '%s' from SAI", name.c_str()); + } +} + +// If we are unable to query the SAI or the type of counter is not supported +// then this method throws a runtime error. +std::string DropCounter::getDebugCounterSAIStat() const +{ + SWSS_LOG_ENTER(); + + sai_attribute_t index_attribute; + index_attribute.id = SAI_DEBUG_COUNTER_ATTR_INDEX; + if (sai_debug_counter_api->get_debug_counter_attribute(counter_id, 1, &index_attribute) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get stat for debug counter '%s'", name.c_str()); + throw runtime_error("Failed to get debug counter stat"); + } + + auto index = index_attribute.value.u32; + if (type == PORT_INGRESS_DROPS) + { + return sai_serialize_port_stat(static_cast(SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE + index)); + } + else if (type == PORT_EGRESS_DROPS) + { + return sai_serialize_port_stat(static_cast(SAI_PORT_STAT_OUT_DROP_REASON_RANGE_BASE + index)); + } + else if (type == SWITCH_INGRESS_DROPS) + { + return sai_serialize_switch_stat(static_cast(SAI_SWITCH_STAT_IN_DROP_REASON_RANGE_BASE + index)); + } + else if (type == SWITCH_EGRESS_DROPS) + { + return sai_serialize_switch_stat(static_cast(SAI_SWITCH_STAT_OUT_DROP_REASON_RANGE_BASE + index)); + } + else + { + SWSS_LOG_ERROR("No stat found for debug counter '%s' of type '%s'", name.c_str(), type.c_str()); + throw runtime_error("No stat found for debug counter"); + } +} + +// If the drop reason is already present on this counter, this method has no +// effect. +// +// If the update fails, this method throws a runtime error. +void DropCounter::addDropReason(const std::string& drop_reason) +{ + SWSS_LOG_ENTER(); + + if (drop_reasons.find(drop_reason) != drop_reasons.end()) + { + SWSS_LOG_DEBUG("Drop reason '%s' already present on '%s'", drop_reason.c_str(), name.c_str()); + return; + } + + try + { + drop_reasons.emplace(drop_reason); + updateDropReasonsInSAI(); + } + catch (const std::runtime_error& e) + { + drop_reasons.erase(drop_reason); + throw; + } +} + +// If the drop reason is not present on this counter, this method has no +// effect. +// +// If the update fails, this method throws a runtime error. +void DropCounter::removeDropReason(const std::string& drop_reason) +{ + SWSS_LOG_ENTER(); + + auto drop_reason_it = drop_reasons.find(drop_reason); + if (drop_reason_it == drop_reasons.end()) + { + SWSS_LOG_DEBUG("Drop reason '%s' not present on '%s'", drop_reason.c_str(), name.c_str()); + return; + } + + try + { + drop_reasons.erase(drop_reason_it); + updateDropReasonsInSAI(); + } + catch (const std::runtime_error& e) + { + drop_reasons.emplace(drop_reason); + throw e; + } +} + +bool DropCounter::isIngressDropReasonValid(const std::string& drop_reason) +{ + return ingress_drop_reason_lookup.find(drop_reason) != ingress_drop_reason_lookup.end(); +} + +bool DropCounter::isEgressDropReasonValid(const std::string& drop_reason) +{ + return egress_drop_reason_lookup.find(drop_reason) != egress_drop_reason_lookup.end(); +} + +// If initialization fails for any reason, this method throws a runtime error. +void DropCounter::initializeDropCounterInSAI() +{ + sai_attribute_t debug_counter_attributes[2]; + vector drop_reason_list(drop_reasons.size()); + DebugCounter::serializeDebugCounterType(debug_counter_attributes[0]); + DropCounter::serializeDropReasons(static_cast(drop_reasons.size()), drop_reason_list.data(), debug_counter_attributes + 1); + DebugCounter::addDebugCounterToSAI(2, debug_counter_attributes); +} + +// serializeDropReasons takes the list of drop reasons associated with this +// counter and stores them in a SAI readable format in drop_reason_attribute. +// +// This method assumes that drop_reason_list points to a region in memory with +// enough space for drop_reason_count drop reasons to be stored. +// +// If any of the provided drop reasons (or their serialization) is undefined, +// then this method throws a runtime error. +void DropCounter::serializeDropReasons(uint32_t drop_reason_count, int32_t *drop_reason_list, sai_attribute_t *drop_reason_attribute) +{ + SWSS_LOG_ENTER(); + + if (type == PORT_INGRESS_DROPS || type == SWITCH_INGRESS_DROPS) + { + drop_reason_attribute->id = SAI_DEBUG_COUNTER_ATTR_IN_DROP_REASON_LIST; + drop_reason_attribute->value.s32list.count = drop_reason_count; + drop_reason_attribute->value.s32list.list = drop_reason_list; + + int index = 0; + for (auto drop_reason: drop_reasons) + { + auto reason_it = ingress_drop_reason_lookup.find(drop_reason); + if (reason_it == ingress_drop_reason_lookup.end()) + { + SWSS_LOG_ERROR("Ingress drop reason '%s' not found", drop_reason.c_str()); + throw runtime_error("Ingress drop reason not found"); + } + + drop_reason_list[index++] = static_cast(reason_it->second); + } + } + else if (type == PORT_EGRESS_DROPS || type == SWITCH_EGRESS_DROPS) + { + drop_reason_attribute->id = SAI_DEBUG_COUNTER_ATTR_OUT_DROP_REASON_LIST; + drop_reason_attribute->value.s32list.count = drop_reason_count; + drop_reason_attribute->value.s32list.list = drop_reason_list; + + int index = 0; + for (auto drop_reason: drop_reasons) + { + auto reason_it = egress_drop_reason_lookup.find(drop_reason); + if (reason_it == egress_drop_reason_lookup.end()) + { + SWSS_LOG_ERROR("Egress drop reason '%s' not found", drop_reason.c_str()); + throw runtime_error("Egress drop reason not found"); + } + + drop_reason_list[index++] = static_cast(reason_it->second); + } + } + else + { + SWSS_LOG_ERROR("Serialization undefined for drop counter type '%s'", type.c_str()); + throw runtime_error("Failed to serialize drop counter attributes"); + } +} + +// If the SAI update fails, this method throws a runtime error. +void DropCounter::updateDropReasonsInSAI() +{ + SWSS_LOG_ENTER(); + + sai_attribute_t updated_drop_reasons; + vector drop_reason_list(drop_reasons.size()); + serializeDropReasons(static_cast(drop_reasons.size()), drop_reason_list.data(), &updated_drop_reasons); + if (sai_debug_counter_api->set_debug_counter_attribute(counter_id, &updated_drop_reasons) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Could not update drop reasons for drop counter '%s'", name.c_str()); + throw runtime_error("Could not update drop reason list"); + } +} + +// Multiple calls to this function are guaranteed to return the same reasons +// (assuming the device has not been rebooted between calls). The order of +// the list of reasons is not guaranteed to be the same between calls. +// +// If the device does not support querying drop reasons, this method will +// return an empty list. +vector DropCounter::getSupportedDropReasons(sai_debug_counter_attr_t drop_reason_type) +{ + sai_s32_list_t drop_reason_list; + int32_t supported_reasons[maxDropReasons]; + drop_reason_list.count = maxDropReasons; + drop_reason_list.list = supported_reasons; + + if (sai_query_attribute_enum_values_capability(gSwitchId, + SAI_OBJECT_TYPE_DEBUG_COUNTER, + drop_reason_type, + &drop_reason_list) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("This device does not support querying drop reasons"); + return {}; + } + + vector supported_drop_reasons; + for (uint32_t i = 0; i < drop_reason_list.count; i++) + { + string drop_reason; + if (drop_reason_type == SAI_DEBUG_COUNTER_ATTR_IN_DROP_REASON_LIST) + { + drop_reason = sai_serialize_ingress_drop_reason(static_cast(drop_reason_list.list[i])); + } + else + { + drop_reason = sai_serialize_egress_drop_reason(static_cast(drop_reason_list.list[i])); + } + + supported_drop_reasons.push_back(drop_reason); + } + + return supported_drop_reasons; +} + +// serializeSupportedDropReasons takes a list of drop reasons and returns that +// list as a string. +// +// e.g. { "SMAC_EQUALS_DMAC", "INGRESS_VLAN_FILTER" } -> "["SMAC_EQUALS_DMAC","INGRESS_VLAN_FILTER"]" +// e.g. { } -> "[]" +string DropCounter::serializeSupportedDropReasons(vector drop_reasons) +{ + if (drop_reasons.size() == 0) + { + return "[]"; + } + + string supported_drop_reasons; + for (auto const &drop_reason : drop_reasons) + { + supported_drop_reasons += ','; + supported_drop_reasons += drop_reason; + } + + supported_drop_reasons[0] = '['; + return supported_drop_reasons + ']'; +} + +// It is not guaranteed that the amount of available counters will change only +// if counters are added or removed. Depending on the platform, debug counters +// may share hardware resources with other ASIC objects in which case this +// amount may change due to resource allocation in other parts of the system. +// +// If the device does not support querying counter availability, this method +// will return 0. +uint64_t DropCounter::getSupportedDebugCounterAmounts(sai_debug_counter_type_t counter_type) +{ + sai_attribute_t attr; + uint64_t count; + + attr.id = SAI_DEBUG_COUNTER_ATTR_TYPE; + attr.value.s32 = counter_type; + if (sai_object_type_get_availability(gSwitchId, SAI_OBJECT_TYPE_DEBUG_COUNTER, 1, &attr, &count) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("This device does not support querying the number of drop counters"); + return 0; + } + + return count; +} diff --git a/orchagent/debug_counter/drop_counter.h b/orchagent/debug_counter/drop_counter.h new file mode 100644 index 0000000000..8ae34e3d4b --- /dev/null +++ b/orchagent/debug_counter/drop_counter.h @@ -0,0 +1,54 @@ +#ifndef SWSS_UTIL_DROP_COUNTER_H_ +#define SWSS_UTIL_DROP_COUNTER_H_ + +#include +#include +#include +#include +#include "debug_counter.h" +#include "drop_reasons.h" + +extern "C" { +#include "sai.h" +} + +// DropCounter represents a SAI debug counter object that track packet drops. +class DropCounter : public DebugCounter +{ + public: + DropCounter(const std::string& counter_name, + const std::string& counter_type, + const std::unordered_set& drop_reasons) noexcept(false); + DropCounter(const DropCounter&) = delete; + DropCounter& operator=(const DropCounter&) = delete; + virtual ~DropCounter(); + + const std::unordered_set& getDropReasons() const { return drop_reasons; } + + virtual std::string getDebugCounterSAIStat() const noexcept(false); + + void addDropReason(const std::string& drop_reason) noexcept(false); + void removeDropReason(const std::string& drop_reason) noexcept(false); + + static bool isIngressDropReasonValid(const std::string& drop_reason); + static bool isEgressDropReasonValid(const std::string& drop_reason); + + static std::vector getSupportedDropReasons(sai_debug_counter_attr_t drop_reason_type); + static std::string serializeSupportedDropReasons(std::vector drop_reasons); + static uint64_t getSupportedDebugCounterAmounts(sai_debug_counter_type_t counter_type); + + private: + void initializeDropCounterInSAI() noexcept(false); + void serializeDropReasons( + uint32_t drop_reason_count, + int32_t *drop_reason_list, + sai_attribute_t *drop_reason_attribute) noexcept(false); + void updateDropReasonsInSAI() noexcept(false); + + std::unordered_set drop_reasons; + + static const std::unordered_map ingress_drop_reason_lookup; + static const std::unordered_map egress_drop_reason_lookup; +}; + +#endif // SWSS_UTIL_DROP_COUNTER_H_ diff --git a/orchagent/debug_counter/drop_reasons.h b/orchagent/debug_counter/drop_reasons.h new file mode 100644 index 0000000000..08265f21e4 --- /dev/null +++ b/orchagent/debug_counter/drop_reasons.h @@ -0,0 +1,65 @@ +#ifndef DROP_REASONS_H +#define DROP_REASONS_H + +// L2 Drop Reasons +#define L2_ANY "L2_ANY" +#define SMAC_MULTICAST "SMAC_MULTICAST" +#define SMAC_EQUALS_DMAC "SMAC_EQUALS_DMAC" +#define DMAC_RESERVED "DMAC_RESERVED" +#define VLAN_TAG_NOT_ALLOWED "VLAN_TAG_NOT_ALLOWED" +#define INGRESS_VLAN_FILTER "INGRESS_VLAN_FILTER" +#define EGRESS_VLAN_FILTER "EGRESS_VLAN_FILTER" +#define INGRESS_STP_FILTER "INGRESS_STP_FILTER" +#define FDB_UC_DISCARD "FDB_UC_DISCARD" +#define FDB_MC_DISCARD "FDB_MC_DISCARD" +#define L2_LOOPBACK_FILTER "L2_LOOPBACK_FILTER" +#define EXCEEDS_L2_MTU "EXCEEDS_L2_MTU" + +// L3 Drop Reasons +#define L3_ANY "L3_ANY" +#define EXCEEDS_L3_MTU "EXCEEDS_L3_MTU" +#define TTL "TTL" +#define L3_LOOPBACK_FILTER "L3_LOOPBACK_FILTER" +#define NON_ROUTABLE "NON_ROUTABLE" +#define NO_L3_HEADER "NO_L3_HEADER" +#define IP_HEADER_ERROR "IP_HEADER_ERROR" +#define UC_DIP_MC_DMAC "UC_DIP_MC_DMAC" +#define DIP_LOOPBACK "DIP_LOOPBACK" +#define SIP_LOOPBACK "SIP_LOOPBACK" +#define SIP_MC "SIP_MC" +#define SIP_CLASS_E "SIP_CLASS_E" +#define SIP_UNSPECIFIED "SIP_UNSPECIFIED" +#define MC_DMAC_MISMATCH "MC_DMAC_MISMATCH" +#define SIP_EQUALS_DIP "SIP_EQUALS_DIP" +#define SIP_BC "SIP_BC" +#define DIP_LOCAL "DIP_LOCAL" +#define DIP_LINK_LOCAL "DIP_LINK_LOCAL" +#define SIP_LINK_LOCAL "SIP_LINK_LOCAL" +#define IPV6_MC_SCOPE0 "IPV6_MC_SCOPE0" +#define IPV6_MC_SCOPE1 "IPV6_MC_SCOPE1" +#define IRIF_DISABLED "IRIF_DISABLED" +#define ERIF_DISABLED "ERIF_DISABLED" +#define LPM4_MISS "LPM4_MISS" +#define LPM6_MISS "LPM6_MISS" +#define BLACKHOLE_ROUTE "BLACKHOLE_ROUTE" +#define BLACKHOLE_ARP "BLACKHOLE_ARP" +#define UNRESOLVED_NEXT_HOP "UNRESOLVED_NEXT_HOP" +#define L3_EGRESS_LINK_DOWN "L3_EGRESS_LINK_DOWN" + +// Tunnel Drop Reasons +#define DECAP_ERROR "DECAP_ERROR" + +// ACL Drop Reasons +#define ACL_ANY "ACL_ANY" +#define ACL_INGRESS_PORT "ACL_INGRESS_PORT" +#define ACL_INGRESS_LAG "ACL_INGRESS_LAG" +#define ACL_INGRESS_VLAN "ACL_INGRESS_VLAN" +#define ACL_INGRESS_RIF "ACL_INGRESS_RIF" +#define ACL_INGRESS_SWITCH "ACL_INGRESS_SWITCH" +#define ACL_EGRESS_PORT "ACL_EGRESS_PORT" +#define ACL_EGRESS_LAG "ACL_EGRESS_LAG" +#define ACL_EGRESS_VLAN "ACL_EGRESS_VLAN" +#define ACL_EGRESS_RIF "ACL_EGRESS_RIF" +#define ACL_EGRESS_SWITCH "ACL_EGRESS_SWITCH" + +#endif diff --git a/orchagent/debugcounterorch.cpp b/orchagent/debugcounterorch.cpp new file mode 100644 index 0000000000..b70c6625c6 --- /dev/null +++ b/orchagent/debugcounterorch.cpp @@ -0,0 +1,588 @@ +#include "debugcounterorch.h" +#include "portsorch.h" +#include "rediscommand.h" +#include "sai_serialize.h" +#include "schema.h" +#include "drop_counter.h" +#include + +using std::string; +using std::unordered_map; +using std::unordered_set; +using std::vector; + +extern sai_object_id_t gSwitchId; +extern PortsOrch *gPortsOrch; + +static const unordered_map flex_counter_type_lookup = { + { PORT_INGRESS_DROPS, CounterType::PORT_DEBUG }, + { PORT_EGRESS_DROPS, CounterType::PORT_DEBUG }, + { SWITCH_INGRESS_DROPS, CounterType::SWITCH_DEBUG }, + { SWITCH_EGRESS_DROPS, CounterType::SWITCH_DEBUG }, +}; + +// Initializing DebugCounterOrch creates a group entry in FLEX_COUNTER_DB, so this +// object should only be initialized once. +DebugCounterOrch::DebugCounterOrch(DBConnector *db, const vector& table_names, int poll_interval) : + Orch(db, table_names), + flex_counter_manager(DEBUG_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, poll_interval), + m_stateDb(new DBConnector("STATE_DB", 0)), + m_debugCapabilitiesTable(new Table(m_stateDb.get(), STATE_DEBUG_COUNTER_CAPABILITIES_NAME)), + m_countersDb(new DBConnector("COUNTERS_DB", 0)), + m_counterNameToPortStatMap(new Table(m_countersDb.get(), COUNTERS_DEBUG_NAME_PORT_STAT_MAP)), + m_counterNameToSwitchStatMap(new Table(m_countersDb.get(), COUNTERS_DEBUG_NAME_SWITCH_STAT_MAP)) +{ + SWSS_LOG_ENTER(); + publishDropCounterCapabilities(); + flex_counter_manager.enableFlexCounterGroup(); +} + +DebugCounterOrch::~DebugCounterOrch(void) +{ + SWSS_LOG_ENTER(); +} + +// doTask processes updates from the consumer and modifies the state of the +// following components: +// 1) The ASIC, by creating, modifying, and deleting debug counters +// 2) syncd, by creating, modifying, and deleting flex counters to +// keep track of the debug counters +// +// Updates can fail due to the following: +// 1) Malformed requests: if the update contains an unknown or unsupported +// counter type or drop reason then the update will fail +// 2) SAI failures: if the SAI returns an error for any reason then the +// update will fail +// It is guaranteed that failed updates will not modify the state of the +// system. +// +// In addition, updates are idempotent - repeating the same request any number +// of times will always result in the same external behavior. +void DebugCounterOrch::doTask(Consumer& consumer) +{ + SWSS_LOG_ENTER(); + + // Currently we depend on 1) the switch and 2) the ports being ready + // before we can set up the counters. If debug counters for other + // object types are added we may need to update this dependency. + if (!gPortsOrch->allPortsReady()) + { + return; + } + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + string op = kfvOp(t); + std::vector values = kfvFieldsValues(t); + + auto table_name = consumer.getTableName(); + task_process_status task_status = task_process_status::task_ignore; + if (table_name == CFG_DEBUG_COUNTER_TABLE_NAME) + { + if (op == SET_COMMAND) + { + try + { + task_status = installDebugCounter(key, values); + } + catch (const std::runtime_error& e) + { + SWSS_LOG_ERROR("Failed to create debug counter '%s'", key.c_str()); + task_status = task_process_status::task_failed; + } + } + else if (op == DEL_COMMAND) + { + try + { + task_status = uninstallDebugCounter(key); + } + catch (const std::runtime_error& e) + { + SWSS_LOG_ERROR("Failed to delete debug counter '%s'", key.c_str()); + task_status = task_process_status::task_failed; + } + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + } + } + else if (table_name == CFG_DEBUG_COUNTER_DROP_REASON_TABLE_NAME) + { + string counter_name, drop_reason; + parseDropReasonUpdate(key, '|', &counter_name, &drop_reason); + + if (op == SET_COMMAND) + { + try + { + task_status = addDropReason(counter_name, drop_reason); + } + catch (const std::runtime_error& e) + { + SWSS_LOG_ERROR("Failed to add drop reason '%s' to counter '%s'", drop_reason.c_str(), counter_name.c_str()); + task_status = task_process_status::task_failed; + } + } + else if (op == DEL_COMMAND) + { + try + { + task_status = removeDropReason(counter_name, drop_reason); + } + catch (const std::runtime_error& e) + { + SWSS_LOG_ERROR("Failed to remove drop reason '%s' from counter '%s'", drop_reason.c_str(), counter_name.c_str()); + task_status = task_process_status::task_failed; + } + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + } + } + else + { + SWSS_LOG_ERROR("Received update from unknown table '%s'", table_name.c_str()); + } + + switch (task_status) + { + case task_process_status::task_success: + consumer.m_toSync.erase(it++); + break; + case task_process_status::task_ignore: + SWSS_LOG_WARN("Debug counters '%s' task ignored", op.c_str()); + consumer.m_toSync.erase(it++); + break; + case task_process_status::task_need_retry: + SWSS_LOG_NOTICE("Failed to process debug counters '%s' task, retrying", op.c_str()); + ++it; + break; + case task_process_status::task_failed: + SWSS_LOG_ERROR("Failed to process debug counters '%s' task, error(s) occured during execution", op.c_str()); + consumer.m_toSync.erase(it++); + break; + default: + SWSS_LOG_ERROR("Invalid task status %d", task_status); + consumer.m_toSync.erase(it++); + break; + } + } +} + +// Debug Capability Reporting Functions START HERE ------------------------------------------------- + +// publishDropCounterCapabilities queries the SAI for available drop counter +// capabilities on this device and publishes the information to the +// DROP_COUNTER_CAPABILITIES table in STATE_DB. +void DebugCounterOrch::publishDropCounterCapabilities() +{ + string supported_ingress_drop_reasons = DropCounter::serializeSupportedDropReasons( + DropCounter::getSupportedDropReasons(SAI_DEBUG_COUNTER_ATTR_IN_DROP_REASON_LIST)); + string supported_egress_drop_reasons = DropCounter::serializeSupportedDropReasons( + DropCounter::getSupportedDropReasons(SAI_DEBUG_COUNTER_ATTR_OUT_DROP_REASON_LIST)); + + for (auto const &counter_type : DebugCounter::getDebugCounterTypeLookup()) + { + string num_counters = std::to_string(DropCounter::getSupportedDebugCounterAmounts(counter_type.second)); + + string drop_reasons; + if (counter_type.first == PORT_INGRESS_DROPS || counter_type.first == SWITCH_INGRESS_DROPS) + { + drop_reasons = supported_ingress_drop_reasons; + } + else + { + drop_reasons = supported_egress_drop_reasons; + } + + // Only include available capabilities in State DB + if (num_counters != "0" && !drop_reasons.empty()) + { + vector fieldValues; + fieldValues.push_back(FieldValueTuple("count", num_counters)); + fieldValues.push_back(FieldValueTuple("reasons", drop_reasons)); + + SWSS_LOG_DEBUG("Setting '%s' capabilities to count='%s', reasons='%s'", counter_type.first.c_str(), num_counters.c_str(), drop_reasons.c_str()); + m_debugCapabilitiesTable->set(counter_type.first, fieldValues); + } + } +} + +// doTask Handler Functions START HERE ------------------------------------------------------------- + +// Note that this function cannot be used to re-initialize a counter. To create a counter +// with the same name but different attributes (e.g. type, drop reasons, etc.) you will need +// to delete the original counter first or use a function like addDropReason, if available. +task_process_status DebugCounterOrch::installDebugCounter(const string& counter_name, const vector& attributes) +{ + SWSS_LOG_ENTER(); + + if (debug_counters.find(counter_name) != debug_counters.end()) + { + SWSS_LOG_DEBUG("Debug counter '%s' already exists", counter_name.c_str()); + return task_process_status::task_success; + } + + // Note: this method currently assumes that all counters are drop counters. + // If you are adding support for a non-drop counter than it may make sense + // to either: a) dispatch to different handlers in doTask or b) dispatch to + // different helper methods in this method. + + string counter_type = getDebugCounterType(attributes); + addFreeCounter(counter_name, counter_type); + reconcileFreeDropCounters(counter_name); + + SWSS_LOG_NOTICE("Succesfully created drop counter %s", counter_name.c_str()); + return task_process_status::task_success; +} + +task_process_status DebugCounterOrch::uninstallDebugCounter(const string& counter_name) +{ + SWSS_LOG_ENTER(); + + auto it = debug_counters.find(counter_name); + if (it == debug_counters.end()) + { + if (free_drop_counters.find(counter_name) != free_drop_counters.end()) + { + deleteFreeCounter(counter_name); + } + else + { + SWSS_LOG_ERROR("Debug counter %s does not exist", counter_name.c_str()); + } + + return task_process_status::task_ignore; + } + + DebugCounter *counter = it->second.get(); + string counter_type = counter->getCounterType(); + string counter_stat = counter->getDebugCounterSAIStat(); + + debug_counters.erase(it); + uninstallDebugFlexCounters(counter_type, counter_stat); + + if (counter_type == PORT_INGRESS_DROPS || counter_type == PORT_EGRESS_DROPS) + { + m_counterNameToPortStatMap->hdel("", counter_name); + } + else + { + m_counterNameToSwitchStatMap->hdel("", counter_name); + } + + SWSS_LOG_NOTICE("Succesfully deleted drop counter %s", counter_name.c_str()); + return task_process_status::task_success; +} + +task_process_status DebugCounterOrch::addDropReason(const string& counter_name, const string& drop_reason) +{ + SWSS_LOG_ENTER(); + + if (!isDropReasonValid(drop_reason)) + { + return task_failed; + } + + auto it = debug_counters.find(counter_name); + if (it == debug_counters.end()) + { + // In order to gracefully handle the case where the drop reason updates + // are received before the create counter update, we keep track of reasons + // we've seen in the free_drop_reasons table. + addFreeDropReason(counter_name, drop_reason); + reconcileFreeDropCounters(counter_name); + + SWSS_LOG_NOTICE("Succesfully created drop counter %s", counter_name.c_str()); + return task_process_status::task_success; + } + + DropCounter *counter = dynamic_cast(it->second.get()); + counter->addDropReason(drop_reason); + + SWSS_LOG_NOTICE("Added drop reason %s to drop counter %s", drop_reason.c_str(), counter_name.c_str()); + return task_process_status::task_success; +} + +// A drop counter must always contain at least one drop reason, so this function +// will do nothing if you attempt to remove the last drop reason. +task_process_status DebugCounterOrch::removeDropReason(const string& counter_name, const string& drop_reason) +{ + SWSS_LOG_ENTER(); + + if (!isDropReasonValid(drop_reason)) + { + return task_failed; + } + + auto it = debug_counters.find(counter_name); + if (it == debug_counters.end()) + { + deleteFreeDropReason(counter_name, drop_reason); + return task_success; + } + + DropCounter *counter = dynamic_cast(it->second.get()); + const unordered_set& drop_reasons = counter->getDropReasons(); + + if (drop_reasons.size() <= 1) + { + SWSS_LOG_WARN("Attempted to remove all drop reasons from counter '%s'", counter_name.c_str()); + return task_ignore; + } + + counter->removeDropReason(drop_reason); + + SWSS_LOG_NOTICE("Removed drop reason %s from drop counter %s", drop_reason.c_str(), counter_name.c_str()); + return task_success; +} + +// Free Table Management Functions START HERE ------------------------------------------------------ + +// Note that entries will remain in the table until at least one drop reason is added to the counter. +void DebugCounterOrch::addFreeCounter(const string& counter_name, const string& counter_type) +{ + SWSS_LOG_ENTER(); + + if (free_drop_counters.find(counter_name) != free_drop_counters.end()) + { + SWSS_LOG_DEBUG("Debug counter '%s' is in free counter table", counter_name.c_str()); + return; + } + + SWSS_LOG_DEBUG("Adding debug counter '%s' to free counter table", counter_name.c_str()); + free_drop_counters.emplace(counter_name, counter_type); +} + +void DebugCounterOrch::deleteFreeCounter(const string& counter_name) +{ + SWSS_LOG_ENTER(); + + if (free_drop_counters.find(counter_name) == free_drop_counters.end()) + { + SWSS_LOG_ERROR("Debug counter %s does not exist", counter_name.c_str()); + return; + } + + SWSS_LOG_DEBUG("Removing debug counter '%s' from free counter table", counter_name.c_str()); + free_drop_counters.erase(counter_name); +} + +// Note that entries will remain in the table until a drop counter is added. +void DebugCounterOrch::addFreeDropReason(const string& counter_name, const string& drop_reason) +{ + SWSS_LOG_ENTER(); + + auto reasons_it = free_drop_reasons.find(counter_name); + + if (reasons_it == free_drop_reasons.end()) + { + SWSS_LOG_DEBUG("Creating free drop reason table for counter '%s'", counter_name.c_str()); + unordered_set new_reasons = { drop_reason }; + free_drop_reasons.emplace(make_pair(counter_name, new_reasons)); + } + else + { + SWSS_LOG_DEBUG("Adding additional drop reasons to free drop reason table for counter '%s'", counter_name.c_str()); + reasons_it->second.emplace(drop_reason); + } +} + +void DebugCounterOrch::deleteFreeDropReason(const string& counter_name, const string& drop_reason) +{ + SWSS_LOG_ENTER(); + + auto reasons_it = free_drop_reasons.find(counter_name); + + if (reasons_it == free_drop_reasons.end()) { + SWSS_LOG_DEBUG("Attempted to remove drop reason '%s' from counter '%s' that does not exist", drop_reason.c_str(), counter_name.c_str()); + return; + } + + SWSS_LOG_DEBUG("Removing free drop reason from counter '%s'", counter_name.c_str()); + reasons_it->second.erase(drop_reason); + + if (reasons_it->second.empty()) { + free_drop_reasons.erase(reasons_it); + } +} + +void DebugCounterOrch::reconcileFreeDropCounters(const string& counter_name) +{ + SWSS_LOG_ENTER(); + + auto counter_it = free_drop_counters.find(counter_name); + auto reasons_it = free_drop_reasons.find(counter_name); + + if (counter_it != free_drop_counters.end() && reasons_it != free_drop_reasons.end()) + { + SWSS_LOG_DEBUG("Found counter '%s' and drop reasons, creating the counter", counter_name.c_str()); + createDropCounter(counter_name, counter_it->second, reasons_it->second); + free_drop_counters.erase(counter_it); + free_drop_reasons.erase(reasons_it); + SWSS_LOG_NOTICE("Succesfully created new drop counter %s", counter_name.c_str()); + } +} + +// Flex Counter Management Functions START HERE ---------------------------------------------------- + +CounterType DebugCounterOrch::getFlexCounterType(const string& counter_type) +{ + SWSS_LOG_ENTER(); + + auto flex_counter_type_it = flex_counter_type_lookup.find(counter_type); + if (flex_counter_type_it == flex_counter_type_lookup.end()) + { + SWSS_LOG_ERROR("Flex counter type '%s' not found", counter_type.c_str()); + throw runtime_error("Flex counter type not found"); + } + return flex_counter_type_it->second; +} + +void DebugCounterOrch::installDebugFlexCounters(const string& counter_type, + const string& counter_stat) +{ + SWSS_LOG_ENTER(); + CounterType flex_counter_type = getFlexCounterType(counter_type); + + if (flex_counter_type == CounterType::SWITCH_DEBUG) + { + flex_counter_manager.addFlexCounterStat(gSwitchId, flex_counter_type, counter_stat); + } + else if (flex_counter_type == CounterType::PORT_DEBUG) + { + for (auto const &curr : gPortsOrch->getAllPorts()) + { + if (curr.second.m_type != Port::Type::PHY) + { + continue; + } + + flex_counter_manager.addFlexCounterStat( + curr.second.m_port_id, + flex_counter_type, + counter_stat); + } + } +} + +void DebugCounterOrch::uninstallDebugFlexCounters(const string& counter_type, + const string& counter_stat) +{ + SWSS_LOG_ENTER(); + CounterType flex_counter_type = getFlexCounterType(counter_type); + + if (flex_counter_type == CounterType::SWITCH_DEBUG) + { + flex_counter_manager.removeFlexCounterStat(gSwitchId, flex_counter_type, counter_stat); + } + else if (flex_counter_type == CounterType::PORT_DEBUG) + { + for (auto const &curr : gPortsOrch->getAllPorts()) + { + if (curr.second.m_type != Port::Type::PHY) + { + continue; + } + + flex_counter_manager.removeFlexCounterStat( + curr.second.m_port_id, + flex_counter_type, + counter_stat); + } + } +} + +// Debug Counter Initialization Helper Functions START HERE ---------------------------------------- + +// NOTE: At this point COUNTER_TYPE is the only field from CONFIG_DB that we care about. In the future +// if other fields become relevant it may make sense to extend this method and return a struct with the +// relevant fields. +std::string DebugCounterOrch::getDebugCounterType(const vector& values) const +{ + SWSS_LOG_ENTER(); + + std::string counter_type; + for (auto attr : values) + { + std::string attr_name = fvField(attr); + auto supported_debug_counter_attributes = DebugCounter::getSupportedDebugCounterAttributes(); + auto attr_name_it = supported_debug_counter_attributes.find(attr_name); + if (attr_name_it == supported_debug_counter_attributes.end()) + { + SWSS_LOG_ERROR("Unknown debug counter attribute '%s'", attr_name.c_str()); + continue; + } + + std::string attr_value = fvValue(attr); + if (attr_name == "type") + { + auto debug_counter_type_lookup = DebugCounter::getDebugCounterTypeLookup(); + auto counter_type_it = debug_counter_type_lookup.find(attr_value); + if (counter_type_it == debug_counter_type_lookup.end()) + { + SWSS_LOG_ERROR("Debug counter type '%s' does not exist", attr_value.c_str()); + throw std::runtime_error("Failed to initialize debug counter"); + } + + counter_type = counter_type_it->first; + } + } + + return counter_type; +} + +// createDropCounter creates a new drop counter in the SAI and installs a +// flex counter to poll the counter data. +// +// If SAI initialization fails or flex counter installation fails then this +// method will throw an exception. +void DebugCounterOrch::createDropCounter(const string& counter_name, const string& counter_type, const unordered_set& drop_reasons) +{ + auto counter = std::unique_ptr(new DropCounter(counter_name, counter_type, drop_reasons)); + std::string counter_stat = counter->getDebugCounterSAIStat(); + debug_counters.emplace(counter_name, std::move(counter)); + installDebugFlexCounters(counter_type, counter_stat); + + if (counter_type == PORT_INGRESS_DROPS || counter_type == PORT_EGRESS_DROPS) + { + m_counterNameToPortStatMap->set("", { FieldValueTuple(counter_name, counter_stat) }); + } + else + { + m_counterNameToSwitchStatMap->set("", { FieldValueTuple(counter_name, counter_stat) }); + } +} + +// Debug Counter Configuration Helper Functions START HERE ----------------------------------------- + +// parseDropReasonUpdate takes a key from CONFIG_DB and returns the 1) the counter name being targeted and +// 2) the drop reason to be added or removed via output parameters. +void DebugCounterOrch::parseDropReasonUpdate(const string& key, const char delimeter, string *counter_name, string *drop_reason) const +{ + size_t counter_end = key.find(delimeter); + *counter_name = key.substr(0, counter_end); + SWSS_LOG_DEBUG("DEBUG_COUNTER COUNTER NAME = %s (%d, %zd)", counter_name->c_str(), 0, counter_end); + *drop_reason = key.substr(counter_end + 1); + SWSS_LOG_DEBUG("DEBUG_COUNTER RULE NAME = %s (%zd, %zd)", drop_reason->c_str(), counter_end + 1, key.length()); +} + +bool DebugCounterOrch::isDropReasonValid(const string& drop_reason) const +{ + SWSS_LOG_ENTER(); + + if (!DropCounter::isIngressDropReasonValid(drop_reason) && + !DropCounter::isEgressDropReasonValid(drop_reason)) + { + SWSS_LOG_ERROR("Drop reason %s not found", drop_reason.c_str()); + return false; + } + + return true; +} diff --git a/orchagent/debugcounterorch.h b/orchagent/debugcounterorch.h new file mode 100644 index 0000000000..49ca64b7f7 --- /dev/null +++ b/orchagent/debugcounterorch.h @@ -0,0 +1,97 @@ +#ifndef DEBUG_COUNTER_ORCH_H +#define DEBUG_COUNTER_ORCH_H + +#include +#include +#include +#include + +#include "orch.h" +#include "flex_counter_stat_manager.h" +#include "debug_counter.h" +#include "drop_counter.h" + +extern "C" { +#include "sai.h" +} + +#define DEBUG_COUNTER_FLEX_COUNTER_GROUP "DEBUG_COUNTER" + +// DebugCounterOrch is an orchestrator for managing debug counters. It handles +// the creation, deletion, and modification of debug counters. +class DebugCounterOrch: public Orch +{ +public: + DebugCounterOrch(swss::DBConnector *db, const std::vector& table_names, int poll_interval); + virtual ~DebugCounterOrch(void); + + void doTask(Consumer& consumer); + +private: + // Debug Capability Reporting Functions + void publishDropCounterCapabilities(); + + // doTask Handler Functions + task_process_status installDebugCounter(const std::string& counter_name, const std::vector& attributes); + task_process_status uninstallDebugCounter(const std::string& counter_name); + task_process_status addDropReason(const std::string& counter_name, const std::string& drop_reason); + task_process_status removeDropReason(const std::string& counter_name, const std::string& drop_reason); + + // Free Table Management Functions + void addFreeCounter(const std::string& counter_name, const std::string& counter_type); + void deleteFreeCounter(const std::string& counter_name); + void addFreeDropReason(const std::string& counter_name, const std::string& drop_reason); + void deleteFreeDropReason(const std::string& counter_name, const std::string& drop_reason); + void reconcileFreeDropCounters(const std::string& counter_name); + + // Flex Counter Management Functions + CounterType getFlexCounterType(const std::string& counter_type) noexcept(false); + void installDebugFlexCounters( + const std::string& counter_type, + const std::string& counter_stat); + void uninstallDebugFlexCounters( + const std::string& counter_type, + const std::string& counter_stat); + + // Debug Counter Initialization Helper Functions + std::string getDebugCounterType( + const std::vector& values) const noexcept(false); + void createDropCounter( + const std::string& counter_name, + const std::string& counter_type, + const std::unordered_set& drop_reasons) noexcept(false); + + // Debug Counter Configuration Helper Functions + void parseDropReasonUpdate( + const std::string& key, + const char delimeter, + std::string *counter_name, + std::string *drop_reason) const; + bool isDropReasonValid(const std::string& drop_reason) const; + + // Data Members + std::shared_ptr m_stateDb = nullptr; + std::shared_ptr m_debugCapabilitiesTable = nullptr; + + std::shared_ptr m_countersDb = nullptr; + std::shared_ptr m_counterNameToPortStatMap = nullptr; + std::shared_ptr m_counterNameToSwitchStatMap = nullptr; + + FlexCounterStatManager flex_counter_manager; + + std::unordered_map> debug_counters; + + // free_drop_counters are drop counters that have been created by a user + // that do not have any drop reasons associated with them yet. Because + // we cannot create a drop counter without any drop reasons, we keep track + // of these counters in this table. + std::unordered_map free_drop_counters; + + // free_drop_reasons are drop reasons that have been added by a user + // that do not have a counter associated with them yet. Because we + // cannot add drop reasons to a counter that doesn't exist yet, + // we keep track of the reasons in this table. + std::unordered_map> free_drop_reasons; +}; + +#endif diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index 2c0ff988df..9a54ba98f1 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -32,7 +32,7 @@ FdbOrch::FdbOrch(TableConnector applDbConnector, TableConnector stateDbConnector Orch::addExecutor(flushNotifier); /* Add FDB notifications support from ASIC */ - DBConnector *notificationsDb = new DBConnector(ASIC_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector *notificationsDb = new DBConnector("ASIC_DB", 0); m_fdbNotificationConsumer = new swss::NotificationConsumer(notificationsDb, "NOTIFICATIONS"); auto fdbNotifier = new Notifier(m_fdbNotificationConsumer, this, "FDB_NOTIFICATIONS"); Orch::addExecutor(fdbNotifier); diff --git a/orchagent/flex_counter/flex_counter_manager.cpp b/orchagent/flex_counter/flex_counter_manager.cpp index 0b0eddd8c4..323bd309c4 100644 --- a/orchagent/flex_counter/flex_counter_manager.cpp +++ b/orchagent/flex_counter/flex_counter_manager.cpp @@ -45,7 +45,7 @@ FlexCounterManager::FlexCounterManager( stats_mode(stats_mode), polling_interval(polling_interval), enabled(false), - flex_counter_db(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + flex_counter_db(new DBConnector("FLEX_COUNTER_DB", 0)), flex_counter_group_table(new ProducerTable(flex_counter_db.get(), FLEX_COUNTER_GROUP_TABLE)), flex_counter_table(new ProducerTable(flex_counter_db.get(), FLEX_COUNTER_TABLE)) { @@ -222,4 +222,4 @@ string FlexCounterManager::serializeCounterStats( } return stats_string; -} \ No newline at end of file +} diff --git a/orchagent/flexcounterorch.cpp b/orchagent/flexcounterorch.cpp index 8493f6897a..571b69c50f 100644 --- a/orchagent/flexcounterorch.cpp +++ b/orchagent/flexcounterorch.cpp @@ -7,6 +7,8 @@ #include "sai_serialize.h" #include "pfcwdorch.h" #include "bufferorch.h" +#include "flexcounterorch.h" +#include "debugcounterorch.h" extern sai_port_api_t *sai_port_api; @@ -25,12 +27,13 @@ unordered_map flexCounterGroupMap = {"PG_WATERMARK", PG_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP}, {BUFFER_POOL_WATERMARK_KEY, BUFFER_POOL_WATERMARK_STAT_COUNTER_FLEX_COUNTER_GROUP}, {"RIF", RIF_STAT_COUNTER_FLEX_COUNTER_GROUP}, + {"DEBUG_COUNTER", DEBUG_COUNTER_FLEX_COUNTER_GROUP}, }; FlexCounterOrch::FlexCounterOrch(DBConnector *db, vector &tableNames): Orch(db, tableNames), - m_flexCounterDb(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_flexCounterDb(new DBConnector("FLEX_COUNTER_DB", 0)), m_flexCounterGroupTable(new ProducerTable(m_flexCounterDb.get(), FLEX_COUNTER_GROUP_TABLE)) { SWSS_LOG_ENTER(); diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index f540224129..ddf2240455 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -55,9 +55,9 @@ IntfsOrch::IntfsOrch(DBConnector *db, string tableName, VRFOrch *vrf_orch) : SWSS_LOG_ENTER(); /* Initialize DB connectors */ - m_counter_db = shared_ptr(new DBConnector(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)); - m_flex_db = shared_ptr(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)); - m_asic_db = shared_ptr(new DBConnector(ASIC_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)); + m_counter_db = shared_ptr(new DBConnector("COUNTERS_DB", 0)); + m_flex_db = shared_ptr(new DBConnector("FLEX_COUNTER_DB", 0)); + m_asic_db = shared_ptr(new DBConnector("ASIC_DB", 0)); /* Initialize COUNTER_DB tables */ m_rifNameTable = unique_ptr(new Table(m_counter_db.get(), COUNTERS_RIF_NAME_MAP)); m_rifTypeTable = unique_ptr
(new Table(m_counter_db.get(), COUNTERS_RIF_TYPE_MAP)); diff --git a/orchagent/main.cpp b/orchagent/main.cpp index a0bb8af524..a5591b9d00 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -291,9 +291,9 @@ int main(int argc, char **argv) SWSS_LOG_NOTICE("Created underlay router interface ID %" PRIx64, gUnderlayIfId); /* Initialize orchestration components */ - DBConnector appl_db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector config_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector state_db(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector appl_db("APPL_DB", 0); + DBConnector config_db("CONFIG_DB", 0); + DBConnector state_db("STATE_DB", 0); auto orchDaemon = make_shared(&appl_db, &config_db, &state_db); diff --git a/orchagent/orchagent_restart_check.cpp b/orchagent/orchagent_restart_check.cpp index aaf0901427..f91f701a53 100644 --- a/orchagent/orchagent_restart_check.cpp +++ b/orchagent/orchagent_restart_check.cpp @@ -105,7 +105,7 @@ int main(int argc, char **argv) } } - swss::DBConnector db(APPL_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0); + swss::DBConnector db("APPL_DB", 0); // Send warm restart query via "RESTARTCHECK" notification channel swss::NotificationProducer restartQuery(&db, "RESTARTCHECK"); // Will listen for the reply on "RESTARTCHECKREPLY" channel diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index ada19d1494..756b8a35eb 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -200,6 +200,13 @@ bool OrchDaemon::init() }; SflowOrch *sflow_orch = new SflowOrch(m_applDb, sflow_tables); + vector debug_counter_tables = { + CFG_DEBUG_COUNTER_TABLE_NAME, + CFG_DEBUG_COUNTER_DROP_REASON_TABLE_NAME + }; + + DebugCounterOrch *debug_counter_orch = new DebugCounterOrch(m_configDb, debug_counter_tables, 1000); + /* * The order of the orch list is important for state restore of warm start and * the queued processing in m_toSync map after gPortsOrch->allPortsReady() is set. @@ -208,8 +215,7 @@ bool OrchDaemon::init() * when iterating ConsumerMap. * That is ensured implicitly by the order of map key, "LAG_TABLE" is smaller than "VLAN_TABLE" in lexicographic order. */ - m_orchList = { gSwitchOrch, gCrmOrch, gBufferOrch, gPortsOrch, gIntfsOrch, gNeighOrch, gRouteOrch, copp_orch, tunnel_decap_orch, qos_orch, wm_orch, policer_orch, sflow_orch}; - + m_orchList = { gSwitchOrch, gCrmOrch, gBufferOrch, gPortsOrch, gIntfsOrch, gNeighOrch, gRouteOrch, copp_orch, tunnel_decap_orch, qos_orch, wm_orch, policer_orch, sflow_orch, debug_counter_orch}; bool initialize_dtel = false; if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index bfa52fdef8..5e9c9b914a 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -28,6 +28,7 @@ #include "watermarkorch.h" #include "policerorch.h" #include "sfloworch.h" +#include "debugcounterorch.h" #include "directory.h" using namespace swss; diff --git a/orchagent/pfcwdorch.cpp b/orchagent/pfcwdorch.cpp index d6635b49b3..d9de320527 100644 --- a/orchagent/pfcwdorch.cpp +++ b/orchagent/pfcwdorch.cpp @@ -36,7 +36,7 @@ extern PortsOrch *gPortsOrch; template PfcWdOrch::PfcWdOrch(DBConnector *db, vector &tableNames): Orch(db, tableNames), - m_countersDb(new DBConnector(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_countersDb(new DBConnector("COUNTERS_DB", 0)), m_countersTable(new Table(m_countersDb.get(), COUNTERS_TABLE)) { SWSS_LOG_ENTER(); @@ -652,14 +652,14 @@ PfcWdSwOrch::PfcWdSwOrch( const vector &queueAttrIds, int pollInterval): PfcWdOrch(db, tableNames), - m_flexCounterDb(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_flexCounterDb(new DBConnector("FLEX_COUNTER_DB", 0)), m_flexCounterTable(new ProducerTable(m_flexCounterDb.get(), FLEX_COUNTER_TABLE)), m_flexCounterGroupTable(new ProducerTable(m_flexCounterDb.get(), FLEX_COUNTER_GROUP_TABLE)), c_portStatIds(portStatIds), c_queueStatIds(queueStatIds), c_queueAttrIds(queueAttrIds), m_pollInterval(pollInterval), - m_applDb(make_shared(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_applDb(make_shared("APPL_DB", 0)), m_applTable(make_shared
(m_applDb.get(), APP_PFC_WD_TABLE_NAME "_INSTORM")), m_applDbRedisClient(m_applDb.get()) { diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 586a81f755..30e93430ca 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -156,7 +156,7 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) SWSS_LOG_ENTER(); /* Initialize counter table */ - m_counter_db = shared_ptr(new DBConnector(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)); + m_counter_db = shared_ptr(new DBConnector("COUNTERS_DB", 0)); m_counterTable = unique_ptr
(new Table(m_counter_db.get(), COUNTERS_PORT_NAME_MAP)); /* Initialize port table */ @@ -173,7 +173,7 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) m_pgPortTable = unique_ptr
(new Table(m_counter_db.get(), COUNTERS_PG_PORT_MAP)); m_pgIndexTable = unique_ptr
(new Table(m_counter_db.get(), COUNTERS_PG_INDEX_MAP)); - m_flex_db = shared_ptr(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)); + m_flex_db = shared_ptr(new DBConnector("FLEX_COUNTER_DB", 0)); m_flexCounterTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_TABLE)); m_flexCounterGroupTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_GROUP_TABLE)); @@ -315,7 +315,7 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) removeDefaultBridgePorts(); /* Add port oper status notification support */ - DBConnector *notificationsDb = new DBConnector(ASIC_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector *notificationsDb = new DBConnector("ASIC_DB", 0); m_portStatusNotificationConsumer = new swss::NotificationConsumer(notificationsDb, "NOTIFICATIONS"); auto portStatusNotificatier = new Notifier(m_portStatusNotificationConsumer, this, "PORT_STATUS_NOTIFICATIONS"); Orch::addExecutor(portStatusNotificatier); diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp index 4e92275d79..a3a317a986 100644 --- a/orchagent/qosorch.cpp +++ b/orchagent/qosorch.cpp @@ -278,7 +278,7 @@ sai_object_id_t Dot1pToTcMapHandler::addQosItem(const vector &a SWSS_LOG_ERROR("Failed to create dot1p_to_tc map. status: %s", sai_serialize_status(sai_status).c_str()); return SAI_NULL_OBJECT_ID; } - SWSS_LOG_DEBUG("created QosMap object: 0x%lx", object_id); + SWSS_LOG_DEBUG("created QosMap object: 0x%" PRIx64, object_id); return object_id; } diff --git a/orchagent/routeresync.cpp b/orchagent/routeresync.cpp index 871d1c34e8..477226374b 100644 --- a/orchagent/routeresync.cpp +++ b/orchagent/routeresync.cpp @@ -19,7 +19,7 @@ int main(int argc, char **argv) SWSS_LOG_ENTER(); - DBConnector db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector db("APPL_DB", 0); ProducerStateTable r(&db, APP_ROUTE_TABLE_NAME); if (argc != 2) diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index dbadc7e288..9fbe449179 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -42,6 +42,7 @@ sai_fdb_api_t* sai_fdb_api; sai_dtel_api_t* sai_dtel_api; sai_bmtor_api_t* sai_bmtor_api; sai_samplepacket_api_t* sai_samplepacket_api; +sai_debug_counter_api_t* sai_debug_counter_api; extern sai_object_id_t gSwitchId; extern bool gSairedisRecord; @@ -132,6 +133,7 @@ void initSaiApi() sai_api_query(SAI_API_DTEL, (void **)&sai_dtel_api); sai_api_query((sai_api_t)SAI_API_BMTOR, (void **)&sai_bmtor_api); sai_api_query(SAI_API_SAMPLEPACKET, (void **)&sai_samplepacket_api); + sai_api_query(SAI_API_DEBUG_COUNTER, (void **)&sai_debug_counter_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -159,6 +161,7 @@ void initSaiApi() sai_log_set(SAI_API_DTEL, SAI_LOG_LEVEL_NOTICE); sai_log_set((sai_api_t)SAI_API_BMTOR, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_SAMPLEPACKET, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_DEBUG_COUNTER, SAI_LOG_LEVEL_NOTICE); } void initSaiRedis(const string &record_location) diff --git a/orchagent/watermarkorch.cpp b/orchagent/watermarkorch.cpp index 0b8c20e911..7860ef7cc1 100644 --- a/orchagent/watermarkorch.cpp +++ b/orchagent/watermarkorch.cpp @@ -23,8 +23,8 @@ WatermarkOrch::WatermarkOrch(DBConnector *db, const vector &tables): { SWSS_LOG_ENTER(); - m_countersDb = make_shared(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - m_appDb = make_shared(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + m_countersDb = make_shared("COUNTERS_DB", 0); + m_appDb = make_shared("APPL_DB", 0); m_countersTable = make_shared
(m_countersDb.get(), COUNTERS_TABLE); m_periodicWatermarkTable = make_shared
(m_countersDb.get(), PERIODIC_WATERMARKS_TABLE); m_persistentWatermarkTable = make_shared
(m_countersDb.get(), PERSISTENT_WATERMARKS_TABLE); diff --git a/portsyncd/portsyncd.cpp b/portsyncd/portsyncd.cpp index 7efec9d58a..8d618c80cb 100644 --- a/portsyncd/portsyncd.cpp +++ b/portsyncd/portsyncd.cpp @@ -65,9 +65,9 @@ int main(int argc, char **argv) } } - DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector appl_db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector state_db(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector cfgDb("CONFIG_DB", 0); + DBConnector appl_db("APPL_DB", 0); + DBConnector state_db("STATE_DB", 0); ProducerStateTable p(&appl_db, APP_PORT_TABLE_NAME); SubscriberStateTable portCfg(&cfgDb, CFG_PORT_TABLE_NAME); diff --git a/swssconfig/swssconfig.cpp b/swssconfig/swssconfig.cpp index 02fdaaaa13..058b83a56b 100644 --- a/swssconfig/swssconfig.cpp +++ b/swssconfig/swssconfig.cpp @@ -15,8 +15,6 @@ using namespace std; using namespace swss; using json = nlohmann::json; -int db_port = 6379; -const char* const hostname = "localhost"; const char* const op_name = "OP"; const char* const name_delimiter = ":"; const int el_count = 2; @@ -43,7 +41,7 @@ void dump_db_item(KeyOpFieldsValuesTuple &db_item) bool write_db_data(vector &db_items) { - DBConnector db(APPL_DB, hostname, db_port, 0); + DBConnector db("APPL_DB", 0, true); for (auto &db_item : db_items) { dump_db_item(db_item); diff --git a/swssconfig/swssplayer.cpp b/swssconfig/swssplayer.cpp index a4c0ce5355..d999ace999 100644 --- a/swssconfig/swssplayer.cpp +++ b/swssconfig/swssplayer.cpp @@ -9,11 +9,8 @@ using namespace std; using namespace swss; -constexpr int DB_PORT = 6379; -constexpr char* DB_HOSTNAME = "localhost"; - static int line_index = 0; -static DBConnector db(APPL_DB, DB_HOSTNAME, DB_PORT, 0); +static DBConnector db("APPL_DB", 0, true); void usage() { diff --git a/teamsyncd/teamsync.cpp b/teamsyncd/teamsync.cpp index a194f5dd9c..64016ff557 100644 --- a/teamsyncd/teamsync.cpp +++ b/teamsyncd/teamsync.cpp @@ -12,6 +12,8 @@ #include "warm_restart.h" #include "teamsync.h" +#include + using namespace std; using namespace std::chrono; using namespace swss; @@ -203,32 +205,54 @@ TeamSync::TeamPortSync::TeamPortSync(const string &lagName, int ifindex, m_lagName(lagName), m_ifindex(ifindex) { - m_team = team_alloc(); - if (!m_team) - { - SWSS_LOG_ERROR("Unable to allocated team socket"); - throw system_error(make_error_code(errc::address_not_available), - "Unable to allocated team socket"); - } - - int err = team_init(m_team, ifindex); - if (err) - { - team_free(m_team); - m_team = NULL; - SWSS_LOG_ERROR("Unable to init team socket"); - throw system_error(make_error_code(errc::address_not_available), - "Unable to init team socket"); - } + int count = 0; + int max_retries = 3; - err = team_change_handler_register(m_team, &gPortChangeHandler, this); - if (err) + while (true) { - team_free(m_team); - m_team = NULL; - SWSS_LOG_ERROR("Unable to register port change event"); - throw system_error(make_error_code(errc::address_not_available), - "Unable to register port change event"); + try + { + m_team = team_alloc(); + if (!m_team) + { + throw system_error(make_error_code(errc::address_not_available), + "Unable to allocate team socket"); + } + + int err = team_init(m_team, ifindex); + if (err) + { + team_free(m_team); + m_team = NULL; + throw system_error(make_error_code(errc::address_not_available), + "Unable to initialize team socket"); + } + + err = team_change_handler_register(m_team, &gPortChangeHandler, this); + if (err) + { + team_free(m_team); + m_team = NULL; + throw system_error(make_error_code(errc::address_not_available), + "Unable to register port change event"); + } + + break; + } + catch (const system_error& e) + { + if (++count == max_retries) + { + throw; + } + else + { + SWSS_LOG_WARN("Failed to initialize team handler. LAG=%s error=%d:%s, attempt=%d", + lagName.c_str(), e.code().value(), e.what(), count); + } + + sleep(1); + } } /* Sync LAG at first */ diff --git a/teamsyncd/teamsyncd.cpp b/teamsyncd/teamsyncd.cpp index faf3e24f57..ef368ca691 100644 --- a/teamsyncd/teamsyncd.cpp +++ b/teamsyncd/teamsyncd.cpp @@ -12,8 +12,8 @@ using namespace swss; int main(int argc, char **argv) { swss::Logger::linkToDbNative(TEAMSYNCD_APP_NAME); - DBConnector db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector db("APPL_DB", 0); + DBConnector stateDb("STATE_DB", 0); Select s; TeamSync sync(&db, &stateDb, &s); diff --git a/tests/conftest.py b/tests/conftest.py index 8fba25d8d8..5e1c496c71 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -232,6 +232,7 @@ def __init__(self, name=None, imgname=None, keeptb=False, fakeplatform=None): self.init_asicdb_validator() self.appldb = ApplDbValidator(self) except: + self.get_logs() self.destroy() raise diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 247a33bf1a..73574f5b62 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -1,3 +1,8 @@ +FLEX_CTR_DIR = $(top_srcdir)/orchagent/flex_counter +DEBUG_CTR_DIR = $(top_srcdir)/orchagent/debug_counter + +INCLUDES = -I $(FLEX_CTR_DIR) -I $(DEBUG_CTR_DIR) + CFLAGS_SAI = -I /usr/include/sai TESTS = tests @@ -54,7 +59,11 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/flexcounterorch.cpp \ $(top_srcdir)/orchagent/watermarkorch.cpp \ $(top_srcdir)/orchagent/chassisorch.cpp \ - $(top_srcdir)/orchagent/sfloworch.cpp + $(top_srcdir)/orchagent/sfloworch.cpp \ + $(top_srcdir)/orchagent/debugcounterorch.cpp + +tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.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) tests_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) -I$(top_srcdir)/orchagent diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index c6541dbded..19d52cafbf 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -98,7 +98,7 @@ namespace aclorch_test AclTest() { - m_config_db = make_shared(CONFIG_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0); + m_config_db = make_shared("CONFIG_DB", 0); } void SetUp() override @@ -235,9 +235,9 @@ namespace aclorch_test AclOrchTest() { // FIXME: move out from constructor - m_app_db = make_shared(APPL_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0); - m_config_db = make_shared(CONFIG_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0); - m_state_db = make_shared(STATE_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0); + m_app_db = make_shared("APPL_DB", 0); + m_config_db = make_shared("CONFIG_DB", 0); + m_state_db = make_shared("STATE_DB", 0); } static map gProfileMap; diff --git a/tests/mock_tests/database_config.json b/tests/mock_tests/database_config.json new file mode 100644 index 0000000000..b86ae11bba --- /dev/null +++ b/tests/mock_tests/database_config.json @@ -0,0 +1,57 @@ +{ + "INSTANCES": { + "redis":{ + "hostname" : "127.0.0.1", + "port" : 6379, + "unix_socket_path" : "/var/run/redis/redis.sock" + } + }, + "DATABASES" : { + "APPL_DB" : { + "id" : 0, + "separator": ":", + "instance" : "redis" + }, + "ASIC_DB" : { + "id" : 1, + "separator": ":", + "instance" : "redis" + }, + "COUNTERS_DB" : { + "id" : 2, + "separator": ":", + "instance" : "redis" + }, + "LOGLEVEL_DB" : { + "id" : 3, + "separator": ":", + "instance" : "redis" + }, + "CONFIG_DB" : { + "id" : 4, + "separator": "|", + "instance" : "redis" + }, + "PFC_WD_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "FLEX_COUNTER_DB" : { + "id" : 5, + "separator": ":", + "instance" : "redis" + }, + "STATE_DB" : { + "id" : 6, + "separator": "|", + "instance" : "redis" + }, + "SNMP_OVERLAY_DB" : { + "id" : 7, + "separator": "|", + "instance" : "redis" + } + }, + "VERSION" : "1.0" +} diff --git a/tests/mock_tests/mock_dbconnector.cpp b/tests/mock_tests/mock_dbconnector.cpp index 6a021e036a..df9b25e179 100644 --- a/tests/mock_tests/mock_dbconnector.cpp +++ b/tests/mock_tests/mock_dbconnector.cpp @@ -38,6 +38,28 @@ namespace swss m_conn->fd = socket(AF_UNIX, SOCK_DGRAM, 0); } + DBConnector::DBConnector(const std::string& dbName, unsigned int timeout, bool isTcpConn) + { + if (swss::SonicDBConfig::isInit() == false) + swss::SonicDBConfig::initialize("./database_config.json"); + m_dbId = swss::SonicDBConfig::getDbId(dbName); + if (isTcpConn) + { + m_conn = (redisContext *)calloc(1, sizeof(redisContext)); + m_conn->connection_type = REDIS_CONN_TCP; + m_conn->tcp.host = strdup(swss::SonicDBConfig::getDbHostname(dbName).c_str()); + m_conn->tcp.port = swss::SonicDBConfig::getDbPort(dbName); + m_conn->fd = socket(AF_UNIX, SOCK_DGRAM, 0); + } + else + { + m_conn = (redisContext *)calloc(1, sizeof(redisContext)); + m_conn->connection_type = REDIS_CONN_UNIX; + m_conn->unix_sock.path = strdup(swss::SonicDBConfig::getDbSock(dbName).c_str()); + m_conn->fd = socket(AF_UNIX, SOCK_DGRAM, 0); + } + } + int DBConnector::getDbId() const { return m_dbId; diff --git a/tests/mock_tests/portsorch_ut.cpp b/tests/mock_tests/portsorch_ut.cpp index 40cfbd3ecc..40b0db6c3b 100644 --- a/tests/mock_tests/portsorch_ut.cpp +++ b/tests/mock_tests/portsorch_ut.cpp @@ -19,11 +19,11 @@ namespace portsorch_test { // FIXME: move out from constructor m_app_db = make_shared( - APPL_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0); + "APPL_DB", 0); m_config_db = make_shared( - CONFIG_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0); + "CONFIG_DB", 0); m_state_db = make_shared( - STATE_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0); + "STATE_DB", 0); } virtual void SetUp() override diff --git a/tests/test_drop_counters.py b/tests/test_drop_counters.py new file mode 100644 index 0000000000..473c20885f --- /dev/null +++ b/tests/test_drop_counters.py @@ -0,0 +1,705 @@ +from swsscommon import swsscommon + +import time + +# Supported drop counters +PORT_INGRESS_DROPS = 'PORT_INGRESS_DROPS' +PORT_EGRESS_DROPS = 'PORT_EGRESS_DROPS' +SWITCH_INGRESS_DROPS = 'SWITCH_INGRESS_DROPS' +SWITCH_EGRESS_DROPS = 'SWITCH_EGRESS_DROPS' + +# Debug Counter Table +DEBUG_COUNTER_TABLE = 'DEBUG_COUNTER' +DROP_REASON_TABLE = 'DEBUG_COUNTER_DROP_REASON' + +# Debug Counter Capability Table +CAPABILITIES_TABLE = 'DEBUG_COUNTER_CAPABILITIES' +CAP_COUNT = 'count' +CAP_REASONS = 'reasons' +SUPPORTED_COUNTER_CAPABILITIES = [CAP_COUNT, CAP_REASONS] +INGRESS_COUNTER_TYPES = [PORT_INGRESS_DROPS, SWITCH_INGRESS_DROPS] +EGRESS_COUNTER_TYPES = [PORT_EGRESS_DROPS, SWITCH_EGRESS_DROPS] +SUPPORTED_COUNTER_TYPES = INGRESS_COUNTER_TYPES + EGRESS_COUNTER_TYPES + +# Debug Counter Flex Counter Group +FLEX_COUNTER_GROUP_TABLE = 'FLEX_COUNTER_GROUP_TABLE' +DEBUG_COUNTER_FLEX_GROUP = 'DEBUG_COUNTER' +FLEX_STATS_MODE_FIELD = 'STATS_MODE' +FLEX_POLL_INTERVAL_FIELD = 'POLL_INTERVAL' +FLEX_STATUS_FIELD = 'FLEX_COUNTER_STATUS' +FLEX_STATUS_ENABLE = 'enable' +EXPECTED_FLEX_STATS_MODE = 'STATS_MODE_READ' +EXPECTED_POLL_INTERVAL_THRESHOLD = 0 +EXPECTED_FLEX_GROUP_FIELDS = [FLEX_STATS_MODE_FIELD, FLEX_POLL_INTERVAL_FIELD, FLEX_STATUS_FIELD] + +# Debug Counter Flex Counters +FLEX_COUNTER_TABLE = 'FLEX_COUNTER_TABLE:DEBUG_COUNTER' +PORT_DEBUG_COUNTER_LIST = 'PORT_DEBUG_COUNTER_ID_LIST' +SWITCH_DEBUG_COUNTER_LIST = 'SWITCH_DEBUG_COUNTER_ID_LIST' +PORT_STAT_BASE = 'SAI_PORT_STAT_IN_DROP_REASON_RANGE_BASE' +PORT_STAT_INDEX_1 = 'SAI_PORT_STAT_IN_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS' +SWITCH_STAT_BASE = 'SAI_SWITCH_STAT_IN_DROP_REASON_RANGE_BASE' +SWITCH_STAT_INDEX_1 = 'SAI_SWITCH_STAT_IN_CONFIGURED_DROP_REASONS_1_DROPPED_PKTS' + +# ASIC DB Fields +ASIC_STATE_TABLE = 'ASIC_STATE:SAI_OBJECT_TYPE_DEBUG_COUNTER' +ASIC_COUNTER_TYPE_FIELD = 'SAI_DEBUG_COUNTER_ATTR_TYPE' +ASIC_COUNTER_INGRESS_REASON_LIST_FIELD = 'SAI_DEBUG_COUNTER_ATTR_IN_DROP_REASON_LIST' +ASIC_COUNTER_EGRESS_REASON_LIST_FIELD = 'SAI_DEBUG_COUNTER_ATTR_OUT_DROP_REASON_LIST' +ASIC_COUNTER_PORT_IN_TYPE = 'SAI_DEBUG_COUNTER_TYPE_PORT_IN_DROP_REASONS' +ASIC_COUNTER_PORT_OUT_TYPE = 'SAI_DEBUG_COUNTER_TYPE_PORT_OUT_DROP_REASONS' +ASIC_COUNTER_SWITCH_IN_TYPE = 'SAI_DEBUG_COUNTER_TYPE_SWITCH_IN_DROP_REASONS' +ASIC_COUNTER_SWITCH_OUT_TYPE = 'SAI_DEBUG_COUNTER_TYPE_SWITCH_OUT_DROP_REASONS' +EXPECTED_ASIC_FIELDS = [ASIC_COUNTER_TYPE_FIELD, ASIC_COUNTER_INGRESS_REASON_LIST_FIELD, ASIC_COUNTER_EGRESS_REASON_LIST_FIELD] +EXPECTED_NUM_ASIC_FIELDS = 2 + +# FIXME: It is really annoying to have to re-run tests due to inconsistent timing, should +# implement some sort of polling interface for checking ASIC/flex counter tables after +# applying changes to config DB +class TestDropCounters(object): + def setup_db(self, dvs): + self.asic_db = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.config_db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + self.flex_db = swsscommon.DBConnector(5, dvs.redis_sock, 0) + self.state_db = swsscommon.DBConnector(6, dvs.redis_sock, 0) + + def genericGetAndAssert(self, table, key): + status, fields = table.get(key) + assert status + return fields + + def checkFlexCounterGroup(self): + flex_group_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_GROUP_TABLE) + status, group_vars = flex_group_table.get(DEBUG_COUNTER_FLEX_GROUP) + assert status + assert len(group_vars) == len(EXPECTED_FLEX_GROUP_FIELDS) + + for var in group_vars: + assert len(var) == 2 + group_field = var[0] + group_contents = var[1] + + assert group_field in EXPECTED_FLEX_GROUP_FIELDS + + if group_field == FLEX_STATS_MODE_FIELD: + assert group_contents == EXPECTED_FLEX_STATS_MODE + elif group_field == FLEX_POLL_INTERVAL_FIELD: + assert int(group_contents) > EXPECTED_POLL_INTERVAL_THRESHOLD + elif group_field == FLEX_STATUS_FIELD: + assert group_contents == FLEX_STATUS_ENABLE + else: + assert False + + def checkFlexState(self, stats, counter_list_type): + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + for oid in flex_counter_table.getKeys(): + attributes = self.genericGetAndAssert(flex_counter_table, oid) + assert len(attributes) == 1 + field, tracked_stats = attributes[0] + assert field == counter_list_type + for stat in stats: + assert stat in tracked_stats + + def checkASICState(self, counter, counter_type, reasons): + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + asic_counter_params = self.genericGetAndAssert(asic_state_table, counter) + if len(asic_counter_params) != EXPECTED_NUM_ASIC_FIELDS: + return False + + for param in asic_counter_params: + param_name = param[0] + param_contents = param[1] + + if param_name not in EXPECTED_ASIC_FIELDS: + return False + + if param_name == ASIC_COUNTER_TYPE_FIELD: + if param_contents != counter_type: + return False + elif param_name == ASIC_COUNTER_INGRESS_REASON_LIST_FIELD: + if int(param_contents[0]) != len(reasons): + return False + + for reason in reasons: + if reason not in param_contents: + return False + else: + return False + + return True + + def create_drop_counter(self, name, counter_type): + debug_counter_table = swsscommon.Table(self.config_db, DEBUG_COUNTER_TABLE) + counter_metadata = swsscommon.FieldValuePairs([('type', counter_type)]) + debug_counter_table.set(name, counter_metadata) + + def add_drop_reason(self, name, drop_reason): + drop_reason_table = swsscommon.Table(self.config_db, '{}|{}'.format(DROP_REASON_TABLE, name)) + drop_reason_entry = swsscommon.FieldValuePairs([('NULL', 'NULL')]) + drop_reason_table.set(drop_reason, drop_reason_entry) + + def remove_drop_reason(self, name, drop_reason): + drop_reason_table = swsscommon.Table(self.config_db, '{}|{}'.format(DROP_REASON_TABLE, name)) + drop_reason_table._del(drop_reason) + + def delete_drop_counter(self, name): + debug_counter_table = swsscommon.Table(self.config_db, DEBUG_COUNTER_TABLE) + debug_counter_table._del(name) + + def test_deviceCapabilitiesTablePopulated(self, dvs, testlog): + """ + This test verifies that DebugCounterOrch has succesfully queried + the capabilities for this device and populated state DB with the + correct values. + """ + self.setup_db(dvs) + + # Check that the capabilities table 1) exists and 2) has been populated + # for each type of counter + capabilities_table = swsscommon.Table(self.state_db, CAPABILITIES_TABLE) + counter_types = capabilities_table.getKeys() + assert len(counter_types) == len(SUPPORTED_COUNTER_TYPES) + + # Check that the data for each counter type is consistent + for counter_type in SUPPORTED_COUNTER_TYPES: + assert counter_type in counter_types + + # By definiton, each capability entry should contain exactly the same fields + counter_capabilities = self.genericGetAndAssert(capabilities_table, counter_type) + assert len(counter_capabilities) == len(SUPPORTED_COUNTER_CAPABILITIES) + + # Check that the values of each field actually match the + # capabilities currently defined in the virtual switch + for capability in counter_capabilities: + assert len(capability) == 2 + capability_name = capability[0] + capability_contents = capability[1] + + assert capability_name in SUPPORTED_COUNTER_CAPABILITIES + + if capability_name == CAP_COUNT: + assert int(capability_contents) == 3 + elif capability_name == CAP_REASONS and counter_type in INGRESS_COUNTER_TYPES: + assert len(capability_contents.split(',')) == 3 + elif capability_name == CAP_REASONS and counter_type in EGRESS_COUNTER_TYPES: + assert len(capability_contents.split(',')) == 2 + else: + assert False + + def test_flexCounterGroupInitialized(self, dvs, testlog): + """ + This test verifies that DebugCounterOrch has succesfully + setup a flex counter group for the drop counters. + """ + self.setup_db(dvs) + self.checkFlexCounterGroup() + + def test_createAndRemoveDropCounterBasic(self, dvs, testlog): + """ + This test verifies that a drop counter can succesfully be + created and deleted. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'TEST' + reason = 'L3_ANY' + + self.create_drop_counter(name, PORT_INGRESS_DROPS) + + # Because no reasons have been added to the counter yet, nothing should + # be put in ASIC DB and the flex counters should not start polling yet. + assert len(asic_state_table.getKeys()) == 0 + assert len(flex_counter_table.getKeys()) == 0 + + self.add_drop_reason(name, reason) + time.sleep(3) + + # Verify that the flex counters have been created to poll the new + # counter. + self.checkFlexState([PORT_STAT_BASE], PORT_DEBUG_COUNTER_LIST) + + # Verify that the drop counter has been added to ASIC DB with the + # correct reason added. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_PORT_IN_TYPE, [reason]) + + self.delete_drop_counter(name) + time.sleep(3) + + # Verify that the counter has been removed from ASIC DB and the flex + # counters have been torn down. + assert len(asic_state_table.getKeys()) == 0 + assert len(flex_counter_table.getKeys()) == 0 + + # Cleanup for the next test. + self.remove_drop_reason(name, reason) + + def test_createAndRemoveDropCounterReversed(self, dvs, testlog): + """ + This test verifies that a drop counter can succesfully be created + and deleted when the drop reasons are added before the counter is + created. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'TEST' + reason = 'L3_ANY' + + self.add_drop_reason(name, reason) + + # Because the actual counter has not been created yet, nothing should + # be put in ASIC DB and the flex counters should not start polling yet. + assert len(asic_state_table.getKeys()) == 0 + assert len(flex_counter_table.getKeys()) == 0 + + self.create_drop_counter(name, PORT_INGRESS_DROPS) + time.sleep(3) + + # Verify that the flex counters have been created to poll the new + # counter. + self.checkFlexState([PORT_STAT_BASE], PORT_DEBUG_COUNTER_LIST) + + # Verify that the drop counter has been added to ASIC DB with the + # correct reason added. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_PORT_IN_TYPE, [reason]) + + self.delete_drop_counter(name) + time.sleep(3) + + # Verify that the counter has been removed from ASIC DB and the flex + # counters have been torn down. + assert len(asic_state_table.getKeys()) == 0 + assert len(flex_counter_table.getKeys()) == 0 + + # Cleanup for the next test. + self.remove_drop_reason(name, reason) + + def test_createCounterWithInvalidCounterType(self, dvs, testlog): + """ + This test verifies that the state of the system is unaffected + when an invalid counter type is passed to CONFIG DB. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'BAD_CTR' + reason = 'L3_ANY' + + self.create_drop_counter(name, 'THIS_IS_DEFINITELY_NOT_A_VALID_COUNTER_TYPE') + self.add_drop_reason(name, reason) + time.sleep(3) + + # Verify that nothing has been added to ASIC DB and no flex counters + # were created. + assert len(asic_state_table.getKeys()) == 0 + assert len(flex_counter_table.getKeys()) == 0 + + # Cleanup for the next test. + self.delete_drop_counter(name) + self.remove_drop_reason(name, reason) + + def test_createCounterWithInvalidDropReason(self, dvs, testlog): + """ + This test verifies that the state of the system is unaffected + when an invalid drop reason is passed to CONFIG DB. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'BAD_CTR' + reason = 'THIS_IS_DEFINITELY_NOT_A_VALID_DROP_REASON' + + self.create_drop_counter(name, SWITCH_INGRESS_DROPS) + self.add_drop_reason(name, reason) + time.sleep(3) + + # Verify that nothing has been added to ASIC DB and no flex counters + # were created. + assert len(asic_state_table.getKeys()) == 0 + assert len(flex_counter_table.getKeys()) == 0 + + # Cleanup for the next test. + self.delete_drop_counter(name) + self.remove_drop_reason(name, reason) + + def test_addReasonToInitializedCounter(self, dvs, testlog): + """ + This test verifies that a drop reason can be added to a counter + that has already been initialized. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'ADD_TEST' + reason1 = 'L3_ANY' + + self.create_drop_counter(name, SWITCH_INGRESS_DROPS) + self.add_drop_reason(name, reason1) + time.sleep(3) + + # Verify that a counter has been created. We will verify the state of + # the counter in the next step. + assert len(asic_state_table.getKeys()) == 1 + self.checkFlexState([SWITCH_STAT_BASE], SWITCH_DEBUG_COUNTER_LIST) + + reason2 = 'ACL_ANY' + self.add_drop_reason(name, reason2) + time.sleep(3) + + # Verify that the drop counter has been added to ASIC DB, including the + # reason that was added. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1, reason2]) + + # Cleanup for the next test. + self.delete_drop_counter(name) + self.remove_drop_reason(name, reason1) + self.remove_drop_reason(name, reason2) + + def test_removeReasonFromInitializedCounter(self, dvs, testlog): + """ + This test verifies that a drop reason can be removed from a counter + that has already been initialized without deleting the counter. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'ADD_TEST' + reason1 = 'L3_ANY' + reason2 = 'ACL_ANY' + + self.create_drop_counter(name, SWITCH_INGRESS_DROPS) + self.add_drop_reason(name, reason1) + self.add_drop_reason(name, reason2) + time.sleep(3) + + # Verify that a counter has been created. We will verify the state of + # the counter in the next step. + assert len(asic_state_table.getKeys()) == 1 + self.checkFlexState([SWITCH_STAT_BASE], SWITCH_DEBUG_COUNTER_LIST) + + self.remove_drop_reason(name, reason2) + time.sleep(3) + + # Verify that the drop counter has been added to ASIC DB, excluding the + # reason that was removed. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1]) + + # Cleanup for the next test. + self.delete_drop_counter(name) + self.remove_drop_reason(name, reason1) + + def test_removeAllDropReasons(self, dvs, testlog): + """ + This test verifies that it is not possible to remove all drop + reasons from a drop counter. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'ADD_TEST' + reason1 = 'L3_ANY' + + self.create_drop_counter(name, SWITCH_INGRESS_DROPS) + self.add_drop_reason(name, reason1) + time.sleep(3) + + # Verify that a counter has been created. We will verify the state of + # the counter in the next step. + assert len(asic_state_table.getKeys()) == 1 + self.checkFlexState([SWITCH_STAT_BASE], SWITCH_DEBUG_COUNTER_LIST) + + self.remove_drop_reason(name, reason1) + time.sleep(3) + + # Verify that the drop counter has been added to ASIC DB, including the + # last reason that we attempted to remove. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1]) + + # Cleanup for the next test. + self.delete_drop_counter(name) + + def test_addDropReasonMultipleTimes(self, dvs, testlog): + """ + This test verifies that the same drop reason can be added multiple + times without affecting the system. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'ADD_TEST' + reason1 = 'L3_ANY' + + self.create_drop_counter(name, SWITCH_INGRESS_DROPS) + self.add_drop_reason(name, reason1) + time.sleep(3) + + # Verify that a counter has been created. We will verify the state of + # the counter in the next step. + assert len(asic_state_table.getKeys()) == 1 + self.checkFlexState([SWITCH_STAT_BASE], SWITCH_DEBUG_COUNTER_LIST) + + reason2 = 'ACL_ANY' + self.add_drop_reason(name, reason2) + time.sleep(3) + + # Verify that the drop counter has been added to ASIC DB, including the + # reason that was added. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1, reason2]) + + self.add_drop_reason(name, reason2) + time.sleep(3) + + # Verify that the ASIC state is the same as before adding the redundant + # drop reason. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1, reason2]) + + # Cleanup for the next test. + self.delete_drop_counter(name) + self.remove_drop_reason(name, reason1) + self.remove_drop_reason(name, reason2) + + def test_addInvalidDropReason(self, dvs, testlog): + """ + This test verifies that adding a drop reason to a counter that is + not recognized will not affect the system. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'ADD_TEST' + reason1 = 'L3_ANY' + + self.create_drop_counter(name, SWITCH_INGRESS_DROPS) + self.add_drop_reason(name, reason1) + time.sleep(3) + + # Verify that a counter has been created. We will verify the state of + # the counter in the next step. + assert len(asic_state_table.getKeys()) == 1 + self.checkFlexState([SWITCH_STAT_BASE], SWITCH_DEBUG_COUNTER_LIST) + + reason2 = 'ACL_ANY' + self.add_drop_reason(name, reason2) + time.sleep(3) + + # Verify that the drop counter has been added to ASIC DB, including the + # reason that was added. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1, reason2]) + + dummy_reason = 'ZOBOOMBAFOO' + self.add_drop_reason(name, dummy_reason) + time.sleep(3) + + # Verify that the ASIC state is the same as before adding the invalid + # drop reason. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1, reason2]) + + # Cleanup for the next test. + self.delete_drop_counter(name) + self.remove_drop_reason(name, reason1) + self.remove_drop_reason(name, reason2) + + def test_removeDropReasonMultipleTimes(self, dvs, testlog): + """ + This test verifies that removing a drop reason multiple times will + not affect the system. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'ADD_TEST' + reason1 = 'L3_ANY' + reason2 = 'ACL_ANY' + + self.create_drop_counter(name, SWITCH_INGRESS_DROPS) + self.add_drop_reason(name, reason1) + self.add_drop_reason(name, reason2) + time.sleep(3) + + # Verify that a counter has been created. We will verify the state of + # the counter in the next step. + assert len(asic_state_table.getKeys()) == 1 + self.checkFlexState([SWITCH_STAT_BASE], SWITCH_DEBUG_COUNTER_LIST) + + self.remove_drop_reason(name, reason2) + time.sleep(3) + + # Verify that the drop counter has been added to ASIC DB, excluding the + # reason that was removed. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1]) + + self.remove_drop_reason(name, reason2) + time.sleep(3) + + # Verify that the ASIC state is the same as before the redundant + # remove operation. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1]) + + # Cleanup for the next test. + self.delete_drop_counter(name) + self.remove_drop_reason(name, reason1) + + def test_removeNonexistentDropReason(self, dvs, testlog): + """ + This test verifies that removing a drop reason that does not exist + on the device will not affect the system. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'ADD_TEST' + reason1 = 'L3_ANY' + reason2 = 'ACL_ANY' + + self.create_drop_counter(name, SWITCH_INGRESS_DROPS) + self.add_drop_reason(name, reason1) + time.sleep(3) + + # Verify the counter has been created and is in the correct state. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1]) + + self.remove_drop_reason(name, reason2) + time.sleep(3) + + # Verify that the ASIC state is unchanged after the nonexistent remove. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1]) + + # Cleanup for the next test. + self.delete_drop_counter(name) + self.remove_drop_reason(name, reason1) + + def test_removeInvalidDropReason(self, dvs, testlog): + """ + This test verifies that removing a drop reason that is not recognized + will not affect the system. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name = 'ADD_TEST' + reason1 = 'L3_ANY' + bogus_reason = 'LIVE_LAUGH_LOVE' + + self.create_drop_counter(name, SWITCH_INGRESS_DROPS) + self.add_drop_reason(name, reason1) + time.sleep(3) + + # Verify the counter has been created and is in the correct state. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1]) + + self.remove_drop_reason(name, bogus_reason) + time.sleep(3) + + # Verify that the ASIC state is unchanged after the bad remove. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_SWITCH_IN_TYPE, [reason1]) + + # Cleanup for the next test. + self.delete_drop_counter(name) + self.remove_drop_reason(name, reason1) + + def test_createAndDeleteMultipleCounters(self, dvs, testlog): + """ + This test verifies that creating and deleting multiple drop counters + at the same time works correctly. + """ + self.setup_db(dvs) + + asic_state_table = swsscommon.Table(self.asic_db, ASIC_STATE_TABLE) + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + + name1 = 'DEBUG_0' + reason1 = 'L3_ANY' + + name2 = 'DEBUG_1' + reason2 = 'ACL_ANY' + + self.create_drop_counter(name1, PORT_INGRESS_DROPS) + self.add_drop_reason(name1, reason1) + + self.create_drop_counter(name2, PORT_INGRESS_DROPS) + self.add_drop_reason(name2, reason2) + + time.sleep(5) + + # Verify that the flex counters are correctly tracking two different + # drop counters. + self.checkFlexState([PORT_STAT_BASE, PORT_STAT_INDEX_1], PORT_DEBUG_COUNTER_LIST) + + # Verify that there are two entries in the ASIC DB, one for each counter. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 2 + for key in asic_keys: + assert (self.checkASICState(key, ASIC_COUNTER_PORT_IN_TYPE, [reason1]) or self.checkASICState(key, ASIC_COUNTER_PORT_IN_TYPE, [reason2])) + + self.delete_drop_counter(name2) + self.remove_drop_reason(name2, reason2) + time.sleep(3) + + # Verify that the flex counters are tracking ONE drop counter after + # the update. + self.checkFlexState([PORT_STAT_BASE], PORT_DEBUG_COUNTER_LIST) + + # Verify that there is ONE entry in the ASIC DB after the update. + asic_keys = asic_state_table.getKeys() + assert len(asic_keys) == 1 + assert self.checkASICState(asic_keys[0], ASIC_COUNTER_PORT_IN_TYPE, [reason1]) + + # Cleanup for the next test. + self.delete_drop_counter(name1) + self.remove_drop_reason(name1, reason1)