From bbbd5f44f2c55808785672177e44527f635204d6 Mon Sep 17 00:00:00 2001 From: Vivek R Date: Wed, 18 May 2022 15:27:11 -0700 Subject: [PATCH] [FDB] Fix fbdorch to properly handle syncd FDB FLUSH Notif (#2254) Vlan delete couldn't be handled by OA when there is fdb learnt on the member and when the member is deleted This inability of handling APPL_DB notif is affecting warm-restart. FDB Entry from State DB is not removed. OA doesn't have the logic to handle consolidate flush notif coming from syncd FdbOrch doesn't have logic to clear internal cache and decrement corresponding fdb counters during a flush notification Signed-off-by: Vivek Reddy Karri --- orchagent/fdborch.cpp | 223 ++++----- orchagent/fdborch.h | 3 + orchagent/portsorch.cpp | 17 +- orchagent/portsorch.h | 1 + tests/mock_tests/Makefile.am | 1 + .../fdborch/flush_syncd_notif_ut.cpp | 424 ++++++++++++++++++ tests/mock_tests/mock_table.cpp | 18 +- 7 files changed, 577 insertions(+), 110 deletions(-) create mode 100644 tests/mock_tests/fdborch/flush_syncd_notif_ut.cpp diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index 20869f9b3fff..d4e0d8ffadec 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -18,7 +18,6 @@ extern sai_fdb_api_t *sai_fdb_api; extern sai_object_id_t gSwitchId; -extern PortsOrch* gPortsOrch; extern CrmOrch * gCrmOrch; extern MlagOrch* gMlagOrch; extern Directory gDirectory; @@ -175,6 +174,107 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) } } +/* +clears stateDb and decrements corresponding internal fdb counters +*/ +void FdbOrch::clearFdbEntry(const MacAddress& mac, + const sai_object_id_t& bv_id, + const string& port_alias) +{ + FdbUpdate update; + update.entry.mac = mac; + update.entry.bv_id = bv_id; + update.add = false; + + /* Fetch Vlan and decrement the counter */ + Port temp_vlan; + if (m_portsOrch->getPort(bv_id, temp_vlan)) + { + m_portsOrch->decrFdbCount(temp_vlan.m_alias, 1); + } + + /* Decrement port fdb_counter */ + m_portsOrch->decrFdbCount(port_alias, 1); + + /* Remove the FdbEntry from the internal cache, update state DB and CRM counter */ + storeFdbEntryState(update); + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + + SWSS_LOG_INFO("FdbEntry removed from internal cache, MAC: %s , port: %s, BVID: 0x%" PRIx64, + mac.to_string().c_str(), port_alias.c_str(), bv_id); +} + +/* +Handles the SAI_FDB_EVENT_FLUSHED notification recieved from syncd +*/ +void FdbOrch::handleSyncdFlushNotif(const sai_object_id_t& bv_id, + const sai_object_id_t& bridge_port_id, + const MacAddress& mac) +{ + // Consolidated flush will have a zero mac + MacAddress flush_mac("00:00:00:00:00:00"); + + /* TODO: Read the SAI_FDB_FLUSH_ATTR_ENTRY_TYPE attr from the flush notif + and clear the entries accordingly, currently only non-static entries are flushed + */ + if (bridge_port_id == SAI_NULL_OBJECT_ID && bv_id == SAI_NULL_OBJECT_ID) + { + for (auto itr = m_entries.begin(); itr != m_entries.end();) + { + auto curr = itr++; + if (curr->second.type != "static" && (curr->first.mac == mac || mac == flush_mac)) + { + clearFdbEntry(curr->first.mac, curr->first.bv_id, curr->first.port_name); + } + } + } + else if (bv_id == SAI_NULL_OBJECT_ID) + { + /* FLUSH based on PORT */ + for (auto itr = m_entries.begin(); itr != m_entries.end();) + { + auto curr = itr++; + if (curr->second.bridge_port_id == bridge_port_id) + { + if (curr->second.type != "static" && (curr->first.mac == mac || mac == flush_mac)) + { + clearFdbEntry(curr->first.mac, curr->first.bv_id, curr->first.port_name); + } + } + } + } + else if (bridge_port_id == SAI_NULL_OBJECT_ID) + { + /* FLUSH based on BV_ID */ + for (auto itr = m_entries.begin(); itr != m_entries.end();) + { + auto curr = itr++; + if (curr->first.bv_id == bv_id) + { + if (curr->second.type != "static" && (curr->first.mac == mac || mac == flush_mac)) + { + clearFdbEntry(curr->first.mac, curr->first.bv_id, curr->first.port_name); + } + } + } + } + else + { + /* FLUSH based on port and VLAN */ + for (auto itr = m_entries.begin(); itr != m_entries.end();) + { + auto curr = itr++; + if (curr->first.bv_id == bv_id && curr->second.bridge_port_id == bridge_port_id) + { + if (curr->second.type != "static" && (curr->first.mac == mac || mac == flush_mac)) + { + clearFdbEntry(curr->first.mac, curr->first.bv_id, curr->first.port_name); + } + } + } + } +} + void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_object_id_t bridge_port_id) @@ -192,24 +292,29 @@ void FdbOrch::update(sai_fdb_event_t type, type, update.entry.mac.to_string().c_str(), entry->bv_id, bridge_port_id); - if (bridge_port_id && !m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) { if (type == SAI_FDB_EVENT_FLUSHED) { - /* In case of flush - can be ignored due to a race. - There are notifications about FDB FLUSH (syncd/sai_redis) on port, - which was already removed by orchagent as a result of - removeVlanMember action (removeBridgePort) */ + /* There are notifications about FDB FLUSH (syncd/sai_redis) on port, + which was already removed by orchagent as a result of removeVlanMember + action (removeBridgePort). But the internal cleanup of statedb and + internal counters is yet to be performed, thus continue + */ SWSS_LOG_INFO("Flush event: Failed to get port by bridge port ID 0x%" PRIx64 ".", bridge_port_id); - } else { SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%" PRIx64 ".", bridge_port_id); - + return; } + } + + if (entry->bv_id && + !m_portsOrch->getPort(entry->bv_id, vlan)) + { + SWSS_LOG_NOTICE("FdbOrch notification type %d: Failed to locate vlan port from bv_id 0x%" PRIx64, type, entry->bv_id); return; } @@ -219,12 +324,6 @@ void FdbOrch::update(sai_fdb_event_t type, { SWSS_LOG_INFO("Received LEARN event for bvid=0x%" PRIx64 "mac=%s port=0x%" PRIx64, entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); - if (!m_portsOrch->getPort(entry->bv_id, vlan)) - { - SWSS_LOG_ERROR("FdbOrch LEARN notification: Failed to locate vlan port from bv_id 0x%" PRIx64, entry->bv_id); - return; - } - // we already have such entries auto existing_entry = m_entries.find(update.entry); if (existing_entry != m_entries.end()) @@ -319,11 +418,6 @@ void FdbOrch::update(sai_fdb_event_t type, SWSS_LOG_INFO("Received AGE event for bvid=0x%" PRIx64 " mac=%s port=0x%" PRIx64, entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); - if (!m_portsOrch->getPort(entry->bv_id, vlan)) - { - SWSS_LOG_NOTICE("FdbOrch AGE notification: Failed to locate vlan port from bv_id 0x%" PRIx64, entry->bv_id); - } - auto existing_entry = m_entries.find(update.entry); // we don't have such entries if (existing_entry == m_entries.end()) @@ -457,12 +551,6 @@ void FdbOrch::update(sai_fdb_event_t type, SWSS_LOG_INFO("Received MOVE event for bvid=0x%" PRIx64 " mac=%s port=0x%" PRIx64, entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); - if (!m_portsOrch->getPort(entry->bv_id, vlan)) - { - SWSS_LOG_ERROR("FdbOrch MOVE notification: Failed to locate vlan port from bv_id 0x%" PRIx64, entry->bv_id); - return; - } - // We should already have such entry if (existing_entry == m_entries.end()) { @@ -500,80 +588,15 @@ void FdbOrch::update(sai_fdb_event_t type, bridge_port_id); string vlanName = "-"; - if (entry->bv_id) { - Port vlan; - - if (!m_portsOrch->getPort(entry->bv_id, vlan)) - { - SWSS_LOG_NOTICE("FdbOrch notification: Failed to locate vlan\ - port from bv_id 0x%" PRIx64, entry->bv_id); - return; - } + if (!vlan.m_alias.empty()) { vlanName = "Vlan" + to_string(vlan.m_vlan_info.vlan_id); } + SWSS_LOG_INFO("FDB Flush: [ %s , %s ] = { port: %s }", update.entry.mac.to_string().c_str(), + vlanName.c_str(), update.port.m_alias.c_str()); - if (bridge_port_id == SAI_NULL_OBJECT_ID && - entry->bv_id == SAI_NULL_OBJECT_ID) - { - SWSS_LOG_INFO("FDB Flush: [ %s , %s ] = { port: - }", - update.entry.mac.to_string().c_str(), vlanName.c_str()); - for (auto itr = m_entries.begin(); itr != m_entries.end();) - { - /* - TODO: here should only delete the dynamic fdb entries, - but unfortunately in structure FdbEntry currently have - no member to indicate the fdb entry type, - if there is static mac added, here will have issue. - */ - update.entry.mac = itr->first.mac; - update.entry.bv_id = itr->first.bv_id; - update.add = false; - itr++; - - storeFdbEntryState(update); - - notify(SUBJECT_TYPE_FDB_CHANGE, &update); - - } - } - else if (entry->bv_id == SAI_NULL_OBJECT_ID) - { - /* FLUSH based on port */ - SWSS_LOG_INFO("FDB Flush: [ %s , %s ] = { port: %s }", - update.entry.mac.to_string().c_str(), - vlanName.c_str(), update.port.m_alias.c_str()); - - for (auto itr = m_entries.begin(); itr != m_entries.end();) - { - auto next_item = std::next(itr); - if (itr->first.port_name == update.port.m_alias) - { - update.entry.mac = itr->first.mac; - update.entry.bv_id = itr->first.bv_id; - update.add = false; + handleSyncdFlushNotif(entry->bv_id, bridge_port_id, update.entry.mac); - storeFdbEntryState(update); - notify(SUBJECT_TYPE_FDB_CHANGE, &update); - } - itr = next_item; - } - } - else if (bridge_port_id == SAI_NULL_OBJECT_ID) - { - /* FLUSH based on VLAN - unsupported */ - SWSS_LOG_ERROR("Unsupported FDB Flush: [ %s , %s ] = { port: - }", - update.entry.mac.to_string().c_str(), - vlanName.c_str()); - - } - else - { - /* FLUSH based on port and VLAN - unsupported */ - SWSS_LOG_ERROR("Unsupported FDB Flush: [ %s , %s ] = { port: %s }", - update.entry.mac.to_string().c_str(), - vlanName.c_str(), update.port.m_alias.c_str()); - } break; } @@ -649,7 +672,7 @@ void FdbOrch::doTask(Consumer& consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->allPortsReady()) + if (!m_portsOrch->allPortsReady()) { return; } @@ -856,7 +879,7 @@ void FdbOrch::doTask(NotificationConsumer& consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->allPortsReady()) + if (!m_portsOrch->allPortsReady()) { return; } @@ -892,7 +915,7 @@ void FdbOrch::doTask(NotificationConsumer& consumer) SWSS_LOG_ERROR("Receive wrong port to flush fdb!"); return; } - if (!gPortsOrch->getPort(alias, port)) + if (!m_portsOrch->getPort(alias, port)) { SWSS_LOG_ERROR("Get Port from port(%s) failed!", alias.c_str()); return; @@ -913,7 +936,7 @@ void FdbOrch::doTask(NotificationConsumer& consumer) SWSS_LOG_ERROR("Receive wrong vlan to flush fdb!"); return; } - if (!gPortsOrch->getPort(vlan, vlanPort)) + if (!m_portsOrch->getPort(vlan, vlanPort)) { SWSS_LOG_ERROR("Get Port from vlan(%s) failed!", vlan.c_str()); return; @@ -939,12 +962,12 @@ void FdbOrch::doTask(NotificationConsumer& consumer) SWSS_LOG_ERROR("Receive wrong port or vlan to flush fdb!"); return; } - if (!gPortsOrch->getPort(alias, port)) + if (!m_portsOrch->getPort(alias, port)) { SWSS_LOG_ERROR("Get Port from port(%s) failed!", alias.c_str()); return; } - if (!gPortsOrch->getPort(vlan, vlanPort)) + if (!m_portsOrch->getPort(vlan, vlanPort)) { SWSS_LOG_ERROR("Get Port from vlan(%s) failed!", vlan.c_str()); return; diff --git a/orchagent/fdborch.h b/orchagent/fdborch.h index 82611e686f67..3e53dcd3948d 100644 --- a/orchagent/fdborch.h +++ b/orchagent/fdborch.h @@ -122,6 +122,9 @@ class FdbOrch: public Orch, public Subject, public Observer bool storeFdbEntryState(const FdbUpdate& update); void notifyTunnelOrch(Port& port); + + void clearFdbEntry(const MacAddress&, const sai_object_id_t&, const string&); + void handleSyncdFlushNotif(const sai_object_id_t&, const sai_object_id_t&, const MacAddress& ); }; #endif /* SWSS_FDBORCH_H */ diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 0946d4ff4388..5a6ba61e5cf7 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -4573,9 +4573,10 @@ bool PortsOrch::removeVlan(Port vlan) return false for retry */ if (vlan.m_fdb_count > 0) { - SWSS_LOG_NOTICE("VLAN %s still has assiciated FDB entries", vlan.m_alias.c_str()); + SWSS_LOG_NOTICE("VLAN %s still has %d FDB entries", vlan.m_alias.c_str(), vlan.m_fdb_count); return false; } + if (m_port_ref_count[vlan.m_alias] > 0) { SWSS_LOG_ERROR("Failed to remove ref count %d VLAN %s", @@ -6928,3 +6929,17 @@ std::unordered_set PortsOrch::generateCounterStats(const string& ty } return counter_stats; } + +bool PortsOrch::decrFdbCount(const std::string& alias, int count) +{ + auto itr = m_portList.find(alias); + if (itr == m_portList.end()) + { + return false; + } + else + { + itr->second.m_fdb_count -= count; + } + return true; +} diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 554d49a2fcc1..2848cdcb9177 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -168,6 +168,7 @@ class PortsOrch : public Orch, public Subject bool getPortOperStatus(const Port& port, sai_port_oper_status_t& status) const; + bool decrFdbCount(const string& alias, int count); private: unique_ptr m_counterTable; unique_ptr
m_counterLagTable; diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 0bc676e6d3a8..2a6dade25444 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -26,6 +26,7 @@ tests_SOURCES = aclorch_ut.cpp \ routeorch_ut.cpp \ qosorch_ut.cpp \ bufferorch_ut.cpp \ + fdborch/flush_syncd_notif_ut.cpp \ copporch_ut.cpp \ saispy_ut.cpp \ consumer_ut.cpp \ diff --git a/tests/mock_tests/fdborch/flush_syncd_notif_ut.cpp b/tests/mock_tests/fdborch/flush_syncd_notif_ut.cpp new file mode 100644 index 000000000000..7bf22373c162 --- /dev/null +++ b/tests/mock_tests/fdborch/flush_syncd_notif_ut.cpp @@ -0,0 +1,424 @@ +#include "../ut_helper.h" +#include "../mock_orchagent_main.h" +#include "../mock_table.h" +#include "port.h" +#define private public // Need to modify internal cache +#include "portsorch.h" +#include "fdborch.h" +#include "crmorch.h" +#undef private + +#define ETH0 "Ethernet0" +#define VLAN40 "Vlan40" + +extern redisReply *mockReply; +extern CrmOrch* gCrmOrch; + +/* +Test Fixture +*/ +namespace fdb_syncd_flush_test +{ + struct FdbOrchTest : public ::testing::Test + { + std::shared_ptr m_config_db; + std::shared_ptr m_app_db; + std::shared_ptr m_state_db; + std::shared_ptr m_asic_db; + std::shared_ptr m_chassis_app_db; + std::shared_ptr m_portsOrch; + std::shared_ptr m_fdborch; + + virtual void SetUp() override + { + + testing_db::reset(); + + map profile = { + { "SAI_VS_SWITCH_TYPE", "SAI_VS_SWITCH_TYPE_BCM56850" }, + { "KV_DEVICE_MAC_ADDRESS", "20:03:04:05:06:00" } + }; + + ut_helper::initSaiApi(profile); + + /* Create Switch */ + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + auto status = sai_switch_api->create_switch(&gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + m_config_db = std::make_shared("CONFIG_DB", 0); + m_app_db = std::make_shared("APPL_DB", 0); + m_state_db = make_shared("STATE_DB", 0); + m_asic_db = std::make_shared("ASIC_DB", 0); + + // Construct dependencies + // 1) Portsorch + const int portsorch_base_pri = 40; + + vector ports_tables = { + { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, + { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, + { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, + { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, + { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } + }; + + m_portsOrch = std::make_shared(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + + // 2) Crmorch + ASSERT_EQ(gCrmOrch, nullptr); + gCrmOrch = new CrmOrch(m_config_db.get(), CFG_CRM_TABLE_NAME); + + // Construct fdborch + vector app_fdb_tables = { + { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_MCLAG_FDB_TABLE_NAME, FdbOrch::fdborch_pri} + }; + + TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); + TableConnector stateMclagDbFdb(m_state_db.get(), STATE_MCLAG_REMOTE_FDB_TABLE_NAME); + + m_fdborch = std::make_shared(m_app_db.get(), + app_fdb_tables, + stateDbFdb, + stateMclagDbFdb, + m_portsOrch.get()); + } + + virtual void TearDown() override { + delete gCrmOrch; + gCrmOrch = nullptr; + + ut_helper::uninitSaiApi(); + } + }; + + /* Helper Methods */ + void setUpVlan(PortsOrch* m_portsOrch){ + /* Updates portsOrch internal cache for Vlan40 */ + std::string alias = VLAN40; + sai_object_id_t oid = 0x26000000000796; + + Port vlan(alias, Port::VLAN); + vlan.m_vlan_info.vlan_oid = oid; + vlan.m_vlan_info.vlan_id = 40; + vlan.m_members = set(); + + m_portsOrch->m_portList[alias] = vlan; + m_portsOrch->m_port_ref_count[alias] = 0; + m_portsOrch->saiOidToAlias[oid] = alias; + } + + void setUpPort(PortsOrch* m_portsOrch){ + /* Updates portsOrch internal cache for Ethernet0 */ + std::string alias = ETH0; + sai_object_id_t oid = 0x10000000004a4; + + Port port(alias, Port::PHY); + port.m_index = 1; + port.m_port_id = oid; + port.m_hif_id = 0xd00000000056e; + + m_portsOrch->m_portList[alias] = port; + m_portsOrch->saiOidToAlias[oid] = alias; + } + + void setUpVlanMember(PortsOrch* m_portsOrch){ + /* Updates portsOrch internal cache for adding Ethernet0 into Vlan40 */ + sai_object_id_t bridge_port_id = 0x3a000000002c33; + + /* Add Bridge Port */ + m_portsOrch->m_portList[ETH0].m_bridge_port_id = bridge_port_id; + m_portsOrch->saiOidToAlias[bridge_port_id] = ETH0; + m_portsOrch->m_portList[VLAN40].m_members.insert(ETH0); + } + + void triggerUpdate(FdbOrch* m_fdborch, + sai_fdb_event_t type, + vector mac_addr, + sai_object_id_t bridge_port_id, + sai_object_id_t bv_id){ + sai_fdb_entry_t entry; + for (int i = 0; i < (int)mac_addr.size(); i++){ + *(entry.mac_address+i) = mac_addr[i]; + } + entry.bv_id = bv_id; + m_fdborch->update(type, &entry, bridge_port_id); + } +} + +namespace fdb_syncd_flush_test +{ + /* Test Consolidated Flush Per Vlan and Per Port */ + TEST_F(FdbOrchTest, ConsolidatedFlushVlanandPort) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + /* Event 2: Generate a FDB Flush per port and per vlan */ + vector flush_mac_addr = {0, 0, 0, 0, 0, 0}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + /* make sure fdb_counters are decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } + + /* Test Consolidated Flush All */ + TEST_F(FdbOrchTest, ConsolidatedFlushAll) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + /* Event2: Send a Consolidated Flush response from syncd */ + vector flush_mac_addr = {0, 0, 0, 0, 0, 0}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, SAI_NULL_OBJECT_ID, + SAI_NULL_OBJECT_ID); + + /* make sure fdb_counters are decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } + + /* Test Consolidated Flush per VLAN BV_ID */ + TEST_F(FdbOrchTest, ConsolidatedFlushVlan) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + /* Event2: Send a Consolidated Flush response from syncd for vlan */ + vector flush_mac_addr = {0, 0, 0, 0, 0, 0}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, SAI_NULL_OBJECT_ID, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + /* make sure fdb_counters are decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } + + /* Test Consolidated Flush per bridge port id */ + TEST_F(FdbOrchTest, ConsolidatedFlushPort) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + /* Event2: Send a Consolidated Flush response from syncd for a port */ + vector flush_mac_addr = {0, 0, 0, 0, 0, 0}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + SAI_NULL_OBJECT_ID); + + /* make sure fdb_counters are decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } + + //* Test Consolidated Flush Per Vlan and Per Port, but the bridge_port_id from the internal cache is already deleted */ + TEST_F(FdbOrchTest, ConsolidatedFlushVlanandPortBridgeportDeleted) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + auto bridge_port_oid = m_portsOrch->m_portList[ETH0].m_bridge_port_id; + + /* Delete the bridge_port_oid in the internal OA cache */ + m_portsOrch->m_portList[ETH0].m_bridge_port_id = SAI_NULL_OBJECT_ID; + m_portsOrch->saiOidToAlias.erase(bridge_port_oid); + + /* Event 2: Generate a FDB Flush per port and per vlan */ + vector flush_mac_addr = {0, 0, 0, 0, 0, 0}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, bridge_port_oid, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + /* make sure fdb_counter for Vlan is decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } + + /* Test Flush Per Vlan and Per Port */ + TEST_F(FdbOrchTest, NonConsolidatedFlushVlanandPort) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + /* Event 2: Generate a non-consilidated FDB Flush per port and per vlan */ + vector flush_mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + /* make sure fdb_counters are decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } +} diff --git a/tests/mock_tests/mock_table.cpp b/tests/mock_tests/mock_table.cpp index 080fce775b1b..0af0cb372f33 100644 --- a/tests/mock_tests/mock_table.cpp +++ b/tests/mock_tests/mock_table.cpp @@ -73,6 +73,15 @@ namespace swss keys.push_back(it.first); } } + + + void Table::del(const std::string &key, const std::string& /* op */, const std::string& /*prefix*/) + { + auto table = gDB[m_pipe->getDbId()].find(getTableName()); + if (table != gDB[m_pipe->getDbId()].end()){ + table->second.erase(key); + } + } void ProducerStateTable::set(const std::string &key, const std::vector &values, @@ -105,13 +114,4 @@ namespace swss iter->second.swap(new_values); } } - - void ProducerStateTable::del(const std::string &key, - const std::string &op, - const std::string &prefix) - { - auto &table = gDB[m_pipe->getDbId()][getTableName()]; - table.erase(key); - } - }