diff --git a/cfgmgr/vlanmgr.cpp b/cfgmgr/vlanmgr.cpp index a79aa844f2..dfd27fd989 100644 --- a/cfgmgr/vlanmgr.cpp +++ b/cfgmgr/vlanmgr.cpp @@ -13,10 +13,12 @@ using namespace swss; #define DOT1Q_BRIDGE_NAME "Bridge" #define VLAN_PREFIX "Vlan" +#define SWITCH_STR "switch" #define LAG_PREFIX "PortChannel" #define DEFAULT_VLAN_ID "1" #define DEFAULT_MTU_STR "9100" #define VLAN_HLEN 4 +#define MAX_VLAN_ID 4095 extern MacAddress gMacAddress; @@ -28,6 +30,8 @@ VlanMgr::VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME), m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME), m_stateVlanMemberTable(stateDb, STATE_VLAN_MEMBER_TABLE_NAME), + m_appFdbTableProducer(appDb, APP_FDB_TABLE_NAME), + m_appSwitchTableProducer(appDb, APP_SWITCH_TABLE_NAME), m_appVlanTableProducer(appDb, APP_VLAN_TABLE_NAME), m_appVlanMemberTableProducer(appDb, APP_VLAN_MEMBER_TABLE_NAME), replayDone(false) @@ -112,6 +116,10 @@ VlanMgr::VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c EXEC_WITH_ERROR_THROW(echo_cmd_backup, res); } + /* vlan state notification from portsorch */ + m_VlanStateNotificationConsumer = new swss::NotificationConsumer(appDb, "VLANSTATE"); + auto vlanStatusNotificatier = new Notifier(m_VlanStateNotificationConsumer, this, "VLANSTATE"); + Orch::addExecutor(vlanStatusNotificatier); } bool VlanMgr::addHostVlan(int vlan_id) @@ -125,7 +133,7 @@ bool VlanMgr::addHostVlan(int vlan_id) + BASH_CMD + " -c \"" + BRIDGE_CMD + " vlan add vid " + std::to_string(vlan_id) + " dev " + DOT1Q_BRIDGE_NAME + " self && " + IP_CMD + " link add link " + DOT1Q_BRIDGE_NAME - + " up" + + " down" + " name " + VLAN_PREFIX + std::to_string(vlan_id) + " address " + gMacAddress.to_string() + " type vlan id " + std::to_string(vlan_id) + "\""; @@ -218,14 +226,29 @@ bool VlanMgr::addHostVlanMember(int vlan_id, const string &port_alias, const str // /bin/bash -c "/sbin/ip link set {{port_alias}} master Bridge && // /sbin/bridge vlan del vid 1 dev {{ port_alias }} && // /sbin/bridge vlan add vid {{vlan_id}} dev {{port_alias}} {{tagging_mode}}" - ostringstream cmds, inner; - inner << IP_CMD " link set " << shellquote(port_alias) << " master " DOT1Q_BRIDGE_NAME " && " - BRIDGE_CMD " vlan del vid " DEFAULT_VLAN_ID " dev " << shellquote(port_alias) << " && " - BRIDGE_CMD " vlan add vid " + std::to_string(vlan_id) + " dev " << shellquote(port_alias) << " " + tagging_cmd; - cmds << BASH_CMD " -c " << shellquote(inner.str()); + + const std::string key = std::string("") + "Vlan1|" + port_alias; - std::string res; - EXEC_WITH_ERROR_THROW(cmds.str(), res); + if (isVlanMemberStateOk(key)) { + ostringstream cmds, inner; + inner << IP_CMD " link set " << shellquote(port_alias) << " master " DOT1Q_BRIDGE_NAME " && " + BRIDGE_CMD " vlan add vid " + std::to_string(vlan_id) + " dev " << shellquote(port_alias) << " " + tagging_cmd; + cmds << BASH_CMD " -c " << shellquote(inner.str()); + + std::string res; + EXEC_WITH_ERROR_THROW(cmds.str(), res); + } + else + { + ostringstream cmds, inner; + inner << IP_CMD " link set " << shellquote(port_alias) << " master " DOT1Q_BRIDGE_NAME " && " + BRIDGE_CMD " vlan del vid " DEFAULT_VLAN_ID " dev " << shellquote(port_alias) << " && " + BRIDGE_CMD " vlan add vid " + std::to_string(vlan_id) + " dev " << shellquote(port_alias) << " " + tagging_cmd; + cmds << BASH_CMD " -c " << shellquote(inner.str()); + + std::string res; + EXEC_WITH_ERROR_THROW(cmds.str(), res); + } return true; } @@ -263,6 +286,137 @@ bool VlanMgr::isVlanMacOk() return !!gMacAddress; } +void VlanMgr::doSwitchTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + auto it = consumer.m_toSync.begin(); + + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + /* Ensure the key starts with "switch" otherwise ignore */ + if (key != SWITCH_STR) + { + SWSS_LOG_NOTICE("Ignoring SWITCH key %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + SWSS_LOG_DEBUG("key:switch"); + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "fdb_aging_time") + { + long agingTime = 0; + SWSS_LOG_DEBUG("attribute:fdb_aging_time"); + if (op == SET_COMMAND) + { + SWSS_LOG_DEBUG("operation:set"); + agingTime = strtol(fvValue(i).c_str(), NULL, 0); + if (agingTime < 0) + { + SWSS_LOG_ERROR("Invalid fdb_aging_time %s", fvValue(i).c_str()); + break; + } + SWSS_LOG_DEBUG("value:%s",fvValue(i).c_str()); + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_DEBUG("operation:del"); + agingTime = 0; + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + break; + } + + vector fvVector; + FieldValueTuple aging_time("fdb_aging_time", to_string(agingTime)); + fvVector.push_back(aging_time); + m_appSwitchTableProducer.set(key, fvVector); + break; + } + } + + it = consumer.m_toSync.erase(it); + } +} + +void VlanMgr::doFdbTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + /* format: | */ + vector keys = tokenize(kfvKey(t), config_db_key_delimiter, 1); + /* keys[0] is vlan as (Vlan10) and keys[1] is mac as (00-00-00-00-00-00) */ + string op = kfvOp(t); + + /* Ensure the key starts with "Vlan" otherwise ignore */ + if (strncmp(keys[0].c_str(), VLAN_PREFIX, 4)) + { + SWSS_LOG_ERROR("Invalid key format. No 'Vlan' prefix: %s", keys[0].c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + unsigned long vlan_id; + vlan_id = strtoul(keys[0].substr(strlen(VLAN_PREFIX)).c_str(), NULL, 0); + + if ((vlan_id <= 0) || (vlan_id > MAX_VLAN_ID)) + { + SWSS_LOG_ERROR("Invalid key format. Vlan is out of range: %s", keys[0].c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + MacAddress mac = MacAddress(keys[1]); + + string key = VLAN_PREFIX + to_string(vlan_id); + key += DEFAULT_KEY_SEPARATOR; + key += mac.to_string(); + + if (op == SET_COMMAND) + { + string port; + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "port") + { + port = fvValue(i); + break; + } + } + + vector fvVector; + FieldValueTuple p("port", port); + fvVector.push_back(p); + FieldValueTuple t("type", "static"); + fvVector.push_back(t); + + m_appFdbTableProducer.set(key, fvVector); + } + else if (op == DEL_COMMAND) + { + m_appFdbTableProducer.del(key); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + } + it = consumer.m_toSync.erase(it); + } +} + void VlanMgr::doVlanTask(Consumer &consumer) { if (!isVlanMacOk()) @@ -666,9 +820,47 @@ void VlanMgr::doTask(Consumer &consumer) { doVlanMemberTask(consumer); } + else if (table_name == CFG_FDB_TABLE_NAME) + { + doFdbTask(consumer); + } + else if (table_name == CFG_SWITCH_TABLE_NAME) + { + SWSS_LOG_DEBUG("Table:SWITCH"); + doSwitchTask(consumer); + } else { SWSS_LOG_ERROR("Unknown config table %s ", table_name.c_str()); throw runtime_error("VlanMgr doTask failure."); } } + +void VlanMgr::doTask(NotificationConsumer &consumer) +{ + std::string op; + std::string data; + std::vector values; + + if (&consumer != m_VlanStateNotificationConsumer) + { + SWSS_LOG_WARN("received incorrect notification message"); + return; + } + + consumer.pop(op, data, values); + + unsigned long vlan_id = strtoul(data.substr(strlen(VLAN_PREFIX)).c_str(), NULL, 0); + + SWSS_LOG_NOTICE("vlanmgr received port status notification state %s vlan %s", + op.c_str(), data.c_str()); + + if (isVlanStateOk(data)) + { + setHostVlanAdminState((int)vlan_id, op); + } + else + { + SWSS_LOG_ERROR("received state update for vlan %s not existing", data.c_str()); + } +} diff --git a/cfgmgr/vlanmgr.h b/cfgmgr/vlanmgr.h index 8cf467f41c..3dd4a4d14e 100644 --- a/cfgmgr/vlanmgr.h +++ b/cfgmgr/vlanmgr.h @@ -4,13 +4,13 @@ #include "dbconnector.h" #include "producerstatetable.h" #include "orch.h" +#include "notifier.h" #include #include #include namespace swss { - class VlanMgr : public Orch { public: @@ -19,17 +19,24 @@ class VlanMgr : public Orch private: ProducerStateTable m_appVlanTableProducer, m_appVlanMemberTableProducer; + ProducerStateTable m_appFdbTableProducer; + ProducerStateTable m_appSwitchTableProducer; Table m_cfgVlanTable, m_cfgVlanMemberTable; Table m_statePortTable, m_stateLagTable; Table m_stateVlanTable, m_stateVlanMemberTable; std::set m_vlans; + NotificationConsumer* m_VlanStateNotificationConsumer; + std::set m_vlanReplay; std::set m_vlanMemberReplay; bool replayDone; void doTask(Consumer &consumer); + void doTask(NotificationConsumer &consumer); void doVlanTask(Consumer &consumer); void doVlanMemberTask(Consumer &consumer); + void doFdbTask(Consumer &consumer); + void doSwitchTask(Consumer &consumer); void processUntaggedVlanMembers(std::string vlan, const std::string &members); bool addHostVlan(int vlan_id); diff --git a/cfgmgr/vlanmgrd.cpp b/cfgmgr/vlanmgrd.cpp index 88e4745758..23d25df819 100644 --- a/cfgmgr/vlanmgrd.cpp +++ b/cfgmgr/vlanmgrd.cpp @@ -51,6 +51,8 @@ int main(int argc, char **argv) vector cfg_vlan_tables = { CFG_VLAN_TABLE_NAME, CFG_VLAN_MEMBER_TABLE_NAME, + CFG_FDB_TABLE_NAME, + CFG_SWITCH_TABLE_NAME, }; DBConnector cfgDb("CONFIG_DB", 0); diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index d2edd0ba20..8e23ac4698 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -57,7 +57,10 @@ bool FdbOrch::bake() bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) { const FdbEntry& entry = update.entry; + const FdbData fdbdata = {update.port.m_bridge_port_id, update.type}; + const Port& port = update.port; const MacAddress& mac = entry.mac; + string portName = port.m_alias; Port vlan; if (!m_portsOrch->getPort(entry.bv_id, vlan)) @@ -72,27 +75,35 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) if (update.add) { - SWSS_LOG_INFO("Storing FDB entry: [%s, 0x%" PRIx64 "] [ port: %s ]", - entry.mac.to_string().c_str(), - entry.bv_id, entry.port_name.c_str()); - auto inserted = m_entries.insert(entry); + bool mac_move = false; + auto it = m_entries.find(entry); + if (it != m_entries.end()) + { + if (port.m_bridge_port_id == it->second.bridge_port_id) + { + SWSS_LOG_INFO("FdbOrch notification: mac %s is duplicate", entry.mac.to_string().c_str()); + return false; + } + mac_move = true; + } + m_entries[entry] = fdbdata; SWSS_LOG_DEBUG("FdbOrch notification: mac %s was inserted into bv_id 0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); - - if (!inserted.second) - { - SWSS_LOG_INFO("FdbOrch notification: mac %s is duplicate", entry.mac.to_string().c_str()); - return false; - } + SWSS_LOG_DEBUG("m_entries size=%lu mac=%s port=0x%" PRIx64, + m_entries.size(), entry.mac.to_string().c_str(), m_entries[entry].bridge_port_id); // Write to StateDb std::vector fvs; - fvs.push_back(FieldValueTuple("port", entry.port_name)); - fvs.push_back(FieldValueTuple("type", "dynamic")); + fvs.push_back(FieldValueTuple("port", portName)); + fvs.push_back(FieldValueTuple("type", update.type)); + m_fdbStateTable.set(key, fvs); - gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + if (!mac_move) + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + } return true; } else @@ -122,6 +133,8 @@ void FdbOrch::update(sai_fdb_event_t type, FdbUpdate update; update.entry.mac = entry->mac_address; update.entry.bv_id = entry->bv_id; + update.type = "dynamic"; + Port vlan; SWSS_LOG_INFO("FDB event:%d, MAC: %s , BVID: 0x%" PRIx64 " , \ bridge port ID: 0x%" PRIx64 ".", @@ -139,23 +152,39 @@ void FdbOrch::update(sai_fdb_event_t type, switch (type) { case SAI_FDB_EVENT_LEARNED: + { + 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; + } + if (!m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) { - SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%" PRIx64, bridge_port_id); + SWSS_LOG_ERROR("FdbOrch LEARN notification: Failed to get port by bridge port ID 0x%" PRIx64, bridge_port_id); return; } // we already have such entries - if (m_entries.find(update.entry) != m_entries.end()) + auto existing_entry = m_entries.find(update.entry); + if (existing_entry != m_entries.end()) { - SWSS_LOG_INFO("FdbOrch notification: mac %s is already in bv_id 0x%" PRIx64, - update.entry.mac.to_string().c_str(), entry->bv_id); + SWSS_LOG_INFO("FdbOrch LEARN notification: mac %s is already in bv_id 0x%" + PRIx64 "existing-bp 0x%" PRIx64 "new-bp:0x%" PRIx64, + update.entry.mac.to_string().c_str(), entry->bv_id, existing_entry->second.bridge_port_id, bridge_port_id); break; } update.add = true; + update.type = "dynamic"; update.entry.port_name = update.port.m_alias; storeFdbEntryState(update); + update.port.m_fdb_count++; + m_portsOrch->setPort(update.port.m_alias, update.port); + vlan.m_fdb_count++; + m_portsOrch->setPort(vlan.m_alias, vlan); SWSS_LOG_INFO("Notifying observers of FDB entry LEARN"); for (auto observer: m_observers) @@ -164,107 +193,195 @@ void FdbOrch::update(sai_fdb_event_t type, } break; - + } case SAI_FDB_EVENT_AGED: - case SAI_FDB_EVENT_MOVE: + { + SWSS_LOG_INFO("Received AGE event for bvid=%lx mac=%s port=%lx", 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%lx", entry->bv_id); + } + + auto existing_entry = m_entries.find(update.entry); + // we don't have such entries + if (existing_entry == m_entries.end()) + { + SWSS_LOG_INFO("FdbOrch AGE notification: mac %s is not present in bv_id 0x%lx bp 0x%lx", + update.entry.mac.to_string().c_str(), entry->bv_id, bridge_port_id); + break; + } + + if (!m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) + { + SWSS_LOG_NOTICE("FdbOrch AGE notification: Failed to get port by bridge port ID 0x%lx", bridge_port_id); + } + + if (existing_entry->second.bridge_port_id != bridge_port_id) + { + SWSS_LOG_NOTICE("FdbOrch AGE notification: Stale aging event received for mac-bv_id %s-0x%lx with bp=0x%lx existing bp=0x%lx", update.entry.mac.to_string().c_str(), entry->bv_id, bridge_port_id, existing_entry->second.bridge_port_id); + // We need to get the port for bridge-port in existing fdb + if (!m_portsOrch->getPortByBridgePortId(existing_entry->second.bridge_port_id, update.port)) + { + SWSS_LOG_NOTICE("FdbOrch AGE notification: Failed to get port by bridge port ID 0x%lx", existing_entry->second.bridge_port_id); + } + // dont return, let it delete just to bring SONiC and SAI in sync + // return; + } + + if (existing_entry->second.type == "static") + { + if (vlan.m_members.find(update.port.m_alias) == vlan.m_members.end()) + { + update.type = "static"; + saved_fdb_entries[update.port.m_alias].push_back({existing_entry->first.mac, vlan.m_vlan_info.vlan_id, "static"}); + + } + else + { + /*port added back to vlan before we receive delete + notification for flush from SAI. Re-add entry to SAI + */ + sai_attribute_t attr; + vector attrs; + + attr.id = SAI_FDB_ENTRY_ATTR_TYPE; + attr.value.s32 = SAI_FDB_ENTRY_TYPE_STATIC; + attrs.push_back(attr); + attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; + attr.value.oid = bridge_port_id; + attrs.push_back(attr); + auto status = sai_fdb_api->create_fdb_entry(entry, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create FDB %s on %s, rv:%d", + existing_entry->first.mac.to_string().c_str(), update.port.m_alias.c_str(), status); + return; + } + return; + } + } + update.add = false; storeFdbEntryState(update); + if (!update.port.m_alias.empty()) + { + update.port.m_fdb_count--; + m_portsOrch->setPort(update.port.m_alias, update.port); + } + if (!vlan.m_alias.empty()) + { + vlan.m_fdb_count--; + m_portsOrch->setPort(vlan.m_alias, vlan); + } SWSS_LOG_INFO("Notifying observers of FDB entry removal on AGED/MOVED"); for (auto observer: m_observers) { observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); } - break; + } + case SAI_FDB_EVENT_MOVE: + { + Port port_old; + auto existing_entry = m_entries.find(update.entry); - case SAI_FDB_EVENT_FLUSHED: + SWSS_LOG_INFO("Received MOVE event for bvid=%lx mac=%s port=%lx", entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); + update.add = true; - SWSS_LOG_INFO("FDB Flush event received: [ %s , 0x%" PRIx64 " ], \ - bridge port ID: 0x%" PRIx64 ".", - update.entry.mac.to_string().c_str(), entry->bv_id, - 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%lx", entry->bv_id); + return; + } - string vlanName = "-"; - if (entry->bv_id) { - Port vlan; + if (!m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) + { + SWSS_LOG_ERROR("FdbOrch MOVE notification: Failed to get port by bridge port ID 0x%lx", bridge_port_id); + return; + } - if (!m_portsOrch->getPort(entry->bv_id, vlan)) - { - SWSS_LOG_ERROR("FdbOrch notification: Failed to locate vlan\ - port from bv_id 0x%" PRIx64, entry->bv_id); - return; - } - vlanName = "Vlan" + to_string(vlan.m_vlan_info.vlan_id); + // We should already have such entry + if (existing_entry == m_entries.end()) + { + SWSS_LOG_WARN("FdbOrch MOVE notification: mac %s is not found in bv_id 0x%lx", + update.entry.mac.to_string().c_str(), entry->bv_id); } + else if (!m_portsOrch->getPortByBridgePortId(existing_entry->second.bridge_port_id, port_old)) + { + SWSS_LOG_ERROR("FdbOrch MOVE notification: Failed to get port by bridge port ID 0x%lx", existing_entry->second.bridge_port_id); + return; + } + if (!port_old.m_alias.empty()) + { + port_old.m_fdb_count--; + m_portsOrch->setPort(port_old.m_alias, port_old); + } + update.port.m_fdb_count++; + m_portsOrch->setPort(update.port.m_alias, update.port); + storeFdbEntryState(update); - if (bridge_port_id == SAI_NULL_OBJECT_ID && - entry->bv_id == SAI_NULL_OBJECT_ID) + for (auto observer: m_observers) { - 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();) + observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + } + break; + } + + case SAI_FDB_EVENT_FLUSHED: + SWSS_LOG_INFO("Received FLUSH event for bvid=%lx mac=%s port=%lx", entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); + for (auto itr = m_entries.begin(); itr != m_entries.end();) + { + if (itr->second.type == "static") { - /* - 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->mac; - update.entry.bv_id = itr->bv_id; - update.add = false; itr++; + continue; + } - storeFdbEntryState(update); + if (((bridge_port_id == SAI_NULL_OBJECT_ID) && (entry->bv_id == SAI_NULL_OBJECT_ID)) // Flush all DYNAMIC + || ((bridge_port_id == itr->second.bridge_port_id) && (entry->bv_id == SAI_NULL_OBJECT_ID)) // flush all DYN on a port + || ((bridge_port_id == SAI_NULL_OBJECT_ID) && (entry->bv_id == itr->first.bv_id))) // flush all DYN on a vlan + { - for (auto observer: m_observers) + if (!m_portsOrch->getPortByBridgePortId(itr->second.bridge_port_id, update.port)) { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + SWSS_LOG_ERROR("FdbOrch FLUSH notification: Failed to get port by bridge port ID 0x%lx", itr->second.bridge_port_id); + itr++; + continue; } - } - } - 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->port_name == update.port.m_alias) + if (!m_portsOrch->getPort(itr->first.bv_id, vlan)) { - update.entry.mac = itr->mac; - update.entry.bv_id = itr->bv_id; - update.add = false; + SWSS_LOG_NOTICE("FdbOrch FLUSH notification: Failed to locate vlan port from bv_id 0x%lx", itr->first.bv_id); + itr++; + continue; + } - storeFdbEntryState(update); + update.entry.mac = itr->first.mac; + update.entry.bv_id = itr->first.bv_id; + update.add = false; + update.vlan_id = vlan.m_vlan_info.vlan_id; + itr++; - for (auto observer: m_observers) - { - observer->update(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()); + update.port.m_fdb_count--; + m_portsOrch->setPort(update.port.m_alias, update.port); + vlan.m_fdb_count--; + m_portsOrch->setPort(vlan.m_alias, vlan); + + /* This will invalidate the current iterator hence itr++ is done before */ + storeFdbEntryState(update); + + SWSS_LOG_DEBUG("FdbOrch FLUSH notification: mac %s was removed", update.entry.mac.to_string().c_str()); + + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + } + else + { + itr++; + } } break; } @@ -355,7 +472,24 @@ void FdbOrch::doTask(Consumer& consumer) if (!m_portsOrch->getPort(keys[0], vlan)) { SWSS_LOG_INFO("Failed to locate %s", keys[0].c_str()); - it++; + if(op == DEL_COMMAND) + { + /* Delete if it is in saved_fdb_entry */ + unsigned short vlan_id; + try { + vlan_id = (unsigned short) stoi(keys[0].substr(4)); + } catch(exception &e) { + it = consumer.m_toSync.erase(it); + continue; + } + deleteFdbEntryFromSavedFDB(MacAddress(keys[1]), vlan_id, ""); + + it = consumer.m_toSync.erase(it); + } + else + { + it++; + } continue; } @@ -385,7 +519,7 @@ void FdbOrch::doTask(Consumer& consumer) /* FDB type is either dynamic or static */ assert(type == "dynamic" || type == "static"); - if (addFdbEntry(entry, type)) + if (addFdbEntry(entry, port, type)) it = consumer.m_toSync.erase(it); else it++; @@ -423,7 +557,6 @@ void FdbOrch::doTask(NotificationConsumer& consumer) return; } - sai_status_t status; std::string op; std::string data; std::vector values; @@ -434,28 +567,17 @@ void FdbOrch::doTask(NotificationConsumer& consumer) { if (op == "ALL") { - /* - * so far only support flush all the FDB entris - * flush per port and flush per vlan will be added later. - */ - status = sai_fdb_api->flush_fdb_entries(gSwitchId, 0, NULL); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); - } - + flushFdbAll(0); return; } else if (op == "PORT") { - /*place holder for flush port fdb*/ - SWSS_LOG_ERROR("Received unsupported flush port fdb request"); + flushFdbByPort(data, 0); return; } else if (op == "VLAN") { - /*place holder for flush vlan fdb*/ - SWSS_LOG_ERROR("Received unsupported flush vlan fdb request"); + flushFdbByVlan(data, 0); return; } else @@ -550,14 +672,14 @@ void FdbOrch::notifyObserversFDBFlush(Port &port, sai_object_id_t& bvid) for (auto itr = m_entries.begin(); itr != m_entries.end(); ++itr) { - if ((itr->port_name == port.m_alias) && - (itr->bv_id == bvid)) + if ((itr->second.bridge_port_id == port.m_bridge_port_id) && + (itr->first.bv_id == bvid)) { SWSS_LOG_INFO("Adding MAC learnt on [ port:%s , bvid:0x%" PRIx64 "]\ to ARP flush", port.m_alias.c_str(), bvid); FdbEntry entry; - entry.mac = itr->mac; - entry.bv_id = itr->bv_id; + entry.mac = itr->first.mac; + entry.bv_id = itr->first.bv_id; flushUpdate.entries.push_back(entry); } } @@ -605,7 +727,7 @@ void FdbOrch::updateVlanMember(const VlanMemberUpdate& update) { swss::Port vlan = update.vlan; swss::Port port = update.member; - flushFDBEntries(port.m_bridge_port_id, vlan.m_vlan_info.vlan_oid); + flushFdbByPortVlan(port.m_alias, vlan.m_alias, 1); notifyObserversFDBFlush(port, vlan.m_vlan_info.vlan_oid); return; } @@ -615,43 +737,84 @@ void FdbOrch::updateVlanMember(const VlanMemberUpdate& update) saved_fdb_entries[port_name].clear(); for (const auto& fdb: fdb_list) { - // try to insert an FDB entry. If the FDB entry is not ready to be inserted yet, - // it would be added back to the saved_fdb_entries structure by addFDBEntry() - (void)addFdbEntry(fdb.entry, fdb.type); + if(fdb.vlanId == update.vlan.m_vlan_info.vlan_id) + { + FdbEntry entry; + entry.mac = fdb.mac; + entry.bv_id = update.vlan.m_vlan_info.vlan_oid; + (void)addFdbEntry(entry, port_name, fdb.type); + } + else + { + saved_fdb_entries[port_name].push_back(fdb); + } } } -bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& type) +bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const string& type) { - SWSS_LOG_ENTER(); + Port vlan; + Port port; - sai_fdb_entry_t fdb_entry; + SWSS_LOG_ENTER(); + SWSS_LOG_INFO("mac=%s bv_id=0x%lx port_name %s type %s", entry.mac.to_string().c_str(), entry.bv_id, port_name.c_str(), type.c_str()); - fdb_entry.switch_id = gSwitchId; - memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t)); - fdb_entry.bv_id = entry.bv_id; + if (!m_portsOrch->getPort(entry.bv_id, vlan)) + { + SWSS_LOG_NOTICE("addFdbEntry: Failed to locate vlan port from bv_id 0x%lx", entry.bv_id); + return false; + } - Port port; /* Retry until port is created */ - if (!m_portsOrch->getPort(entry.port_name, port)) + if (!m_portsOrch->getPort(port_name, port) || (port.m_bridge_port_id == SAI_NULL_OBJECT_ID)) { - SWSS_LOG_DEBUG("Saving a fdb entry until port %s becomes active", - entry.port_name.c_str()); - saved_fdb_entries[entry.port_name].push_back({entry, type}); + SWSS_LOG_INFO("Saving a fdb entry until port %s becomes active", port_name.c_str()); + saved_fdb_entries[port_name].push_back({entry.mac, vlan.m_vlan_info.vlan_id, type}); return true; } - /* Retry until port is added to the VLAN */ - if (!port.m_bridge_port_id) + /* Retry until port is member of vlan*/ + if (vlan.m_members.find(port_name) == vlan.m_members.end()) { - SWSS_LOG_DEBUG("Saving a fdb entry until port %s has got a bridge port ID", - entry.port_name.c_str()); - saved_fdb_entries[entry.port_name].push_back({entry, type}); + SWSS_LOG_INFO("Saving a fdb entry until port %s becomes vlan %s member", port_name.c_str(), vlan.m_alias.c_str()); + saved_fdb_entries[port_name].push_back({entry.mac, vlan.m_vlan_info.vlan_id, type}); return true; } + sai_status_t status; + sai_fdb_entry_t fdb_entry; + fdb_entry.switch_id = gSwitchId; + memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t)); + fdb_entry.bv_id = entry.bv_id; + + Port oldPort; + string oldType; + bool macUpdate = false; + auto it = m_entries.find(entry); + if(it != m_entries.end()) + { + if(port.m_bridge_port_id == it->second.bridge_port_id) + { + if((it->second.type == type)) + { + SWSS_LOG_INFO("FdbOrch: mac=%s %s port=%s type=%s is duplicate", entry.mac.to_string().c_str(), vlan.m_alias.c_str(), port_name.c_str(), type.c_str()); + return true; + } + } + + /* get existing port and type */ + oldType = it->second.type; + if (!m_portsOrch->getPortByBridgePortId(it->second.bridge_port_id, oldPort)) + { + SWSS_LOG_ERROR("Existing port 0x%lx details not found", it->second.bridge_port_id); + return false; + } + + macUpdate = true; + } + sai_attribute_t attr; vector attrs; @@ -667,12 +830,47 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& type) attr.value.s32 = SAI_PACKET_ACTION_FORWARD; attrs.push_back(attr); - if (m_entries.count(entry) != 0) // we already have such entries + string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); + + if(macUpdate) { - removeFdbEntry(entry); + /* delete and re-add fdb entry instead of update, + * as entry may age out in HW/ASIC_DB before + * update, causing the update request to fail. + */ + SWSS_LOG_INFO("MAC-Update FDB %s in %s on from-%s:to-%s from-%s:to-%s", entry.mac.to_string().c_str(), vlan.m_alias.c_str(), oldPort.m_alias.c_str(), port_name.c_str(), oldType.c_str(), type.c_str()); + status = sai_fdb_api->remove_fdb_entry(&fdb_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("FdbOrch RemoveFDBEntry: Failed to remove FDB entry. mac=%s, bv_id=0x%lx", + entry.mac.to_string().c_str(), entry.bv_id); + } + else + { + oldPort.m_fdb_count--; + m_portsOrch->setPort(oldPort.m_alias, oldPort); + if (oldPort.m_bridge_port_id == port.m_bridge_port_id) + { + port.m_fdb_count--; + m_portsOrch->setPort(port.m_alias, port); + } + vlan.m_fdb_count--; + m_portsOrch->setPort(vlan.m_alias, vlan); + (void)m_entries.erase(entry); + // Remove in StateDb + m_fdbStateTable.del(key); + + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + FdbUpdate update = {entry, port, vlan.m_vlan_info.vlan_id, type, true}; + for (auto observer: m_observers) + { + observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + } + } } + SWSS_LOG_INFO("MAC-Create %s FDB %s in %s on %s", type.c_str(), entry.mac.to_string().c_str(), vlan.m_alias.c_str(), port_name.c_str()); - sai_status_t status = sai_fdb_api->create_fdb_entry(&fdb_entry, (uint32_t)attrs.size(), attrs.data()); + status = sai_fdb_api->create_fdb_entry(&fdb_entry, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to create %s FDB %s on %s, rv:%d", @@ -680,16 +878,26 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& type) entry.port_name.c_str(), status); return false; //FIXME: it should be based on status. Some could be retried, some not } + port.m_fdb_count++; + m_portsOrch->setPort(port.m_alias, port); + vlan.m_fdb_count++; + m_portsOrch->setPort(vlan.m_alias, vlan); - SWSS_LOG_NOTICE("Storing FDB entry: [%s, 0x%" PRIx64 "] [ port: %s , type: %s]", - entry.mac.to_string().c_str(), - entry.bv_id, entry.port_name.c_str(), type.c_str()); + const FdbData fdbdata = {port.m_bridge_port_id, type}; + m_entries[entry] = fdbdata; - (void) m_entries.insert(entry); + // Write to StateDb + std::vector fvs; + fvs.push_back(FieldValueTuple("port", port_name)); + fvs.push_back(FieldValueTuple("type", type)); + m_fdbStateTable.set(key, fvs); - gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + if(!macUpdate) + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + } - FdbUpdate update = {entry, port, true}; + FdbUpdate update = {entry, port, vlan.m_vlan_info.vlan_id, type, true}; for (auto observer: m_observers) { observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); @@ -700,15 +908,36 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& type) bool FdbOrch::removeFdbEntry(const FdbEntry& entry) { + Port vlan; + Port port; + SWSS_LOG_ENTER(); - if (m_entries.count(entry) == 0) + if (!m_portsOrch->getPort(entry.bv_id, vlan)) { - SWSS_LOG_ERROR("FDB entry isn't found. mac=%s bv_id=0x%" PRIx64 ".", - entry.mac.to_string().c_str(), entry.bv_id); + SWSS_LOG_NOTICE("FdbOrch notification: Failed to locate vlan port from bv_id 0x%lx", entry.bv_id); + return false; + } + + auto it= m_entries.find(entry); + if(it == m_entries.end()) + { + SWSS_LOG_INFO("FdbOrch RemoveFDBEntry: FDB entry isn't found. mac=%s bv_id=0x%lx", entry.mac.to_string().c_str(), entry.bv_id); + + /* check whether the entry is in the saved fdb, if so delete it from there. */ + deleteFdbEntryFromSavedFDB(entry.mac, vlan.m_vlan_info.vlan_id, ""); return true; } + FdbData fdbData = it->second; + if (!m_portsOrch->getPortByBridgePortId(fdbData.bridge_port_id, port)) + { + SWSS_LOG_NOTICE("FdbOrch RemoveFDBEntry: Failed to locate port from bridge_port_id 0x%lx", fdbData.bridge_port_id); + return false; + } + + string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); + sai_status_t status; sai_fdb_entry_t fdb_entry; fdb_entry.switch_id = gSwitchId; @@ -718,20 +947,22 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry) status = sai_fdb_api->remove_fdb_entry(&fdb_entry); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove FDB entry. mac=%s, bv_id=0x%" PRIx64, + SWSS_LOG_ERROR("FdbOrch RemoveFDBEntry: Failed to remove FDB entry. mac=%s, bv_id=0x%lx", entry.mac.to_string().c_str(), entry.bv_id); return true; //FIXME: it should be based on status. Some could be retried. some not } - + port.m_fdb_count--; + m_portsOrch->setPort(port.m_alias, port); + vlan.m_fdb_count--; + m_portsOrch->setPort(vlan.m_alias, vlan); (void)m_entries.erase(entry); - gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + // Remove in StateDb + m_fdbStateTable.del(key); - Port port; - m_portsOrch->getPortByBridgePortId(entry.bv_id, port); + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); - SWSS_LOG_INFO("Notifying observers of FDB entry removal"); - FdbUpdate update = {entry, port, false}; + FdbUpdate update = {entry, port, vlan.m_vlan_info.vlan_id, fdbData.type, false}; for (auto observer: m_observers) { observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); @@ -739,3 +970,178 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry) return true; } + +bool FdbOrch::flushFdbAll(bool flush_static) +{ + sai_status_t status; + sai_attribute_t port_attr; + + if (!flush_static) + { + port_attr.id = SAI_FDB_FLUSH_ATTR_ENTRY_TYPE; + port_attr.value.s32 = SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC; + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 1, &port_attr); + } + else + { + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 0, NULL); + } + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); + return false; + } + return true; +} + +bool FdbOrch::flushFdbByPort(const string &alias, bool flush_static) +{ + sai_status_t status; + Port port; + sai_attribute_t port_attr[2]; + + if (!m_portsOrch->getPort(alias, port)) + { + SWSS_LOG_ERROR("could not locate port from alias %s", alias.c_str()); + return false; + } + + if ((port.m_bridge_port_id == SAI_NULL_OBJECT_ID) || !port.m_fdb_count) + { + /* port is not an L2 port or no macs to flush */ + return true; + } + + SWSS_LOG_NOTICE("m_bridge_port_id 0x%lx flush_static %d m_fdb_count %u", port.m_bridge_port_id, flush_static, port.m_fdb_count); + + port_attr[0].id = SAI_FDB_FLUSH_ATTR_BRIDGE_PORT_ID; + port_attr[0].value.oid = port.m_bridge_port_id; + if (!flush_static) + { + port_attr[1].id = SAI_FDB_FLUSH_ATTR_ENTRY_TYPE; + port_attr[1].value.s32 = SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC; + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 2, port_attr); + } + else + { + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 1, port_attr); + } + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); + return false; + } + return true; +} + +bool FdbOrch::flushFdbByVlan(const string &alias, bool flush_static) +{ + sai_status_t status; + Port vlan; + sai_attribute_t vlan_attr[2]; + + if (!m_portsOrch->getPort(alias, vlan)) + { + SWSS_LOG_ERROR("could not locate vlan from alias %s", alias.c_str()); + return false; + } + SWSS_LOG_NOTICE("vlan_oid 0x%lx flush_static %d", vlan.m_vlan_info.vlan_oid, flush_static); + + vlan_attr[0].id = SAI_FDB_FLUSH_ATTR_BV_ID; + vlan_attr[0].value.oid = vlan.m_vlan_info.vlan_oid; + if (!flush_static) + { + vlan_attr[1].id = SAI_FDB_FLUSH_ATTR_ENTRY_TYPE; + vlan_attr[1].value.s32 = SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC; + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 2, vlan_attr); + } + else + { + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 1, vlan_attr); + } + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); + return false; + } + + return true; +} + +bool FdbOrch::flushFdbByPortVlan(const string &port_alias, const string &vlan_alias, bool flush_static) +{ + + sai_status_t status; + Port vlan; + Port port; + sai_attribute_t port_vlan_attr[3]; + + SWSS_LOG_NOTICE("port %s vlan %s", port_alias.c_str(), vlan_alias.c_str()); + + if (!m_portsOrch->getPort(port_alias, port)) + { + SWSS_LOG_ERROR("could not locate port from alias %s", port_alias.c_str()); + return false; + } + if (!m_portsOrch->getPort(vlan_alias, vlan)) + { + SWSS_LOG_NOTICE("FdbOrch notification: Failed to locate vlan %s", vlan_alias.c_str()); + return false; + } + + if ((port.m_bridge_port_id == SAI_NULL_OBJECT_ID) || !port.m_fdb_count) + { + /* port is not an L2 port or no macs to flush */ + return true; + } + + SWSS_LOG_NOTICE("vlan_oid 0x%lx m_bridge_port_id 0x%lx flush_static %d m_fdb_count %u", vlan.m_vlan_info.vlan_oid, port.m_bridge_port_id, flush_static, port.m_fdb_count); + + port_vlan_attr[0].id = SAI_FDB_FLUSH_ATTR_BV_ID; + port_vlan_attr[0].value.oid = vlan.m_vlan_info.vlan_oid; + port_vlan_attr[1].id = SAI_FDB_FLUSH_ATTR_BRIDGE_PORT_ID; + port_vlan_attr[1].value.oid = port.m_bridge_port_id; + if (!flush_static) + { + port_vlan_attr[2].id = SAI_FDB_FLUSH_ATTR_ENTRY_TYPE; + port_vlan_attr[2].value.s32 = SAI_FDB_FLUSH_ENTRY_TYPE_DYNAMIC; + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 3, port_vlan_attr); + } + else + { + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 2, port_vlan_attr); + } + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); + return false; + } + + return true; +} + +void FdbOrch::deleteFdbEntryFromSavedFDB(const MacAddress &mac, const unsigned short &vlanId, const string portName) +{ + bool found=false; + SavedFdbEntry entry = {mac, vlanId, "static"}; + for (auto& itr: saved_fdb_entries) + { + if(portName.empty() || (portName == itr.first)) + { + auto iter = saved_fdb_entries[itr.first].begin(); + while(iter != saved_fdb_entries[itr.first].end()) + { + if (*iter == entry) + { + SWSS_LOG_INFO("FDB entry found in saved fdb. deleting... mac=%s vlan_id=0x%x", mac.to_string().c_str(), vlanId); + saved_fdb_entries[itr.first].erase(iter); + found=true; + break; + } + iter++; + } + } + if(found) + break; + } +} diff --git a/orchagent/fdborch.h b/orchagent/fdborch.h index 874bf0229f..3605f550e9 100644 --- a/orchagent/fdborch.h +++ b/orchagent/fdborch.h @@ -15,12 +15,18 @@ struct FdbEntry { return tie(mac, bv_id) < tie(other.mac, other.bv_id); } + bool operator==(const FdbEntry& other) const + { + return tie(mac, bv_id) == tie(other.mac, other.bv_id); + } }; struct FdbUpdate { FdbEntry entry; Port port; + uint16_t vlan_id; + string type; bool add; }; @@ -30,10 +36,21 @@ struct FdbFlushUpdate Port port; }; +struct FdbData +{ + sai_object_id_t bridge_port_id; + string type; +}; + struct SavedFdbEntry { - FdbEntry entry; + MacAddress mac; + unsigned short vlanId; string type; + bool operator==(const SavedFdbEntry& other) const + { + return tie(mac, vlanId) == tie(other.mac, other.vlanId); + } }; typedef unordered_map> fdb_entries_by_port_t; @@ -53,13 +70,18 @@ class FdbOrch: public Orch, public Subject, public Observer void update(sai_fdb_event_t, const sai_fdb_entry_t *, sai_object_id_t); void update(SubjectType type, void *cntx); bool getPort(const MacAddress&, uint16_t, Port&); + bool flushFdbByPortVlan(const string &, const string &, bool flush_static); + bool flushFdbByVlan(const string &, bool flush_static); + bool flushFdbByPort(const string &, bool flush_static); + bool flushFdbAll(bool flush_static); + bool removeFdbEntry(const FdbEntry&); void flushFDBEntries(sai_object_id_t bridge_port_oid, sai_object_id_t vlan_oid); void notifyObserversFDBFlush(Port &p, sai_object_id_t&); private: PortsOrch *m_portsOrch; - set m_entries; + map m_entries; fdb_entries_by_port_t saved_fdb_entries; Table m_table; Table m_fdbStateTable; @@ -70,9 +92,10 @@ class FdbOrch: public Orch, public Subject, public Observer void doTask(NotificationConsumer& consumer); void updateVlanMember(const VlanMemberUpdate&); + bool addFdbEntry(const FdbEntry&, const string&, const string&); + void deleteFdbEntryFromSavedFDB(const MacAddress &mac, const unsigned short &vlanId, const string portName=""); + void updatePortOperState(const PortOperStateUpdate&); - bool addFdbEntry(const FdbEntry&, const string&); - bool removeFdbEntry(const FdbEntry&); bool storeFdbEntryState(const FdbUpdate& update); }; diff --git a/orchagent/notifications.cpp b/orchagent/notifications.cpp index 209c03d83b..503a012889 100644 --- a/orchagent/notifications.cpp +++ b/orchagent/notifications.cpp @@ -5,7 +5,8 @@ extern "C" { #include "logger.h" #include "notifications.h" -void on_fdb_event(uint32_t count, sai_fdb_event_notification_data_t *data) + +void on_fdb_event(uint32_t count, sai_fdb_event_notification_data_t *fdbevent) { // don't use this event handler, because it runs by libsairedis in a separate thread // which causes concurrency access to the DB diff --git a/orchagent/port.h b/orchagent/port.h index fb0b8b6434..804972d490 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -26,6 +26,7 @@ namespace swss { struct VlanMemberEntry { + std::string alias; sai_object_id_t vlan_member_id; sai_vlan_tagging_mode_t vlan_mode; }; @@ -87,6 +88,7 @@ class Port VlanInfo m_vlan_info; MacAddress m_mac; sai_object_id_t m_bridge_port_id = 0; // TODO: port could have multiple bridge port IDs + sai_object_id_t m_bridge_port_admin_state = 0; // TODO: port could have multiple bridge port IDs sai_vlan_id_t m_port_vlan_id = DEFAULT_PORT_VLAN_ID; // Port VLAN ID sai_object_id_t m_rif_id = 0; sai_object_id_t m_vr_id = 0; @@ -96,8 +98,8 @@ class Port sai_object_id_t m_tunnel_id = 0; sai_object_id_t m_ingress_acl_table_group_id = 0; sai_object_id_t m_egress_acl_table_group_id = 0; - vlan_members_t m_vlan_members; sai_object_id_t m_parent_port_id = 0; + vlan_members_t m_vlan_members; uint32_t m_dependency_bitmap = 0; sai_port_oper_status_t m_oper_status = SAI_PORT_OPER_STATUS_UNKNOWN; std::set m_members; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index f806cad95f..b9e9e19455 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -236,8 +236,9 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) defaultLagFv.push_back(tuple); m_counterLagTable->set("", defaultLagFv); - /* Initialize port table */ + /* Initialize port and vlan table */ m_portTable = unique_ptr(new Table(db, APP_PORT_TABLE_NAME)); + m_vlanTable = unique_ptr
(new Table(db, APP_VLAN_TABLE_NAME)); /* Initialize gearbox */ m_gearboxTable = unique_ptr
(new Table(db, "_GEARBOX_TABLE")); @@ -257,6 +258,7 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) 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)); + notifications = unique_ptr(new swss::NotificationProducer(db, "VLANSTATE")); initGearbox(); string queueWmSha, pgWmSha; @@ -392,7 +394,19 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) } m_default1QBridge = attrs[0].value.oid; - m_defaultVlan = attrs[1].value.oid; + m_defaultVlan_ObjId = attrs[1].value.oid; + + memset(&attr, 0x00, sizeof(attr)); + attr.id = SAI_VLAN_ATTR_VLAN_ID; + + status = sai_vlan_api->get_vlan_attribute(m_defaultVlan_ObjId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get default VLAN ID, rv:%d", status); + throw runtime_error("PortsOrch initialization failure"); + } + + m_defaultVlan_Id = attr.value.u16; removeDefaultVlanMembers(); removeDefaultBridgePorts(); @@ -414,7 +428,7 @@ void PortsOrch::removeDefaultVlanMembers() attr.value.objlist.count = (uint32_t)vlan_member_list.size(); attr.value.objlist.list = vlan_member_list.data(); - sai_status_t status = sai_vlan_api->get_vlan_attribute(m_defaultVlan, 1, &attr); + sai_status_t status = sai_vlan_api->get_vlan_attribute(m_defaultVlan_ObjId, 1, &attr); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get VLAN member list in default VLAN, rv:%d", status); @@ -573,34 +587,12 @@ bool PortsOrch::getPort(sai_object_id_t id, Port &port) { SWSS_LOG_ENTER(); - for (const auto& portIter: m_portList) - { - switch (portIter.second.m_type) - { - case Port::PHY: - if(portIter.second.m_port_id == id) - { - port = portIter.second; - return true; - } - break; - case Port::LAG: - if(portIter.second.m_lag_id == id) - { - port = portIter.second; - return true; - } - break; - case Port::VLAN: - if (portIter.second.m_vlan_info.vlan_oid == id) - { - port = portIter.second; - return true; - } - break; - default: - continue; - } + auto itr = portOidToName.find(id); + if (itr == portOidToName.end()) + return false; + else { + getPort(itr->second, port); + return true; } return false; @@ -622,13 +614,12 @@ bool PortsOrch::getPortByBridgePortId(sai_object_id_t bridge_port_id, Port &port { SWSS_LOG_ENTER(); - for (auto &it: m_portList) - { - if (it.second.m_bridge_port_id == bridge_port_id) - { - port = it.second; - return true; - } + auto itr = portOidToName.find(bridge_port_id); + if (itr == portOidToName.end()) + return false; + else { + getPort(itr->second, port); + return true; } return false; @@ -1767,6 +1758,21 @@ void PortsOrch::updateDbPortOperStatus(const Port& port, sai_port_oper_status_t m_portTable->set(port.m_alias, tuples); } +void PortsOrch::updateDbVlanOperStatus(const Port& vlan, string status) const +{ + SWSS_LOG_NOTICE("vlan %s status %s", vlan.m_alias.c_str(), status.c_str()); + + vector tuples; + FieldValueTuple tuple("oper_status", status); + tuples.push_back(tuple); + + std::vector entry; + + SWSS_LOG_NOTICE("sending oper state notification to VlanMgr"); + + notifications->send(status, vlan.m_alias, entry); +} + bool PortsOrch::addPort(const set &lane_set, uint32_t speed, int an, string fec_mode) { SWSS_LOG_ENTER(); @@ -1882,6 +1888,7 @@ bool PortsOrch::initPort(const string &alias, const int index, const set &l /* Add port to port list */ m_portList[alias] = p; + portOidToName[id] = alias; m_port_ref_count[alias] = 0; m_portOidToIndex[id] = index; @@ -2827,24 +2834,23 @@ void PortsOrch::doVlanMemberTask(Consumer &consumer) } else if (op == DEL_COMMAND) { + int ret = true; if (vlan.m_members.find(port_alias) != vlan.m_members.end()) { - if (removeVlanMember(vlan, port)) - { - if (port.m_vlan_members.empty()) - { - removeBridgePort(port); - } - it = consumer.m_toSync.erase(it); - } - else - { - it++; - } + ret = removeVlanMember(vlan, port); } - else - /* Cannot locate the VLAN */ + if ((ret) && m_portVlanMember[port.m_alias].empty()) + { + ret = removeBridgePort(port); + } + if (ret) + { it = consumer.m_toSync.erase(it); + } + else + { + it++; + } } else { @@ -2870,7 +2876,10 @@ void PortsOrch::doLagTask(Consumer &consumer) { // Retrieve attributes uint32_t mtu = 0; + + string oper_status; string learn_mode; + bool operation_status_changed = false; string operation_status; for (auto i : kfvFieldsValues(t)) @@ -2892,19 +2901,22 @@ void PortsOrch::doLagTask(Consumer &consumer) it++; continue; } - } - } - // Create a new LAG when the new alias comes - if (m_portList.find(alias) == m_portList.end()) - { - if (!addLag(alias)) - { - it++; - continue; + gNeighOrch->ifChangeInformNextHop(alias, + (operation_status == "up")); + Port lag; + if (getPort(alias, lag)) + { + operation_status_changed = + (string_oper_status.at(operation_status) != + lag.m_oper_status); + } + oper_status = fvValue(i); } } + auto status = addLag(alias); + // Process attributes Port l; if (!getPort(alias, l)) @@ -2913,14 +2925,20 @@ void PortsOrch::doLagTask(Consumer &consumer) } else { + if (!operation_status.empty()) { - updatePortOperStatus(l, string_oper_status.at(operation_status)); - + l.m_oper_status = string_oper_status.at(operation_status); m_portList[alias] = l; } - - if (mtu != 0) + if (operation_status_changed) + { + PortOperStateUpdate update; + update.port = l; + update.operStatus = string_oper_status.at(operation_status); + notify(SUBJECT_TYPE_PORT_OPER_STATE_CHANGE, static_cast(&update)); + } + if ((mtu != 0) && (mtu != l.m_mtu)) { l.m_mtu = mtu; m_portList[alias] = l; @@ -2932,6 +2950,16 @@ void PortsOrch::doLagTask(Consumer &consumer) updateChildPortsMtu(l, mtu); } + if (!oper_status.empty()) + { + if (oper_status != "up") + gFdbOrch->flushFdbByPort(alias, 0); + + /* Trigger status updated only if it is set */ + sai_port_oper_status_t status = (oper_status == "up") ? SAI_PORT_OPER_STATUS_UP : SAI_PORT_OPER_STATUS_DOWN; + updateLagOperStatus(l, status); + } + if (!learn_mode.empty() && (l.m_learn_mode != learn_mode)) { if (l.m_bridge_port_id != SAI_NULL_OBJECT_ID) @@ -2958,8 +2986,15 @@ void PortsOrch::doLagTask(Consumer &consumer) } } } - - it = consumer.m_toSync.erase(it); + if (!status) + { + it++; + continue; + } + else + { + it = consumer.m_toSync.erase(it); + } } else if (op == DEL_COMMAND) { @@ -3382,8 +3417,40 @@ bool PortsOrch::addBridgePort(Port &port) { SWSS_LOG_ENTER(); + if (port.m_rif_id) + { + SWSS_LOG_ERROR("Adding router interface %s as bridge port is not allowed", port.m_alias.c_str()); + return false; + } if (port.m_bridge_port_id != SAI_NULL_OBJECT_ID) { + /* If the port is being added to the first VLAN, + * set bridge port admin status to UP. + * This can happen if the port was just removed from + * last VLAN and fdb flush is still in progress. + */ + if (m_portVlanMember[port.m_alias].empty()) + { + sai_attribute_t attr; + attr.id = SAI_BRIDGE_PORT_ATTR_ADMIN_STATE; + attr.value.booldata = true; + + sai_status_t status = sai_bridge_api->set_bridge_port_attribute(port.m_bridge_port_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set bridge port %s admin status to UP, rv:%d", + port.m_alias.c_str(), status); + return false; + } + port.m_bridge_port_admin_state = true; + m_portList[port.m_alias] = port; + if (!setHostIntfsStripTag(port, SAI_HOSTIF_VLAN_TAG_KEEP)) + { + SWSS_LOG_ERROR("Failed to set %s for hostif of port %s", + hostif_vlan_tag[SAI_HOSTIF_VLAN_TAG_KEEP], port.m_alias.c_str()); + return false; + } + } return true; } @@ -3456,6 +3523,7 @@ bool PortsOrch::addBridgePort(Port &port) port.m_alias.c_str(), status); return false; } + port.m_bridge_port_admin_state = true; if (!setHostIntfsStripTag(port, SAI_HOSTIF_VLAN_TAG_KEEP)) { @@ -3464,11 +3532,13 @@ bool PortsOrch::addBridgePort(Port &port) return false; } m_portList[port.m_alias] = port; + portOidToName[port.m_bridge_port_id] = port.m_alias; SWSS_LOG_NOTICE("Add bridge port %s to default 1Q bridge", port.m_alias.c_str()); return true; } + bool PortsOrch::removeBridgePort(Port &port) { SWSS_LOG_ENTER(); @@ -3477,38 +3547,45 @@ bool PortsOrch::removeBridgePort(Port &port) { return true; } - /* Set bridge port admin status to DOWN */ - sai_attribute_t attr; - attr.id = SAI_BRIDGE_PORT_ATTR_ADMIN_STATE; - attr.value.booldata = false; - - sai_status_t status = sai_bridge_api->set_bridge_port_attribute(port.m_bridge_port_id, &attr); - if (status != SAI_STATUS_SUCCESS) + if (port.m_bridge_port_admin_state) { - SWSS_LOG_ERROR("Failed to set bridge port %s admin status to DOWN, rv:%d", - port.m_alias.c_str(), status); - return false; + /* Set bridge port admin status to DOWN */ + sai_attribute_t attr; + attr.id = SAI_BRIDGE_PORT_ATTR_ADMIN_STATE; + attr.value.booldata = false; + + sai_status_t status = sai_bridge_api->set_bridge_port_attribute(port.m_bridge_port_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set bridge port %s admin status to DOWN, rv:%d", + port.m_alias.c_str(), status); + return false; + } + port.m_bridge_port_admin_state = false; + m_portList[port.m_alias] = port; + + if (!setHostIntfsStripTag(port, SAI_HOSTIF_VLAN_TAG_STRIP)) + { + SWSS_LOG_ERROR("Failed to set %s for hostif of port %s", + hostif_vlan_tag[SAI_HOSTIF_VLAN_TAG_STRIP], port.m_alias.c_str()); + return false; + } } - if (!setHostIntfsStripTag(port, SAI_HOSTIF_VLAN_TAG_STRIP)) - { - SWSS_LOG_ERROR("Failed to set %s for hostif of port %s", - hostif_vlan_tag[SAI_HOSTIF_VLAN_TAG_STRIP], port.m_alias.c_str()); + if (port.m_fdb_count != 0) { + //SWSS_LOG_NOTICE("Port still has fdb entries, will not remove bridge port for %s", port.m_alias.c_str()); return false; } - //Flush the FDB entires corresponding to the port - gFdbOrch->flushFDBEntries(port.m_bridge_port_id, SAI_NULL_OBJECT_ID); - SWSS_LOG_INFO("Flush FDB entries for port %s", port.m_alias.c_str()); - /* Remove bridge port */ - status = sai_bridge_api->remove_bridge_port(port.m_bridge_port_id); + sai_status_t status = sai_bridge_api->remove_bridge_port(port.m_bridge_port_id); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to remove bridge port %s from default 1Q bridge, rv:%d", port.m_alias.c_str(), status); return false; } + portOidToName.erase(port.m_bridge_port_id); port.m_bridge_port_id = SAI_NULL_OBJECT_ID; SWSS_LOG_NOTICE("Remove bridge port %s from default 1Q bridge", port.m_alias.c_str()); @@ -3561,7 +3638,17 @@ bool PortsOrch::addVlan(string vlan_alias) sai_attribute_t attr; attr.id = SAI_VLAN_ATTR_VLAN_ID; attr.value.u16 = vlan_id; - sai_status_t status = sai_vlan_api->create_vlan(&vlan_oid, gSwitchId, 1, &attr); + sai_status_t status = SAI_STATUS_SUCCESS; + + /* Do not create default VLAN. It is already created by default */ + if (vlan_id != m_defaultVlan_Id) + { + status = sai_vlan_api->create_vlan(&vlan_oid, gSwitchId, 1, &attr); + } + else + { + vlan_oid = m_defaultVlan_ObjId; /* use the default VLAN object id instead */ + } if (status != SAI_STATUS_SUCCESS) { @@ -3576,6 +3663,7 @@ bool PortsOrch::addVlan(string vlan_alias) vlan.m_vlan_info.vlan_id = vlan_id; vlan.m_members = set(); m_portList[vlan_alias] = vlan; + portOidToName[vlan_oid] = vlan_alias; m_port_ref_count[vlan_alias] = 0; return true; @@ -3584,6 +3672,7 @@ bool PortsOrch::addVlan(string vlan_alias) bool PortsOrch::removeVlan(Port vlan) { SWSS_LOG_ENTER(); + if (m_port_ref_count[vlan.m_alias] > 0) { SWSS_LOG_ERROR("Failed to remove ref count %d VLAN %s", @@ -3591,6 +3680,12 @@ bool PortsOrch::removeVlan(Port vlan) vlan.m_alias.c_str()); return false; } + /* If there are still fdb entries associated with the VLAN, + return false for retry */ + if (vlan.m_fdb_count > 0) + { + return false; + } /* Vlan removing is not allowed when the VLAN still has members */ if (vlan.m_members.size() > 0) @@ -3607,12 +3702,16 @@ bool PortsOrch::removeVlan(Port vlan) return false; } - sai_status_t status = sai_vlan_api->remove_vlan(vlan.m_vlan_info.vlan_oid); - if (status != SAI_STATUS_SUCCESS) + /* Do not delete default VLAN from driver, but clear internal state */ + if (vlan.m_vlan_info.vlan_id != m_defaultVlan_Id) { + sai_status_t status = sai_vlan_api->remove_vlan(vlan.m_vlan_info.vlan_oid); + if (status != SAI_STATUS_SUCCESS) + { SWSS_LOG_ERROR("Failed to remove VLAN %s vid:%hu", vlan.m_alias.c_str(), vlan.m_vlan_info.vlan_id); return false; + } } removeAclTableGroup(vlan); @@ -3620,6 +3719,7 @@ bool PortsOrch::removeVlan(Port vlan) SWSS_LOG_NOTICE("Remove VLAN %s vid:%hu", vlan.m_alias.c_str(), vlan.m_vlan_info.vlan_id); + portOidToName.erase(vlan.m_vlan_info.vlan_oid); m_portList.erase(vlan.m_alias); m_port_ref_count.erase(vlan.m_alias); @@ -3690,10 +3790,20 @@ bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode) } /* a physical port may join multiple vlans */ - VlanMemberEntry vme = {vlan_member_id, sai_tagging_mode}; + VlanMemberEntry vme = {vlan.m_alias, vlan_member_id, sai_tagging_mode}; port.m_vlan_members[vlan.m_vlan_info.vlan_id] = vme; + m_portVlanMember[port.m_alias][vlan.m_vlan_info.vlan_id] = vme; m_portList[port.m_alias] = port; vlan.m_members.insert(port.m_alias); + if (port.m_oper_status == SAI_PORT_OPER_STATUS_UP) + { + auto old_count = vlan.m_up_member_count; + vlan.m_up_member_count++; + if (old_count == 0) + { + updateDbVlanOperStatus(vlan, "up"); + } + } m_portList[vlan.m_alias] = vlan; VlanMemberUpdate update = { vlan, port, true }; @@ -3711,7 +3821,7 @@ bool PortsOrch::removeVlanMember(Port &vlan, Port &port) auto vlan_member = port.m_vlan_members.find(vlan.m_vlan_info.vlan_id); /* Assert the port belongs to this VLAN */ - assert (vlan_member != port.m_vlan_members.end()); + assert (vlan_member != m_portVlanMember[port.m_alias].end()); sai_tagging_mode = vlan_member->second.vlan_mode; vlan_member_id = vlan_member->second.vlan_member_id; @@ -3723,6 +3833,10 @@ bool PortsOrch::removeVlanMember(Port &vlan, Port &port) return false; } port.m_vlan_members.erase(vlan_member); + auto vlan_member_rev = m_portVlanMember[port.m_alias].find(vlan.m_vlan_info.vlan_id); + m_portVlanMember[port.m_alias].erase(vlan_member_rev); + if (m_portVlanMember[port.m_alias].empty()) + m_portVlanMember.erase(port.m_alias); SWSS_LOG_NOTICE("Remove member %s from VLAN %s lid:%hx vmid:%" PRIx64, port.m_alias.c_str(), vlan.m_alias.c_str(), vlan.m_vlan_info.vlan_id, vlan_member_id); @@ -3737,6 +3851,14 @@ bool PortsOrch::removeVlanMember(Port &vlan, Port &port) m_portList[port.m_alias] = port; vlan.m_members.erase(port.m_alias); + if (port.m_oper_status == SAI_PORT_OPER_STATUS_UP) + { + vlan.m_up_member_count--; + if (vlan.m_up_member_count == 0) + { + updateDbVlanOperStatus(vlan, "down"); + } + } m_portList[vlan.m_alias] = vlan; VlanMemberUpdate update = { vlan, port, false }; @@ -3757,6 +3879,17 @@ bool PortsOrch::addLag(string lag_alias) { SWSS_LOG_ENTER(); + auto lagport = m_portList.find(lag_alias); + if (lagport != m_portList.end()) + { + if ((m_portList[lag_alias].m_bridge_port_id != SAI_NULL_OBJECT_ID) && + (m_portVlanMember[lag_alias].empty())) + { + return false; + } + return true; + } + sai_object_id_t lag_id; sai_status_t status = sai_lag_api->create_lag(&lag_id, gSwitchId, 0, NULL); @@ -3772,6 +3905,7 @@ bool PortsOrch::addLag(string lag_alias) lag.m_lag_id = lag_id; lag.m_members = set(); m_portList[lag_alias] = lag; + portOidToName[lag_id] = lag_alias; m_port_ref_count[lag_alias] = 0; PortUpdate update = { lag, true }; @@ -3803,11 +3937,15 @@ bool PortsOrch::removeLag(Port lag) SWSS_LOG_ERROR("Failed to remove non-empty LAG %s", lag.m_alias.c_str()); return false; } - if (lag.m_vlan_members.size() > 0) + if (m_portVlanMember[lag.m_alias].size() > 0) { SWSS_LOG_ERROR("Failed to remove LAG %s, it is still in VLAN", lag.m_alias.c_str()); return false; } + if (lag.m_bridge_port_id != SAI_NULL_OBJECT_ID) + { + return false; + } sai_status_t status = sai_lag_api->remove_lag(lag.m_lag_id); if (status != SAI_STATUS_SUCCESS) @@ -3816,8 +3954,10 @@ bool PortsOrch::removeLag(Port lag) return false; } + SWSS_LOG_NOTICE("Remove LAG %s lid:%" PRIx64, lag.m_alias.c_str(), lag.m_lag_id); + portOidToName.erase(lag.m_lag_id); m_portList.erase(lag.m_alias); m_port_ref_count.erase(lag.m_alias); @@ -4245,6 +4385,25 @@ void PortsOrch::updatePortOperStatus(Port &port, sai_port_oper_status_t status) } bool isUp = status == SAI_PORT_OPER_STATUS_UP; + + for(auto vlan_member: m_portVlanMember[port.m_alias]) + { + auto Vlan = m_portList[vlan_member.second.alias]; + auto old_count = Vlan.m_up_member_count; + isUp ? Vlan.m_up_member_count++ : Vlan.m_up_member_count--; + if (Vlan.m_up_member_count == 0) + { + updateDbVlanOperStatus(Vlan, "down"); + } + else if ((old_count == 0) && (Vlan.m_up_member_count == 1)) + { + updateDbVlanOperStatus(Vlan, "up"); + } + SWSS_LOG_NOTICE("Vlan %s Port %s state %s m_up_member_count %d", + vlan_member.second.alias.c_str(), port.m_alias.c_str(), oper_status_strings.at(port.m_oper_status).c_str(), Vlan.m_up_member_count); + + m_portList[Vlan.m_alias] = Vlan; + } if (port.m_type == Port::PHY) { if (!setHostIntfsOperStatus(port, isUp)) @@ -4265,8 +4424,50 @@ void PortsOrch::updatePortOperStatus(Port &port, sai_port_oper_status_t status) } } + /* flush all dynamic FDB on this port */ + if (status == SAI_PORT_OPER_STATUS_DOWN) + { + gFdbOrch->flushFdbByPort(port.m_alias, 0); + } + PortOperStateUpdate update = {port, status}; notify(SUBJECT_TYPE_PORT_OPER_STATE_CHANGE, static_cast(&update)); + +} + +void PortsOrch::updateLagOperStatus(Port &port, sai_port_oper_status_t status) +{ + if (status == port.m_oper_status) + { + return ; + } + SWSS_LOG_NOTICE("Port %s oper state set from %s to %s", + port.m_alias.c_str(), oper_status_strings.at(port.m_oper_status).c_str(), + oper_status_strings.at(status).c_str()); + + port.m_oper_status = status; + m_portList[port.m_alias] = port; + + bool isUp = status == SAI_PORT_OPER_STATUS_UP; + + for(auto vlan_member: m_portVlanMember[port.m_alias]) + { + auto Vlan = m_portList[vlan_member.second.alias]; + auto old_count = Vlan.m_up_member_count; + isUp ? Vlan.m_up_member_count++ : Vlan.m_up_member_count--; + if (Vlan.m_up_member_count == 0) + { + updateDbVlanOperStatus(Vlan, "down"); + } + else if ((old_count == 0) && (Vlan.m_up_member_count == 1)) + { + updateDbVlanOperStatus(Vlan, "up"); + } + SWSS_LOG_NOTICE("Vlan %s Port %s state %s m_up_member_count %d", + vlan_member.second.alias.c_str(), port.m_alias.c_str(), oper_status_strings.at(port.m_oper_status).c_str(), Vlan.m_up_member_count); + + m_portList[Vlan.m_alias] = Vlan; + } } /* diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 42cb7278b8..a2524d6531 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -9,6 +9,7 @@ #include "observer.h" #include "macaddress.h" #include "producertable.h" +#include "notificationproducer.h" #include "flex_counter_manager.h" #include "gearboxutils.h" #include "saihelper.h" @@ -91,11 +92,14 @@ class PortsOrch : public Orch, public Subject void decreasePortRefCount(const string &alias); bool getPortByBridgePortId(sai_object_id_t bridge_port_id, Port &port); void setPort(string alias, Port port); + void erasePort(string alias); void getCpuPort(Port &port); bool getVlanByVlanId(sai_vlan_id_t vlan_id, Port &vlan); bool setHostIntfsOperStatus(const Port& port, bool up) const; void updateDbPortOperStatus(const Port& port, sai_port_oper_status_t status) const; + void updateDbVlanOperStatus(const Port& port, string status) const; + bool createBindAclTableGroup(sai_object_id_t port_oid, sai_object_id_t acl_table_oid, sai_object_id_t &group_oid, @@ -141,6 +145,7 @@ class PortsOrch : public Orch, public Subject unique_ptr
m_counterTable; unique_ptr
m_counterLagTable; unique_ptr
m_portTable; + unique_ptr
m_vlanTable; unique_ptr
m_gearboxTable; unique_ptr
m_queueTable; unique_ptr
m_queuePortTable; @@ -151,6 +156,7 @@ class PortsOrch : public Orch, public Subject unique_ptr
m_pgIndexTable; unique_ptr m_flexCounterTable; unique_ptr m_flexCounterGroupTable; + unique_ptr notifications; std::string getQueueWatermarkFlexCounterTableKey(std::string s); std::string getPriorityGroupWatermarkFlexCounterTableKey(std::string s); @@ -169,7 +175,8 @@ class PortsOrch : public Orch, public Subject Port m_cpuPort; // TODO: Add Bridge/Vlan class sai_object_id_t m_default1QBridge; - sai_object_id_t m_defaultVlan; + sai_object_id_t m_defaultVlan_ObjId; + sai_vlan_id_t m_defaultVlan_Id; typedef enum { @@ -197,6 +204,15 @@ class PortsOrch : public Orch, public Subject map, sai_object_id_t> m_portListLaneMap; map, tuple> m_lanesAliasSpeedMap; map m_portList; + map m_portVlanMember; + + /* mapping from SAI object ID to Name for faster + retrieval of Port/VLAN from object ID for events + + coming from SAI + */ + unordered_map portOidToName; + unordered_map m_portOidToIndex; map m_port_ref_count; unordered_set m_pendingPortSet; @@ -279,6 +295,7 @@ class PortsOrch : public Orch, public Subject bool setPortSerdesAttribute(sai_object_id_t port_id, sai_attr_id_t attr_id, vector &serdes_val); + void updateLagOperStatus(Port &port, sai_port_oper_status_t status); bool getSaiAclBindPointType(Port::Type type, sai_acl_bind_point_type_t &sai_acl_bind_type); void initGearbox(); diff --git a/tests/conftest.py b/tests/conftest.py index fe86b58053..203b8da8ff 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1079,6 +1079,24 @@ def setup_db(self): self.cdb = swsscommon.DBConnector(4, self.redis_sock, 0) self.sdb = swsscommon.DBConnector(6, self.redis_sock, 0) + def getSwitchOid(self): + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_SWITCH") + keys = tbl.getKeys() + return str(keys[0]) + + def getVlanOid(self, vlanId): + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_oid = None + keys = tbl.getKeys() + for k in keys: + (status, fvs) = tbl.get(k) + assert status == True, "Could not read vlan from DB" + for fv in fvs: + if fv[0] == "SAI_VLAN_ATTR_VLAN_ID" and fv[1] == str(vlanId): + vlan_oid = str(k) + break + return vlan_oid + # deps: acl_portchannel, fdb def getCrmCounterValue(self, key, counter): counters_db = swsscommon.DBConnector(swsscommon.COUNTERS_DB, self.redis_sock, 0) diff --git a/tests/test_fdb_clear_all.py b/tests/test_fdb_clear_all.py new file mode 100644 index 0000000000..d32db2185c --- /dev/null +++ b/tests/test_fdb_clear_all.py @@ -0,0 +1,92 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +def test_FDBClearAll(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a FDB entry in Application DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", str(dvs.getVlanOid("2")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + + assert ok, str(extra) + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + # check that the FDB entry was flushed + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry was not flushed" + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + ) + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan("2") + diff --git a/tests/test_fdb_clear_port.py b/tests/test_fdb_clear_port.py new file mode 100644 index 0000000000..c8568c6c36 --- /dev/null +++ b/tests/test_fdb_clear_port.py @@ -0,0 +1,92 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +def test_FDBClearPort(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a FDB entry in Application DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", str(dvs.getVlanOid("2")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + + assert ok, str(extra) + + dvs.runcmd("sonic-clear fdb port Ethernet0") + time.sleep(2) + # check that the FDB entry was flushed + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry was not flushed" + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + ) + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan("2") + diff --git a/tests/test_fdb_clear_vlan.py b/tests/test_fdb_clear_vlan.py new file mode 100644 index 0000000000..0bb99ae1ee --- /dev/null +++ b/tests/test_fdb_clear_vlan.py @@ -0,0 +1,92 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +def test_FDBClearVlan(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a FDB entry in Application DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", str(dvs.getVlanOid("2")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + + assert ok, str(extra) + + dvs.runcmd("sonic-clear fdb vlan Vlan2") + time.sleep(2) + # check that the FDB entry was flushed + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry was not flushed" + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + ) + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan("2") + diff --git a/tests/test_fdb_dynamic_mac_add.py b/tests/test_fdb_dynamic_mac_add.py new file mode 100644 index 0000000000..83898e5304 --- /dev/null +++ b/tests/test_fdb_dynamic_mac_add.py @@ -0,0 +1,90 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion +import pytest + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +@pytest.mark.dev_sanity +def test_FDBAddDynamic(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a FDB entry in Application DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", str(dvs.getVlanOid("2")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + + assert ok, str(extra) + + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + ) + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan("2") + diff --git a/tests/test_fdb_dynamic_mac_del.py b/tests/test_fdb_dynamic_mac_del.py new file mode 100644 index 0000000000..c4ce510022 --- /dev/null +++ b/tests/test_fdb_dynamic_mac_del.py @@ -0,0 +1,93 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion +import pytest + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +@pytest.mark.dev_sanity +def test_FDBDelDynamic(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a FDB entry in Application DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", str(dvs.getVlanOid("2")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + + assert ok, str(extra) + + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + ) + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry was not deleted" + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan("2") + diff --git a/tests/test_fdb_flush_port_vlan_remove.py b/tests/test_fdb_flush_port_vlan_remove.py new file mode 100644 index 0000000000..21fa3d5a43 --- /dev/null +++ b/tests/test_fdb_flush_port_vlan_remove.py @@ -0,0 +1,91 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +def test_FDBFlushPortRemoveVlan(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a FDB entry in Application DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", str(dvs.getVlanOid("2")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + + assert ok, str(extra) + + dvs.remove_vlan_member("2", "Ethernet0") + time.sleep(2) + # check that the FDB entry was flushed + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry was not flushed" + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + ) + dvs.remove_vlan("2") + diff --git a/tests/test_fdb_mac_readd_port_vlan_add.py b/tests/test_fdb_mac_readd_port_vlan_add.py new file mode 100644 index 0000000000..4e5fda4a5b --- /dev/null +++ b/tests/test_fdb_mac_readd_port_vlan_add.py @@ -0,0 +1,86 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +def test_FDBReaddPortAddedBackToVlan(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a static FDB entry + dvs.runcmd("config mac add 52:54:00:25:06:E9 2 Ethernet0") + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", str(dvs.getVlanOid("2")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + + assert ok, str(extra) + + dvs.remove_vlan_member("2", "Ethernet0") + time.sleep(2) + # check that the FDB entry was flushed + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry was not flushed" + dvs.create_vlan_member("2", "Ethernet0") + time.sleep(2) + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") != 0, "The fdb entry wasn't inserted to ASIC" + dvs.runcmd("config mac del 52:54:00:25:06:E9 2") + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan("2") + diff --git a/tests/test_fdb_negative.py b/tests/test_fdb_negative.py new file mode 100644 index 0000000000..4406050a42 --- /dev/null +++ b/tests/test_fdb_negative.py @@ -0,0 +1,627 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +import pytest +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def remove_entry_tbl(db, table, key): + tbl = swsscommon.Table(db, table) + tbl._del(key) + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +def test_negativeFDB(dvs, testlog): + dvs.setup_db() + + #dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + #Find switch_id + switch_id = dvs.getSwitchOid() + print("Switch_id="+str(switch_id)) + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + dvs.create_vlan_member("2", "Ethernet4") + # Find the vlan_oid_2 to be used in DB communications + vlan_oid_2 = dvs.getVlanOid("2") + assert vlan_oid_2 is not None, "Could not find Vlan_oid" + print("VLan-2 vlan_oid="+str(vlan_oid_2)) + + + # create vlan + dvs.create_vlan("4") + dvs.create_vlan_member("4", "Ethernet8") + # Find the vlan_oid_4 to be used in DB communications + vlan_oid_4 = dvs.getVlanOid("4") + assert vlan_oid_4 is not None, "Could not find Vlan_oid" + print("VLan-4 vlan_oid="+str(vlan_oid_4)) + + + dvs.create_vlan("10") + dvs.create_vlan_member("10", "Ethernet12") + # Find the vlan_oid_10 to be used in DB communications + vlan_oid_10 = dvs.getVlanOid("10") + assert vlan_oid_10 is not None, "Could not find Vlan_oid" + print("VLan-10 vlan_oid="+str(vlan_oid_10)) + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 3, "The Vlan2/Vlan4 wasn't created" + assert bp_after - bp_before == 4, "The bridge port wasn't created" + assert vm_after - vm_before == 4, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + #dvs.runcmd("swssloglevel -l DEBUG -c orchagent") + #dvs.runcmd("swssloglevel -l DEBUG -c vlanmgrd") + + print("NEG1 - Add MAC address to an out of range vlan and later delete it") + mac = "52:54:00:25:06:E9" + #dvs.runcmd("config mac add " + mac.lower() + " 3 Ethernet0") + print("ACTION: Creating static FDB Vlan33333|"+mac.lower()+"|Ethernet0 in CONFIG-DB") + create_entry_tbl( + dvs.cdb, + "FDB", "Vlan33333|"+mac.lower(), + [ + ("port", "Ethernet0"), + ] + ) + time.sleep(2) + + # check that the FDB entry was added in Config DB + print("CHECK: Static FDB Vlan33333:"+mac.lower()+":Ethernet0 is created in Config-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.cdb, "FDB", + "Vlan33333\|"+mac.lower(), + [("port", "Ethernet0")] + ) + assert mac1_found, str(extra) + print("CONFIRM: Static FDB Vlan33333:"+mac.lower()+":Ethernet0 is created in Config-DB") + + # check that the FDB entry was not added in APP DB + print("CHECK: Static FDB Vlan33333:"+mac.lower()+":Ethernet0 is not created in APP-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.pdb, "FDB_TABLE", + "Vlan33333:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "static"), + ] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: Static FDB Vlan33333:"+mac.lower()+":Ethernet0 is not created in APP-DB") + + print("ACTION: Deleting Static FDB Vlan33333:"+mac.lower()+":Ethernet0") + remove_entry_tbl(dvs.cdb, "FDB", "Vlan33333|"+mac.lower()) + time.sleep(2) + + #Check the mac is removed from config-db + print("CHECK: Static FDB Vlan33333:"+mac.lower()+":Ethernet0 is deleted from Config-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.cdb, "FDB", + "Vlan33333\|"+mac.lower(), + [("port", "Ethernet0")] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: Static FDB Vlan33333:"+mac.lower()+":Ethernet0 is deleted from Config-DB") + + print("NEG2 - Add MAC address to a vlan which does not exist and later delete it") + mac = "52:54:00:25:06:E9" + #dvs.runcmd("config mac add " + mac.lower() + " 3 Ethernet0") + print("ACTION: Creating static FDB Vlan3:"+mac.lower()+":Ethernet0 in CONFIG-DB") + create_entry_tbl( + dvs.cdb, + "FDB", "Vlan3|"+mac.lower(), + [ + ("port", "Ethernet0"), + ] + ) + time.sleep(2) + + # check that the FDB entry was added in Config DB + print("CHECK: Static FDB Vlan3:"+mac.lower()+":Ethernet0 is created in Config-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.cdb, "FDB", + "Vlan3\|"+mac.lower(), + [("port", "Ethernet0")] + ) + assert mac1_found, str(extra) + print("CONFIRM: Static FDB Vlan3:"+mac.lower()+":Ethernet0 is created in Config-DB") + + # check that the FDB entry was added in APP DB + print("CHECK: Static FDB Vlan3:"+mac.lower()+":Ethernet0 is created in APP-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.pdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "static"), + ] + ) + assert mac1_found, str(extra) + print("CONFIRM: Static FDB Vlan3:"+mac.lower()+":Ethernet0 is created in APP-DB") + + # check that the FDB entry is not inserted into ASIC DB + print("CHECK: Static FDB Vlan3:"+mac.lower()+":Ethernet0 is not created in ASIC-DB") + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac.lower())], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC")] + ) + assert ok == False, str(extra) + print("CONFIRM: Static FDB Vlan3:"+mac.lower()+":Ethernet0 is not created in ASIC-DB") + + print("ACTION: Deleting Static FDB Vlan3:"+mac.lower()+"Ethernet0") + remove_entry_tbl(dvs.cdb, "FDB", "Vlan3|"+mac.lower()) + time.sleep(2) + + #Check the mac is removed from config-db + print("CHECK: Static FDB Vlan3:"+mac.lower()+":Ethernet0 is deleted from Config-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.cdb, "FDB", + "Vlan3\|"+mac.lower(), + [("port", "Ethernet0")] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: Static FDB Vlan3:"+mac.lower()+":Ethernet0 is deleted from Config-DB") + + # check that the FDB entry is removed from APP DB + print("CHECK: Static FDB Vlan3:"+mac.lower()+":Ethernet0 is deleted from APP-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.pdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "static"), + ] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: Static FDB Vlan3:"+mac.lower()+":Ethernet0 is deleted from APP-DB") + + + print("NEG3 - Add MAC address to an invalid port which does not exist and later delete it") + mac = "52:54:00:25:06:E9" + #dvs.runcmd("config mac add " + mac.lower() + " 3 Ethernet0") + print("ACTION: Creating static FDB Vlan2:"+mac.lower()+":Port0 in CONFIG-DB") + create_entry_tbl( + dvs.cdb, + "FDB", "Vlan2|"+mac.lower(), + [ + ("port", "Port0"), + ] + ) + time.sleep(2) + + + # check that the FDB entry was added in Config DB + print("CHECK: Static FDB Vlan2:"+mac.lower()+":Port0 is created in Config-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.cdb, "FDB", + "Vlan2\|"+mac.lower(), + [("port", "Port0")] + ) + assert mac1_found, str(extra) + print("CONFIRM: Static FDB Vlan2:"+mac.lower()+":Port0 is created in Config-DB") + + # check that the FDB entry was added in APP DB + print("CHECK: Static FDB Vlan2:"+mac.lower()+":Port0 is created in APP-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.pdb, "FDB_TABLE", + "Vlan2:"+mac.lower(), + [("port", "Port0"), + ("type", "static"), + ] + ) + assert mac1_found, str(extra) + print("CONFIRM: Static FDB Vlan2:"+mac.lower()+"Port0 is created in APP-DB") + + # check that the FDB entry is not inserted into ASIC DB + print("CHECK: Static FDB Vlan2:"+mac.lower()+":Port0 is not created in ASIC-DB") + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac.lower())], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC")] + ) + assert ok == False, str(extra) + print("CONFIRM: Static FDB Vlan2:"+mac.lower()+":Port0 is not created in ASIC-DB") + + print("ACTION: Removing static FDB Vlan2:"+mac.lower()+":Port0 from CONFIG-DB") + remove_entry_tbl(dvs.cdb, "FDB", "Vlan2|"+mac.lower()) + time.sleep(2) + + + #Check the mac is removed from config-db + print("CHECK: Static FDB Vlan2:"+mac.lower()+":Port0 is deleted from Config-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.cdb, "FDB", + "Vlan2\|"+mac.lower(), + [("port", "Port0")] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: Static FDB Vlan2:"+mac.lower()+":Port0 is deleted from Config-DB") + + # check that the FDB entry is removed from APP DB + print("CHECK: Static FDB Vlan2:"+mac.lower()+":Port0 is deleted from APP-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.pdb, "FDB_TABLE", + "Vlan2:"+mac.lower(), + [("port", "Port0"), + ("type", "static"), + ] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: Static FDB Vlan2:"+mac.lower()+":Port0 is deleted from APP-DB") + + print("NEG4 - simulate mac learn event for a port which is not part of vlan") + bp_eth8 = iface_2_bridge_port_id["Ethernet8"] + dvs.remove_vlan_member("4", "Ethernet8") + + print("ACTION Creating FDB Vlan4:52-54-00-25-06-E9:Ethernet8 in ASIC-DB") + create_entry_tbl( + dvs.adb, + "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_4+"\",\"mac\":\"52:54:00:25:06:E9\",\"switch_id\":\""+switch_id+"\"}", + [ + ("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", bp_eth8), + ] + ) + + ntf = swsscommon.NotificationProducer(dvs.adb, "FDB_NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_4+"\\\",\\\"mac\\\":\\\"52:54:00:25:06:E9\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_LEARNED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+bp_eth8+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + # check that the FDB entry was added in ASIC DB + print("CHECK: FDB Vlan4:52-54-00-25-06-E9:Ethernet8 is created in ASIC-DB") + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", vlan_oid_4)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", bp_eth8)] + ) + assert ok, str(extra) + print("CONFIRM: FDB Vlan4:52-54-00-25-06-E9:Ethernet8 is created in ASIC-DB") + + # check that the FDB entry was not added in STATE DB + print("CHECK: FDB Vlan4:52-54-00-25-06-E9:Ethernet8 is not created in STATE-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan4:52:54:00:25:06:e9", + [("port", "Ethernet8"), + ("type", "dynamic"), + ] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: FDB Vlan4:52-54-00-25-06-E9:Ethernet8 is not created in STATE-DB") + + print("NEG5 - simulate mac learn event for a vlan which does not exist") + bp_eth12 = iface_2_bridge_port_id["Ethernet12"] + dvs.remove_vlan_member("10", "Ethernet12") + dvs.remove_vlan("10") + + print("ACTION: Creating FDB Vlan10:52-54-00-25-06-E9:Ethernet12 in ASIC-DB") + create_entry_tbl( + dvs.adb, + "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_10+"\",\"mac\":\"52:54:00:25:06:E9\",\"switch_id\":\""+switch_id+"\"}", + [ + ("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", bp_eth12), + ] + ) + + ntf = swsscommon.NotificationProducer(dvs.adb, "FDB_NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_10+"\\\",\\\"mac\\\":\\\"52:54:00:25:06:E9\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_LEARNED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+bp_eth12+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + # check that the FDB entry was added in ASIC DB + print("CHECK: FDB Vlan10:52-54-00-25-06-E9:Ethernet12 is created in ASIC-DB") + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", vlan_oid_10)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", bp_eth12)] + ) + assert ok, str(extra) + print("CONFIRM: FDB Vlan10:52-54-00-25-06-E9:Ethernet12 is created in ASIC-DB") + + # check that the FDB entry was not added in STATE DB + print("CHECK: FDB Vlan10:52-54-00-25-06-E9:Ethernet12 is not created in STATE-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan10:52:54:00:25:06:e9", + [("port", "Ethernet12"), + ("type", "dynamic"), + ] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: FDB Vlan10:52-54-00-25-06-E9:Ethernet12 is not created in STATE-DB") + + + print("NEG6 - simulate mac age event for a vlan which does not exist") + + print("ACTION: Deleting FDB Vlan10:52-54-00-25-06-E9:Ethernet12 from ASIC-DB") + remove_entry_tbl(dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_10+"\",\"mac\":\"52:54:00:25:06:E9\",\"switch_id\":\""+switch_id+"\"}") + + ntf = swsscommon.NotificationProducer(dvs.adb, "FDB_NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_10+"\\\",\\\"mac\\\":\\\"52:54:00:25:06:E9\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_AGED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+bp_eth12+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + # check that the FDB entry is not present ASIC DB + print("CHECK: FDB Vlan10:52-54-00-25-06-E9:Ethernet12 is not found in ASIC-DB") + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", vlan_oid_10)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", bp_eth12)] + ) + assert ok == False, str(extra) + print("CONFIRM: FDB Vlan10:52-54-00-25-06-E9:Ethernet12 is not found in ASIC-DB") + + # check that the FDB entry was not found in STATE DB + print("CHECK: FDB Vlan10:52-54-00-25-06-E9:Ethernet12 is not found in STATE-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan10:52:54:00:25:06:e9", + [("port", "Ethernet12"), + ("type", "dynamic"), + ] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: FDB Vlan10:52-54-00-25-06-E9:Ethernet12 is not found in STATE-DB") + + print("NEG7 - simulate mac age event for a port which is not part of vlan") + + print("ACTION: Deleting FDB Vlan4:52-54-00-25-06-E9:Ethernet8 from ASIC-DB") + remove_entry_tbl(dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_4+"\",\"mac\":\"52:54:00:25:06:E9\",\"switch_id\":\""+switch_id+"\"}") + + ntf = swsscommon.NotificationProducer(dvs.adb, "FDB_NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_4+"\\\",\\\"mac\\\":\\\"52:54:00:25:06:E9\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_AGED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+bp_eth8+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + # check that the FDB entry is not present ASIC DB + print("CHECK: FDB Vlan4:52-54-00-25-06-E9:Ethernet8 is not found in ASIC-DB") + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", vlan_oid_4)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", bp_eth8)] + ) + assert ok == False, str(extra) + print("CONFIRM: FDB Vlan4:52-54-00-25-06-E9:Ethernet8 is not found in ASIC-DB") + + # check that the FDB entry was not found in STATE DB + print("CHECK: FDB Vlan4:52-54-00-25-06-E9:Ethernet8 is not found in STATE-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan4:52:54:00:25:06:e9", + [("port", "Ethernet8"), + ("type", "dynamic"), + ] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: FDB Vlan4:52-54-00-25-06-E9:Ethernet8 is not found in STATE-DB") + + + print("NEG8 - simulate mac age event for a mac which does not exist") + + bp_eth0 = iface_2_bridge_port_id["Ethernet0"] + print("ACTION: Deleting FDB Vlan2:52-54-00-25-06-E9:Ethernet0 from ASIC-DB") + remove_entry_tbl(dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_2+"\",\"mac\":\"52:54:00:25:06:E9\",\"switch_id\":\""+switch_id+"\"}") + + ntf = swsscommon.NotificationProducer(dvs.adb, "FDB_NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_2+"\\\",\\\"mac\\\":\\\"52:54:00:25:06:E9\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_AGED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+bp_eth0+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + # check that the FDB entry is not present ASIC DB + print("CHECK: FDB Vlan2:52-54-00-25-06-E9:Ethernet0 is not found in ASIC-DB") + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", vlan_oid_2)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", bp_eth0)] + ) + assert ok == False, str(extra) + print("CONFIRM: FDB Vlan2:52-54-00-25-06-E9:Ethernet0 is not found in ASIC-DB") + + # check that the FDB entry was not found in STATE DB + print("CHECK: FDB Vlan2:52-54-00-25-06-E9:Ethernet0 is not found in STATE-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan2:52:54:00:25:06:e9", + [("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: FDB Vlan2:52-54-00-25-06-E9:Ethernet0 is not found in STATE-DB") + + + print("NEG9 - Add Static MAC M1 to Vlan V1 and Port P1; create V1; assign V1 to P1; remove V1 from P1; remove V1") + mac = "52:54:00:25:06:EF" + #dvs.runcmd("config mac add " + mac.lower() + " 10 Ethernet12") + print("ACTION: Creating static FDB Vlan10|"+mac.lower()+"|Ethernet12 in CONFIG-DB") + create_entry_tbl( + dvs.cdb, + "FDB", "Vlan10|"+mac.lower(), + [ + ("port", "Ethernet12"), + ] + ) + time.sleep(5) + + # check that the FDB entry was added in Config DB + print("CHECK: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is created in Config-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.cdb, "FDB", + "Vlan10\|"+mac.lower(), + [("port", "Ethernet12")] + ) + assert mac1_found, str(extra) + print("CONFIRM: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is created in Config-DB") + + # check that the FDB entry was added in APP DB + print("CHECK: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is created in APP-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.pdb, "FDB_TABLE", + "Vlan10:"+mac.lower(), + [("port", "Ethernet12"), + ("type", "static") + ] + ) + assert mac1_found, str(extra) + print("CONFIRM: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is created in APP-DB") + + # check that the FDB entry is not inserted into ASIC DB + print("CHECK: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is not created in ASIC-DB") + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac.lower())], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC")] + ) + assert ok == False, str(extra) + print("CONFIRM: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is not created in ASIC-DB") + + dvs.create_vlan("10") + time.sleep(1) + dvs.create_vlan_member("10", "Ethernet12") + time.sleep(1) + # Find the vlan_oid_10 to be used in DB communications + vlan_oid_10 = dvs.getVlanOid("10") + assert vlan_oid_10 is not None, "Could not find Vlan_oid" + print("VLan-10 vlan_oid="+str(vlan_oid_10)) + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + bp_eth12 = iface_2_bridge_port_id["Ethernet12"] + print("bp_eth12="+str(bp_eth12)) + + print("CHECK: Static FDB Vlan10:"+mac+":Ethernet12 is created in ASIC-DB") + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", str(vlan_oid_10))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", bp_eth12)] + ) + assert ok, str(extra) + print("CONFIRM: Static FDB Vlan10:"+mac+":Ethernet12 is created in ASIC-DB") + + # check that the FDB entry was added in STATE DB + print("CHECK: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is created in STATE-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan10:"+mac.lower(), + [("port", "Ethernet12"), + ("type", "static"), + ] + ) + assert mac1_found, str(extra) + print("CONFIRM: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is created in STATE-DB") + + print("ACTION: Remove vlan member Ethernet12") + dvs.remove_vlan_member("10", "Ethernet12") + time.sleep(2) + + print("CHECK: Static FDB Vlan10:"+mac+":Ethernet12 is deleted from ASIC-DB") + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", str(vlan_oid_10))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", bp_eth12)] + ) + assert ok == False, str(extra) + print("CONFIRM: Static FDB Vlan10:"+mac+":Ethernet12 is deleted from ASIC-DB") + + # check that the FDB entry was deleted from STATE DB + print("CHECK: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is deleted from STATE-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan10:"+mac.lower(), + [("port", "Ethernet12"), + ("type", "static"), + ] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is deleted from STATE-DB") + + + print("ACTION: Remove vlan Vlan10") + dvs.remove_vlan("10") + time.sleep(2) + + print("ACTION: Remove FDB Vlan10|"+mac.lower()+" from Config DB") + remove_entry_tbl(dvs.cdb, "FDB", "Vlan10|"+mac.lower()) + time.sleep(2) + + #Check the mac is removed from config-db + print("CHECK: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is deleted from Config-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.cdb, "FDB", + "Vlan10\|"+mac.lower(), + [("port", "Ethernet12")] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is deleted from Config-DB") + + # check that the FDB entry is removed from APP DB + print("CHECK: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is deleted from APP-DB") + mac1_found, extra = dvs.is_table_entry_exists(dvs.pdb, "FDB_TABLE", + "Vlan10:"+mac.lower(), + [("port", "Ethernet12"), + ("type", "static"), + ] + ) + assert mac1_found == False, str(extra) + print("CONFIRM: Static FDB Vlan10:"+mac.lower()+":Ethernet12 is deleted from APP-DB") + + print("NEG10 - Received move event with invalid bridge-port") + # Move a FDB entry in ASIC DB + print("Action: Creating FDB Vlan2:52-54-00-25-06-EB:Ethernet0 in ASIC-DB") + create_entry_tbl( + dvs.adb, + "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_2+"\",\"mac\":\"52:54:00:25:06:EB\",\"switch_id\":\""+switch_id+"\"}", + [ + ("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + + ntf = swsscommon.NotificationProducer(dvs.adb, "FDB_NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_2+"\\\",\\\"mac\\\":\\\"52:54:00:25:06:EB\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_LEARNED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+iface_2_bridge_port_id["Ethernet0"]+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + print("Action: Moving FDB Vlan2:52-54-00-25-06-EB:Ethernet0 to non-existing bridge-port Ethernet12") + ntf = swsscommon.NotificationProducer(dvs.adb, "FDB_NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_2+"\\\",\\\"mac\\\":\\\"52:54:00:25:06:EB\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_MOVE\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+bp_eth12+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + print("CHECK: FDB Vlan2:52-54-00-25-06-EB is not Moved in STATE-DB") + # check that the FDB entry was not moved in STATE DB + mac2_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan2:52:54:00:25:06:eb", + [("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + assert mac2_found, str(extra) + print("CONFIRM: FDB Vlan2:52-54-00-25-06-EB is not Moved in STATE-DB") + + #raw_input("Check at the end") + + dvs.runcmd("sonic-clear fdb all") + time.sleep(10) + + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan_member("2", "Ethernet4") + dvs.remove_vlan("2") + dvs.remove_vlan("4") + diff --git a/tests/test_fdb_no_flush_static_port_down.py b/tests/test_fdb_no_flush_static_port_down.py new file mode 100644 index 0000000000..520401d7a4 --- /dev/null +++ b/tests/test_fdb_no_flush_static_port_down.py @@ -0,0 +1,92 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +def test_FDBNoStaticFlushPortDown(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a FDB entry in Application DB + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet0"), + ("type", "static"), + ] + ) + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", str(dvs.getVlanOid("2")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + + assert ok, str(extra) + + dvs.runcmd("config interface shutdown Ethernet0") + time.sleep(2) + # check that the FDB entry was not flushed + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") != 0, "The fdb entry was flushed" + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + ) + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan("2") + diff --git a/tests/test_fdb_static_mac_add.py b/tests/test_fdb_static_mac_add.py new file mode 100644 index 0000000000..80caf095e6 --- /dev/null +++ b/tests/test_fdb_static_mac_add.py @@ -0,0 +1,72 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +def test_FDBAddStatic(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a static FDB entry + dvs.runcmd("config mac add 52:54:00:25:06:E9 2 Ethernet0") + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", str(dvs.getVlanOid("2")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + + assert ok, str(extra) + + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan("2") + diff --git a/tests/test_fdb_static_mac_del.py b/tests/test_fdb_static_mac_del.py new file mode 100644 index 0000000000..b1b56bb896 --- /dev/null +++ b/tests/test_fdb_static_mac_del.py @@ -0,0 +1,76 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +def test_FDBDelStatic(dvs, testlog): + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + # create a static FDB entry + dvs.runcmd("config mac add 52:54:00:25:06:E9 2 Ethernet0") + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + # create vlan + dvs.create_vlan("2") + dvs.create_vlan_member("2", "Ethernet0") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan2 wasn't created" + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", str(dvs.getVlanOid("2")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + + assert ok, str(extra) + + dvs.runcmd("config mac del 52:54:00:25:06:E9 2") + time.sleep(2) + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan("2") +