From 53620f396dc1d0a8700532924742baa4e824580e Mon Sep 17 00:00:00 2001 From: tomer-israel <76040066+tomer-israel@users.noreply.github.com> Date: Thu, 7 Apr 2022 16:01:09 +0300 Subject: [PATCH] [orchagent] add & remove port counters dynamically each time port was added or removed (#2019) - What I did Add support for adding & removing port counters dynamically each time port was added or removed the counters that were supported are: 1. pg watermark counters 2. pg drop counters 3. queue stat counters 4. queue watermark counters 5. debug counters 6. buffer drop counters and port stat counters are already supported to be added or removed each time port is added/removed Implemented according to the - 'HLD document on add/remove ports dynamically feature' https://github.com/Azure/SONiC/pull/900 - Why I did it In order to support dynamically add or removed ports on sonic - How I verified it tested manually that the flex counters were added or removed correctly whenever we add or remove ports added new test cases to the following vs tests: test_flex_counters.py test_drop_counters.py test_pg_drop_counter.py Co-authored-by: dprital --- orchagent/debugcounterorch.cpp | 74 +++++++++++++++++++++++- orchagent/debugcounterorch.h | 14 +++-- orchagent/orchdaemon.cpp | 5 +- orchagent/portsorch.cpp | 101 +++++++++++++++++++++++++++++++-- orchagent/portsorch.h | 2 + tests/conftest.py | 7 ++- tests/dvslib/dvs_database.py | 11 ++++ tests/dvslib/dvs_port.py | 20 +++++++ tests/test_drop_counters.py | 80 +++++++++++++++++++++++++- tests/test_flex_counters.py | 65 ++++++++++++++++++++- tests/test_pg_drop_counter.py | 52 ++++++++++++++++- 11 files changed, 413 insertions(+), 18 deletions(-) create mode 100644 tests/dvslib/dvs_port.py diff --git a/orchagent/debugcounterorch.cpp b/orchagent/debugcounterorch.cpp index 25d0b94589b1..ed27f400d434 100644 --- a/orchagent/debugcounterorch.cpp +++ b/orchagent/debugcounterorch.cpp @@ -5,6 +5,7 @@ #include "schema.h" #include "drop_counter.h" #include +#include "observer.h" using std::string; using std::unordered_map; @@ -34,6 +35,8 @@ DebugCounterOrch::DebugCounterOrch(DBConnector *db, const vector& table_ { SWSS_LOG_ENTER(); publishDropCounterCapabilities(); + + gPortsOrch->attach(this); } DebugCounterOrch::~DebugCounterOrch(void) @@ -41,6 +44,52 @@ DebugCounterOrch::~DebugCounterOrch(void) SWSS_LOG_ENTER(); } +void DebugCounterOrch::update(SubjectType type, void *cntx) +{ + SWSS_LOG_ENTER(); + + if (type == SUBJECT_TYPE_PORT_CHANGE) + { + if (!cntx) + { + SWSS_LOG_ERROR("cntx is NULL"); + return; + } + + PortUpdate *update = static_cast(cntx); + Port &port = update->port; + + if (update->add) + { + for (const auto& debug_counter: debug_counters) + { + DebugCounter *counter = debug_counter.second.get(); + auto counter_type = counter->getCounterType(); + auto counter_stat = counter->getDebugCounterSAIStat(); + auto flex_counter_type = getFlexCounterType(counter_type); + if (flex_counter_type == CounterType::PORT_DEBUG) + { + installDebugFlexCounters(counter_type, counter_stat, port.m_port_id); + } + } + } + else + { + for (const auto& debug_counter: debug_counters) + { + DebugCounter *counter = debug_counter.second.get(); + auto counter_type = counter->getCounterType(); + auto counter_stat = counter->getDebugCounterSAIStat(); + auto flex_counter_type = getFlexCounterType(counter_type); + if (flex_counter_type == CounterType::PORT_DEBUG) + { + uninstallDebugFlexCounters(counter_type, counter_stat, port.m_port_id); + } + } + } + } +} + // doTask processes updates from the consumer and modifies the state of the // following components: // 1) The ASIC, by creating, modifying, and deleting debug counters @@ -476,7 +525,8 @@ CounterType DebugCounterOrch::getFlexCounterType(const string& counter_type) } void DebugCounterOrch::installDebugFlexCounters(const string& counter_type, - const string& counter_stat) + const string& counter_stat, + sai_object_id_t port_id) { SWSS_LOG_ENTER(); CounterType flex_counter_type = getFlexCounterType(counter_type); @@ -489,6 +539,14 @@ void DebugCounterOrch::installDebugFlexCounters(const string& counter_type, { for (auto const &curr : gPortsOrch->getAllPorts()) { + if (port_id != SAI_NULL_OBJECT_ID) + { + if (curr.second.m_port_id != port_id) + { + continue; + } + } + if (curr.second.m_type != Port::Type::PHY) { continue; @@ -503,7 +561,8 @@ void DebugCounterOrch::installDebugFlexCounters(const string& counter_type, } void DebugCounterOrch::uninstallDebugFlexCounters(const string& counter_type, - const string& counter_stat) + const string& counter_stat, + sai_object_id_t port_id) { SWSS_LOG_ENTER(); CounterType flex_counter_type = getFlexCounterType(counter_type); @@ -516,6 +575,14 @@ void DebugCounterOrch::uninstallDebugFlexCounters(const string& counter_type, { for (auto const &curr : gPortsOrch->getAllPorts()) { + if (port_id != SAI_NULL_OBJECT_ID) + { + if (curr.second.m_port_id != port_id) + { + continue; + } + } + if (curr.second.m_type != Port::Type::PHY) { continue; @@ -616,3 +683,6 @@ bool DebugCounterOrch::isDropReasonValid(const string& drop_reason) const return true; } + + + diff --git a/orchagent/debugcounterorch.h b/orchagent/debugcounterorch.h index e5b512c8e46b..edfb5d98e043 100644 --- a/orchagent/debugcounterorch.h +++ b/orchagent/debugcounterorch.h @@ -10,6 +10,7 @@ #include "flex_counter_stat_manager.h" #include "debug_counter.h" #include "drop_counter.h" +#include "observer.h" extern "C" { #include "sai.h" @@ -17,9 +18,11 @@ extern "C" { #define DEBUG_COUNTER_FLEX_COUNTER_GROUP "DEBUG_COUNTER" +using DebugCounterMap = std::unordered_map>; + // DebugCounterOrch is an orchestrator for managing debug counters. It handles // the creation, deletion, and modification of debug counters. -class DebugCounterOrch: public Orch +class DebugCounterOrch: public Orch, public Observer { public: DebugCounterOrch(swss::DBConnector *db, const std::vector& table_names, int poll_interval); @@ -27,6 +30,7 @@ class DebugCounterOrch: public Orch void doTask(Consumer& consumer); + void update(SubjectType, void *cntx); private: // Debug Capability Reporting Functions void publishDropCounterCapabilities(); @@ -48,10 +52,12 @@ class DebugCounterOrch: public Orch CounterType getFlexCounterType(const std::string& counter_type) noexcept(false); void installDebugFlexCounters( const std::string& counter_type, - const std::string& counter_stat); + const std::string& counter_stat, + sai_object_id_t port_id = SAI_NULL_OBJECT_ID); void uninstallDebugFlexCounters( const std::string& counter_type, - const std::string& counter_stat); + const std::string& counter_stat, + sai_object_id_t port_id = SAI_NULL_OBJECT_ID); // Debug Counter Initialization Helper Functions std::string getDebugCounterType( @@ -83,7 +89,7 @@ class DebugCounterOrch: public Orch FlexCounterStatManager flex_counter_manager; - std::unordered_map> debug_counters; + DebugCounterMap 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 diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index d4fe4828b4f9..a6563d45caea 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -52,6 +52,7 @@ CoppOrch *gCoppOrch; P4Orch *gP4Orch; BfdOrch *gBfdOrch; Srv6Orch *gSrv6Orch; +DebugCounterOrch *gDebugCounterOrch; bool gIsNatSupported = false; @@ -282,7 +283,7 @@ bool OrchDaemon::init() CFG_DEBUG_COUNTER_DROP_REASON_TABLE_NAME }; - DebugCounterOrch *debug_counter_orch = new DebugCounterOrch(m_configDb, debug_counter_tables, 1000); + gDebugCounterOrch = new DebugCounterOrch(m_configDb, debug_counter_tables, 1000); const int natorch_base_pri = 50; @@ -330,7 +331,7 @@ bool OrchDaemon::init() * when iterating ConsumerMap. This is ensured implicitly by the order of keys in ordered map. * For cases when Orch has to process tables in specific order, like PortsOrch during warm start, it has to override Orch::doTask() */ - m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, policer_orch, tunnel_decap_orch, sflow_orch, debug_counter_orch, gMacsecOrch, gBfdOrch, gSrv6Orch}; + m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, policer_orch, tunnel_decap_orch, sflow_orch, gDebugCounterOrch, gMacsecOrch, gBfdOrch, gSrv6Orch}; bool initialize_dtel = false; if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 1434f98379f5..d394b53f4661 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -2378,6 +2378,18 @@ bool PortsOrch::initPort(const string &alias, const string &role, const int inde port_buffer_drop_stat_manager.setCounterIdList(p.m_port_id, CounterType::PORT, port_buffer_drop_stats); } + /* when a port is added and priority group map counter is enabled --> we need to add pg counter for it */ + if (m_isPriorityGroupMapGenerated) + { + generatePriorityGroupMapPerPort(p); + } + + /* when a port is added and queue map counter is enabled --> we need to add queue map counter for it */ + if (m_isQueueMapGenerated) + { + generateQueueMapPerPort(p); + } + PortUpdate update = { p, true }; notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); @@ -2410,8 +2422,13 @@ void PortsOrch::deInitPort(string alias, sai_object_id_t port_id) { SWSS_LOG_ENTER(); - Port p(alias, Port::PHY); - p.m_port_id = port_id; + Port p; + + if (!getPort(port_id, p)) + { + SWSS_LOG_ERROR("Failed to get port object for port id 0x%" PRIx64, port_id); + return; + } /* remove port from flex_counter_table for updating counters */ auto flex_counters_orch = gDirectory.get(); @@ -2425,9 +2442,20 @@ void PortsOrch::deInitPort(string alias, sai_object_id_t port_id) port_buffer_drop_stat_manager.clearCounterIdList(p.m_port_id); } + /* remove pg port counters */ + if (m_isPriorityGroupMapGenerated) + { + removePriorityGroupMapPerPort(p); + } + + /* remove queue port counters */ + if (m_isQueueMapGenerated) + { + removeQueueMapPerPort(p); + } /* remove port name map from counter table */ - m_counter_db->hdel(COUNTERS_PORT_NAME_MAP, alias); + m_counterTable->hdel("", alias); /* Remove the associated port serdes attribute */ removePortSerdesAttribute(p.m_port_id); @@ -2436,7 +2464,6 @@ void PortsOrch::deInitPort(string alias, sai_object_id_t port_id) SWSS_LOG_NOTICE("De-Initialized port %s", alias.c_str()); } - bool PortsOrch::bake() { SWSS_LOG_ENTER(); @@ -5409,6 +5436,44 @@ void PortsOrch::generateQueueMap() m_isQueueMapGenerated = true; } +void PortsOrch::removeQueueMapPerPort(const Port& port) +{ + /* Remove the Queue map in the Counter DB */ + + for (size_t queueIndex = 0; queueIndex < port.m_queue_ids.size(); ++queueIndex) + { + std::ostringstream name; + name << port.m_alias << ":" << queueIndex; + std::unordered_set counter_stats; + + const auto id = sai_serialize_object_id(port.m_queue_ids[queueIndex]); + + m_queueTable->hdel("",name.str()); + m_queuePortTable->hdel("",id); + + string queueType; + uint8_t queueRealIndex = 0; + if (getQueueTypeAndIndex(port.m_queue_ids[queueIndex], queueType, queueRealIndex)) + { + m_queueTypeTable->hdel("",id); + m_queueIndexTable->hdel("",id); + } + + for (const auto& it: queue_stat_ids) + { + counter_stats.emplace(sai_serialize_queue_stat(it)); + } + queue_stat_manager.clearCounterIdList(port.m_queue_ids[queueIndex]); + + /* remove watermark queue counters */ + string key = getQueueWatermarkFlexCounterTableKey(id); + + m_flexCounterTable->del(key); + } + + CounterCheckOrch::getInstance().removePort(port); +} + void PortsOrch::generateQueueMapPerPort(const Port& port) { /* Create the Queue map in the Counter DB */ @@ -5487,6 +5552,32 @@ void PortsOrch::generatePriorityGroupMap() m_isPriorityGroupMapGenerated = true; } +void PortsOrch::removePriorityGroupMapPerPort(const Port& port) +{ + /* Remove the PG map in the Counter DB */ + + for (size_t pgIndex = 0; pgIndex < port.m_priority_group_ids.size(); ++pgIndex) + { + std::ostringstream name; + name << port.m_alias << ":" << pgIndex; + + const auto id = sai_serialize_object_id(port.m_priority_group_ids[pgIndex]); + string key = getPriorityGroupWatermarkFlexCounterTableKey(id); + + m_pgTable->hdel("",name.str()); + m_pgPortTable->hdel("",id); + m_pgIndexTable->hdel("",id); + + m_flexCounterTable->del(key); + + key = getPriorityGroupDropPacketsFlexCounterTableKey(id); + /* remove dropped packets counters to flex_counter */ + m_flexCounterTable->del(key); + } + + CounterCheckOrch::getInstance().removePort(port); +} + void PortsOrch::generatePriorityGroupMapPerPort(const Port& port) { /* Create the PG map in the Counter DB */ @@ -5644,7 +5735,7 @@ void PortsOrch::doTask(NotificationConsumer &consumer) updateDbPortOperSpeed(port, 0); } } - + /* update m_portList */ m_portList[port.m_alias] = port; } diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 843ffbd7f1cf..bdfcf47ad04f 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -312,9 +312,11 @@ class PortsOrch : public Orch, public Subject bool m_isQueueMapGenerated = false; void generateQueueMapPerPort(const Port& port); + void removeQueueMapPerPort(const Port& port); bool m_isPriorityGroupMapGenerated = false; void generatePriorityGroupMapPerPort(const Port& port); + void removePriorityGroupMapPerPort(const Port& port); bool m_isPortCounterMapGenerated = false; bool m_isPortBufferDropCounterMapGenerated = false; diff --git a/tests/conftest.py b/tests/conftest.py index f1e8248a1441..39706295afdb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,6 +22,7 @@ from dvslib.dvs_pbh import DVSPbh from dvslib.dvs_route import DVSRoute from dvslib import dvs_vlan +from dvslib import dvs_port from dvslib import dvs_lag from dvslib import dvs_mirror from dvslib import dvs_policer @@ -1765,7 +1766,11 @@ def dvs_vlan_manager(request, dvs): dvs.get_counters_db(), dvs.get_app_db()) - +@pytest.yield_fixture(scope="class") +def dvs_port_manager(request, dvs): + request.cls.dvs_port = dvs_port.DVSPort(dvs.get_asic_db(), + dvs.get_config_db()) + @pytest.yield_fixture(scope="class") def dvs_mirror_manager(request, dvs): request.cls.dvs_mirror = dvs_mirror.DVSMirror(dvs.get_asic_db(), diff --git a/tests/dvslib/dvs_database.py b/tests/dvslib/dvs_database.py index 4256b5802d29..371b7f61e9b9 100644 --- a/tests/dvslib/dvs_database.py +++ b/tests/dvslib/dvs_database.py @@ -92,6 +92,17 @@ def delete_entry(self, table_name: str, key: str) -> None: table = swsscommon.Table(self.db_connection, table_name) table._del(key) # pylint: disable=protected-access + def delete_field(self, table_name: str, key: str, field: str) -> None: + """Remove a field from an entry stored at `key` in the specified table. + + Args: + table_name: The name of the table where the entry is being removed. + key: The key that maps to the entry being removed. + field: The field that needs to be removed + """ + table = swsscommon.Table(self.db_connection, table_name) + table.hdel(key, field) + def get_keys(self, table_name: str) -> List[str]: """Get all of the keys stored in the specified table. diff --git a/tests/dvslib/dvs_port.py b/tests/dvslib/dvs_port.py new file mode 100644 index 000000000000..8c539942422a --- /dev/null +++ b/tests/dvslib/dvs_port.py @@ -0,0 +1,20 @@ + +class DVSPort(object): + def __init__(self, adb, cdb): + self.asic_db = adb + self.config_db = cdb + + def remove_port(self, port_name): + self.config_db.delete_field("CABLE_LENGTH", "AZURE", port_name) + + port_bufferpg_keys = self.config_db.get_keys("BUFFER_PG|%s" % port_name) + for key in port_bufferpg_keys: + self.config_db.delete_entry("BUFFER_PG|%s|%s" % (port_name, key), "") + + port_bufferqueue_keys = self.config_db.get_keys("BUFFER_QUEUE|%s" % port_name) + for key in port_bufferqueue_keys: + self.config_db.delete_entry("BUFFER_QUEUE|%s|%s" % (port_name, key), "") + + self.config_db.delete_entry("BREAKOUT_CFG|%s" % port_name, "") + self.config_db.delete_entry("INTERFACE|%s" % port_name, "") + self.config_db.delete_entry("PORT", port_name) diff --git a/tests/test_drop_counters.py b/tests/test_drop_counters.py index b003876f1ad3..cd089be9170c 100644 --- a/tests/test_drop_counters.py +++ b/tests/test_drop_counters.py @@ -11,7 +11,7 @@ # Debug Counter Table DEBUG_COUNTER_TABLE = 'DEBUG_COUNTER' -DROP_REASON_TABLE = 'DEBUG_COUNTER_DROP_REASON' +DROP_REASON_TABLE = 'DEBUG_COUNTER_DROP_REASON' # Debug Counter Capability Table CAPABILITIES_TABLE = 'DEBUG_COUNTER_CAPABILITIES' @@ -54,7 +54,11 @@ EXPECTED_ASIC_FIELDS = [ASIC_COUNTER_TYPE_FIELD, ASIC_COUNTER_INGRESS_REASON_LIST_FIELD, ASIC_COUNTER_EGRESS_REASON_LIST_FIELD] EXPECTED_NUM_ASIC_FIELDS = 2 +# port to be add and removed +PORT = "Ethernet0" +PORT_TABLE_NAME = "PORT" +@pytest.mark.usefixtures('dvs_port_manager') # 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 @@ -64,6 +68,7 @@ def setup_db(self, dvs): 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) + self.counters_db = swsscommon.DBConnector(2, dvs.redis_sock, 0) def genericGetAndAssert(self, table, key): status, fields = table.get(key) @@ -654,6 +659,79 @@ def test_removeInvalidDropReason(self, dvs, testlog): # Cleanup for the next test. self.delete_drop_counter(name) self.remove_drop_reason(name, reason1) + + def getPortOid(self, dvs, port_name): + port_name_map = swsscommon.Table(self.counters_db, "COUNTERS_PORT_NAME_MAP") + status, returned_value = port_name_map.hget("", port_name); + assert status == True + return returned_value + + def test_add_remove_port(self, dvs, testlog): + """ + This test verifies that debug counters are removed when we remove a port + and debug counters are added each time we add ports (if debug counter is enabled) + """ + self.setup_db(dvs) + + # save port info + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + tbl = swsscommon.Table(cdb, PORT_TABLE_NAME) + (status, fvs) = tbl.get(PORT) + assert status == True + + # get counter oid + oid = self.getPortOid(dvs, PORT) + + # verifies debug coutner dont exist for port + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + status, fields = flex_counter_table.get(oid) + assert len(fields) == 0 + + # add debug counters + name1 = 'DEBUG_0' + reason1 = 'L3_ANY' + name2 = 'DEBUG_1' + reason2 = 'L2_ANY' + + self.create_drop_counter(name1, PORT_INGRESS_DROPS) + self.add_drop_reason(name1, reason1) + + self.create_drop_counter(name2, PORT_EGRESS_DROPS) + self.add_drop_reason(name2, reason2) + time.sleep(3) + + # verifies debug counter exist for port + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + status, fields = flex_counter_table.get(oid) + assert status == True + assert len(fields) == 1 + + # remove port and wait until it was removed from ASIC DB + self.dvs_port.remove_port(PORT) + dvs.get_asic_db().wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_PORT", oid) + + # verify that debug counter were removed + status, fields = flex_counter_table.get(oid) + assert len(fields) == 0 + + # add port and wait until the port is added on asic db + num_of_keys_without_port = len(dvs.get_asic_db().get_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT")) + tbl.set(PORT, fvs) + dvs.get_asic_db().wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT", num_of_keys_without_port + 1) + dvs.get_counters_db().wait_for_fields("COUNTERS_PORT_NAME_MAP", "", [PORT]) + + # verifies that debug counters were added for port + oid = self.getPortOid(dvs, PORT) + status, fields = flex_counter_table.get(oid) + assert status == True + assert len(fields) == 1 + + # Cleanup for the next test. + self.delete_drop_counter(name1) + self.remove_drop_reason(name1, reason1) + + self.delete_drop_counter(name2) + self.remove_drop_reason(name2, reason2) def test_createAndDeleteMultipleCounters(self, dvs, testlog): """ diff --git a/tests/test_flex_counters.py b/tests/test_flex_counters.py index ea950af7c16c..bd95aa433d78 100644 --- a/tests/test_flex_counters.py +++ b/tests/test_flex_counters.py @@ -6,6 +6,8 @@ TUNNEL_TYPE_MAP = "COUNTERS_TUNNEL_TYPE_MAP" NUMBER_OF_RETRIES = 10 CPU_PORT_OID = "0x0" +PORT = "Ethernet0" +PORT_MAP = "COUNTERS_PORT_NAME_MAP" counter_group_meta = { 'port_counter': { @@ -63,7 +65,7 @@ } } - +@pytest.mark.usefixtures('dvs_port_manager') class TestFlexCounters(object): def setup_dbs(self, dvs): @@ -372,3 +374,64 @@ def test_remove_trap_group(self, dvs): assert trap_id not in counters_keys self.set_flex_counter_group_status(meta_data['key'], meta_data['group_name'], 'disable') + + def test_add_remove_ports(self, dvs): + self.setup_dbs(dvs) + + # set flex counter + counter_key = counter_group_meta['queue_counter']['key'] + counter_stat = counter_group_meta['queue_counter']['group_name'] + counter_map = counter_group_meta['queue_counter']['name_map'] + self.set_flex_counter_group_status(counter_key, counter_map) + + # receive port info + fvs = self.config_db.get_entry("PORT", PORT) + assert len(fvs) > 0 + + # save all the oids of the pg drop counters + oid_list = [] + counters_queue_map = self.counters_db.get_entry("COUNTERS_QUEUE_NAME_MAP", "") + for key, oid in counters_queue_map.items(): + if PORT in key: + oid_list.append(oid) + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", counter_stat + ":%s" % oid) + assert len(fields) == 1 + oid_list_len = len(oid_list) + + # get port oid + port_oid = self.counters_db.get_entry(PORT_MAP, "")[PORT] + + # remove port and verify that it was removed properly + self.dvs_port.remove_port(PORT) + dvs.get_asic_db().wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid) + + # verify counters were removed from flex counter table + for oid in oid_list: + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", counter_stat + ":%s" % oid) + assert len(fields) == 0 + + # verify that port counter maps were removed from counters db + counters_queue_map = self.counters_db.get_entry("COUNTERS_QUEUE_NAME_MAP", "") + for key in counters_queue_map.keys(): + if PORT in key: + assert False + + # add port and wait until the port is added on asic db + num_of_keys_without_port = len(dvs.get_asic_db().get_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT")) + + self.config_db.create_entry("PORT", PORT, fvs) + + dvs.get_asic_db().wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT", num_of_keys_without_port + 1) + dvs.get_counters_db().wait_for_fields("COUNTERS_QUEUE_NAME_MAP", "", ["%s:0"%(PORT)]) + + # verify queue counters were added + oid_list = [] + counters_queue_map = self.counters_db.get_entry("COUNTERS_QUEUE_NAME_MAP", "") + + for key, oid in counters_queue_map.items(): + if PORT in key: + oid_list.append(oid) + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", counter_stat + ":%s" % oid) + assert len(fields) == 1 + # the number of the oids needs to be the same as the original number of oids (before removing a port and adding) + assert oid_list_len == len(oid_list) diff --git a/tests/test_pg_drop_counter.py b/tests/test_pg_drop_counter.py index 1cdd83474721..b3682881dec3 100644 --- a/tests/test_pg_drop_counter.py +++ b/tests/test_pg_drop_counter.py @@ -9,6 +9,9 @@ pg_drop_attr = "SAI_INGRESS_PRIORITY_GROUP_STAT_DROPPED_PACKETS" +PORT = "Ethernet0" + +@pytest.mark.usefixtures('dvs_port_manager') class TestPGDropCounter(object): DEFAULT_POLL_INTERVAL = 10 pgs = {} @@ -73,8 +76,7 @@ def clear_flex_counter(self): self.config_db.delete_entry("FLEX_COUNTER_TABLE", "PG_DROP") self.config_db.delete_entry("FLEX_COUNTER_TABLE", "PG_WATERMARK") - - + def test_pg_drop_counters(self, dvs): self.setup_dbs(dvs) self.pgs = self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP") @@ -94,3 +96,49 @@ def test_pg_drop_counters(self, dvs): self.verify_value(dvs, self.pgs, pg_drop_attr, "123") finally: self.clear_flex_counter() + + def test_pg_drop_counter_port_add_remove(self, dvs): + self.setup_dbs(dvs) + + try: + # configure pg drop flex counter + self.set_up_flex_counter() + + # receive port info + fvs = self.config_db.get_entry("PORT", PORT) + assert len(fvs) > 0 + + # save all the oids of the pg drop counters + oid_list = [] + for priority in range(0,7): + oid_list.append(dvs.get_counters_db().get_entry("COUNTERS_PG_NAME_MAP", "")["%s:%d"%(PORT, priority)]) + # verify that counters exists on flex counter + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", "PG_WATERMARK_STAT_COUNTER:%s"%oid_list[-1]) + assert len(fields) == 1 + + # remove port + port_oid = self.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "")[PORT] + self.dvs_port.remove_port(PORT) + dvs.get_asic_db().wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid) + + # verify counters were removed from flex counter table + for oid in oid_list: + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", "PG_WATERMARK_STAT_COUNTER:%s"%oid) + assert len(fields) == 0 + + # add port and wait until the port is added on asic db + num_of_keys_without_port = len(dvs.get_asic_db().get_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT")) + self.config_db.create_entry("PORT", PORT, fvs) + dvs.get_asic_db().wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT", num_of_keys_without_port + 1) + dvs.get_counters_db().wait_for_fields("COUNTERS_PG_NAME_MAP", "", ["%s:0"%(PORT)]) + + # verify counter was added + for priority in range(0,7): + oid = dvs.get_counters_db().get_entry("COUNTERS_PG_NAME_MAP", "")["%s:%d"%(PORT, priority)] + + # verify that counters exists on flex counter + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", "PG_WATERMARK_STAT_COUNTER:%s"%oid) + assert len(fields) == 1 + + finally: + self.clear_flex_counter()