diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index 45afff7e9b..096c222418 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -5,7 +5,7 @@ LIBNL_LIBS = -lnl-genl-3 -lnl-route-3 -lnl-3 SAIMETA_LIBS = -lsaimeta -lsaimetadata -lzmq COMMON_LIBS = -lswsscommon -lpthread -bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd coppmgrd tunnelmgrd macsecmgrd fabricmgrd +bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd coppmgrd tunnelmgrd macsecmgrd fabricmgrd stpmgrd cfgmgrdir = $(datadir)/swss @@ -101,6 +101,12 @@ macsecmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CF macsecmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) macsecmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) + +stpmgrd_SOURCES = stpmgrd.cpp stpmgr.cpp $(COMMON_ORCH_SOURCE) shellcmd.h +stpmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +stpmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +stpmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) + if GCOV_ENABLED vlanmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp teammgrd_SOURCES += ../gcovpreload/gcovpreload.cpp @@ -116,6 +122,7 @@ natmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp coppmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp tunnelmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp macsecmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp +stpmgrd_SOURCES += ../gcovpreload/gcovpreload.cpp endif if ASAN_ENABLED @@ -133,5 +140,6 @@ coppmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp tunnelmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp macsecmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp fabricmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +stpmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp endif diff --git a/cfgmgr/stpmgr.cpp b/cfgmgr/stpmgr.cpp new file mode 100644 index 0000000000..77991e21b0 --- /dev/null +++ b/cfgmgr/stpmgr.cpp @@ -0,0 +1,1093 @@ +#include "exec.h" +#include "stpmgr.h" +#include "logger.h" +#include "tokenize.h" +#include "warm_restart.h" + +#include +#include +#include +#include + +#include +#include +#include + +using namespace std; +using namespace swss; + +StpMgr::StpMgr(DBConnector *confDb, DBConnector *applDb, DBConnector *statDb, + const vector &tables) : + Orch(tables), + m_cfgStpGlobalTable(confDb, CFG_STP_GLOBAL_TABLE_NAME), + m_cfgStpVlanTable(confDb, CFG_STP_VLAN_TABLE_NAME), + m_cfgStpVlanPortTable(confDb, CFG_STP_VLAN_PORT_TABLE_NAME), + m_cfgStpPortTable(confDb, CFG_STP_PORT_TABLE_NAME), + m_cfgLagMemberTable(confDb, CFG_LAG_MEMBER_TABLE_NAME), + m_cfgVlanMemberTable(confDb, CFG_VLAN_MEMBER_TABLE_NAME), + m_stateVlanTable(statDb, STATE_VLAN_TABLE_NAME), + m_stateLagTable(statDb, STATE_LAG_TABLE_NAME), + m_stateStpTable(statDb, STATE_STP_TABLE_NAME), + m_stateVlanMemberTable(statDb, STATE_VLAN_MEMBER_TABLE_NAME) +{ + SWSS_LOG_ENTER(); + l2ProtoEnabled = L2_NONE; + + stpGlobalTask = stpVlanTask = stpVlanPortTask = stpPortTask = false; + + // Initialize all VLANs to Invalid instance + fill_n(m_vlanInstMap, MAX_VLANS, INVALID_INSTANCE); + + int ret = system("ebtables -D FORWARD -d 01:00:0c:cc:cc:cd -j DROP"); + SWSS_LOG_DEBUG("ebtables ret %d", ret); +} + +void StpMgr::doTask(Consumer &consumer) +{ + auto table = consumer.getTableName(); + + SWSS_LOG_INFO("Get task from table %s", table.c_str()); + + if (table == CFG_STP_GLOBAL_TABLE_NAME) + doStpGlobalTask(consumer); + else if (table == CFG_STP_VLAN_TABLE_NAME) + doStpVlanTask(consumer); + else if (table == CFG_STP_VLAN_PORT_TABLE_NAME) + doStpVlanPortTask(consumer); + else if (table == CFG_STP_PORT_TABLE_NAME) + doStpPortTask(consumer); + else if (table == CFG_LAG_MEMBER_TABLE_NAME) + doLagMemUpdateTask(consumer); + else if (table == STATE_VLAN_MEMBER_TABLE_NAME) + doVlanMemUpdateTask(consumer); + else + SWSS_LOG_ERROR("Invalid table %s", table.c_str()); +} + +void StpMgr::doStpGlobalTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (stpGlobalTask == false) + stpGlobalTask = true; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + STP_BRIDGE_CONFIG_MSG msg; + memset(&msg, 0, sizeof(STP_BRIDGE_CONFIG_MSG)); + + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + SWSS_LOG_INFO("STP global key %s op %s", key.c_str(), op.c_str()); + if (op == SET_COMMAND) + { + msg.opcode = STP_SET_COMMAND; + for (auto i : kfvFieldsValues(t)) + { + SWSS_LOG_DEBUG("Field: %s Val %s", fvField(i).c_str(), fvValue(i).c_str()); + if (fvField(i) == "mode") + { + if (fvValue(i) == "pvst") + { + if (l2ProtoEnabled == L2_NONE) + { + const std::string cmd = std::string("") + + " ebtables -A FORWARD -d 01:00:0c:cc:cc:cd -j DROP"; + std::string res; + int ret = swss::exec(cmd, res); + if (ret != 0) + SWSS_LOG_ERROR("ebtables add failed %d", ret); + + l2ProtoEnabled = L2_PVSTP; + } + msg.stp_mode = L2_PVSTP; + } + else + SWSS_LOG_ERROR("Error invalid mode %s", fvValue(i).c_str()); + } + else if (fvField(i) == "rootguard_timeout") + { + msg.rootguard_timeout = stoi(fvValue(i).c_str()); + } + } + + memcpy(msg.base_mac_addr, macAddress.getMac(), 6); + } + else if (op == DEL_COMMAND) + { + msg.opcode = STP_DEL_COMMAND; + l2ProtoEnabled = L2_NONE; + + //Free Up all instances + FREE_ALL_INST_ID(); + + // Initialize all VLANs to Invalid instance + fill_n(m_vlanInstMap, MAX_VLANS, INVALID_INSTANCE); + + const std::string cmd = std::string("") + + " ebtables -D FORWARD -d 01:00:0c:cc:cc:cd -j DROP"; + std::string res; + int ret = swss::exec(cmd, res); + if (ret != 0) + SWSS_LOG_ERROR("ebtables del failed %d", ret); + } + + sendMsgStpd(STP_BRIDGE_CONFIG, sizeof(msg), (void *)&msg); + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::doStpVlanTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (stpGlobalTask == false || (stpPortTask == false && !isStpPortEmpty())) + return; + + if (stpVlanTask == false) + stpVlanTask = true; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + STP_VLAN_CONFIG_MSG *msg = NULL; + uint32_t len = 0; + bool stpEnable = false; + uint8_t newInstance = 0; + int instId, forwardDelay, helloTime, maxAge, priority, portCnt = 0; + instId = forwardDelay = helloTime = maxAge = priority = portCnt = 0; + + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + string vlanKey = key.substr(4); // Remove Vlan prefix + int vlan_id = stoi(vlanKey.c_str()); + + SWSS_LOG_INFO("STP vlan key %s op %s", key.c_str(), op.c_str()); + if (op == SET_COMMAND) + { + if (l2ProtoEnabled == L2_NONE || !isVlanStateOk(key)) + { + // Wait till STP is configured + it++; + continue; + } + + for (auto i : kfvFieldsValues(t)) + { + SWSS_LOG_DEBUG("Field: %s Val: %s", fvField(i).c_str(), fvValue(i).c_str()); + + if (fvField(i) == "enabled") + { + stpEnable = (fvValue(i) == "true") ? true : false; + } + else if (fvField(i) == "forward_delay") + { + forwardDelay = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "hello_time") + { + helloTime = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "max_age") + { + maxAge = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "priority") + { + priority = stoi(fvValue(i).c_str()); + } + } + } + else if (op == DEL_COMMAND) + { + stpEnable = false; + if (l2ProtoEnabled == L2_NONE) + { + it = consumer.m_toSync.erase(it); + continue; + } + } + + len = sizeof(STP_VLAN_CONFIG_MSG); + if (stpEnable == true) + { + vector port_list; + if (m_vlanInstMap[vlan_id] == INVALID_INSTANCE) + { + /* VLAN is being added to the instance. Get all members for VLAN Mapping*/ + if (l2ProtoEnabled == L2_PVSTP) + { + newInstance = 1; + instId = allocL2Instance(vlan_id); + if (instId == -1) + { + SWSS_LOG_ERROR("Couldnt allocate instance to VLAN %d", vlan_id); + it = consumer.m_toSync.erase(it); + continue; + } + + portCnt = getAllVlanMem(key, port_list); + SWSS_LOG_DEBUG("Port count %d", portCnt); + } + + len += (uint32_t)(portCnt * sizeof(PORT_ATTR)); + } + + msg = (STP_VLAN_CONFIG_MSG *)calloc(1, len); + if (!msg) + { + SWSS_LOG_ERROR("mem failed for vlan %d", vlan_id); + return; + } + + msg->opcode = STP_SET_COMMAND; + msg->vlan_id = vlan_id; + msg->newInstance = newInstance; + msg->inst_id = m_vlanInstMap[vlan_id]; + msg->forward_delay = forwardDelay; + msg->hello_time = helloTime; + msg->max_age = maxAge; + msg->priority = priority; + msg->count = portCnt; + + if(msg->count) + { + int i = 0; + PORT_ATTR *attr = msg->port_list; + for (auto p = port_list.begin(); p != port_list.end(); p++) + { + attr[i].mode = p->mode; + attr[i].enabled = p->enabled; + strncpy(attr[i].intf_name, p->intf_name, IFNAMSIZ); + SWSS_LOG_DEBUG("MemIntf: %s", p->intf_name); + i++; + } + } + } + else + { + if (m_vlanInstMap[vlan_id] == INVALID_INSTANCE) + { + // Already deallocated. NoOp. This can happen when STP + // is disabled on a VLAN more than once + it = consumer.m_toSync.erase(it); + continue; + } + + msg = (STP_VLAN_CONFIG_MSG *)calloc(1, len); + if (!msg) + { + SWSS_LOG_ERROR("mem failed for vlan %d", vlan_id); + return; + } + + msg->opcode = STP_DEL_COMMAND; + msg->inst_id = m_vlanInstMap[vlan_id]; + + deallocL2Instance(vlan_id); + } + + sendMsgStpd(STP_VLAN_CONFIG, len, (void *)msg); + if (msg) + free(msg); + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::processStpVlanPortAttr(const string op, uint32_t vlan_id, const string intfName, + vector&tupEntry) +{ + STP_VLAN_PORT_CONFIG_MSG msg; + memset(&msg, 0, sizeof(STP_VLAN_PORT_CONFIG_MSG)); + + msg.vlan_id = vlan_id; + msg.inst_id = m_vlanInstMap[vlan_id]; + strncpy(msg.intf_name, intfName.c_str(), IFNAMSIZ-1); + + if (op == SET_COMMAND) + { + msg.opcode = STP_SET_COMMAND; + msg.priority = -1; + + for (auto i : tupEntry) + { + SWSS_LOG_DEBUG("Field: %s Val: %s", fvField(i).c_str(), fvValue(i).c_str()); + if (fvField(i) == "path_cost") + { + msg.path_cost = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "priority") + { + msg.priority = stoi(fvValue(i).c_str()); + } + } + } + else if (op == DEL_COMMAND) + { + msg.opcode = STP_DEL_COMMAND; + } + + sendMsgStpd(STP_VLAN_PORT_CONFIG, sizeof(msg), (void *)&msg); +} + +void StpMgr::doStpVlanPortTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (stpGlobalTask == false || stpVlanTask == false || stpPortTask == false) + return; + + if (stpVlanPortTask == false) + stpVlanPortTask = true; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + STP_VLAN_PORT_CONFIG_MSG msg; + memset(&msg, 0, sizeof(STP_VLAN_PORT_CONFIG_MSG)); + + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + string vlanKey = key.substr(4); // Remove VLAN keyword + size_t found = vlanKey.find(CONFIGDB_KEY_SEPARATOR); + + int vlan_id; + string intfName; + if (found != string::npos) + { + vlan_id = stoi(vlanKey.substr(0, found)); + intfName = vlanKey.substr(found+1); + } + else + { + SWSS_LOG_ERROR("Invalid key format %s", kfvKey(t).c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + SWSS_LOG_INFO("STP vlan intf key:%s op:%s", key.c_str(), op.c_str()); + + if (op == SET_COMMAND) + { + if ((l2ProtoEnabled == L2_NONE) || (m_vlanInstMap[vlan_id] == INVALID_INSTANCE)) + { + // Wait till STP/VLAN is configured + it++; + continue; + } + } + else + { + if (l2ProtoEnabled == L2_NONE || (m_vlanInstMap[vlan_id] == INVALID_INSTANCE)) + { + it = consumer.m_toSync.erase(it); + continue; + } + } + + if (isLagEmpty(intfName)) + { + // Lag has no member. Process when first member is added/deleted + it = consumer.m_toSync.erase(it); + continue; + } + + processStpVlanPortAttr(op, vlan_id, intfName, kfvFieldsValues(t)); + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::processStpPortAttr(const string op, vector&tupEntry, const string intfName) +{ + STP_PORT_CONFIG_MSG *msg; + uint32_t len = 0; + int vlanCnt = 0; + vector vlan_list; + + if (op == SET_COMMAND) + vlanCnt = getAllPortVlan(intfName, vlan_list); + + len = (uint32_t)(sizeof(STP_PORT_CONFIG_MSG) + (vlanCnt * sizeof(VLAN_ATTR))); + msg = (STP_PORT_CONFIG_MSG *)calloc(1, len); + if (!msg) + { + SWSS_LOG_ERROR("mem failed for %s", intfName.c_str()); + return; + } + + strncpy(msg->intf_name, intfName.c_str(), IFNAMSIZ-1); + msg->count = vlanCnt; + SWSS_LOG_INFO("Vlan count %d", vlanCnt); + + if(msg->count) + { + int i = 0; +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Waddress-of-packed-member" + VLAN_ATTR *attr = msg->vlan_list; +#pragma GCC diagnostic pop + for (auto p = vlan_list.begin(); p != vlan_list.end(); p++) + { + attr[i].inst_id = p->inst_id; + attr[i].mode = p->mode; + SWSS_LOG_DEBUG("Inst:%d Mode:%d", p->inst_id, p->mode); + i++; + } + } + + if (op == SET_COMMAND) + { + msg->opcode = STP_SET_COMMAND; + msg->priority = -1; + + for (auto i : tupEntry) + { + SWSS_LOG_DEBUG("Field: %s Val: %s", fvField(i).c_str(), fvValue(i).c_str()); + if (fvField(i) == "enabled") + { + msg->enabled = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "root_guard") + { + msg->root_guard = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "bpdu_guard") + { + msg->bpdu_guard = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "bpdu_guard_do_disable") + { + msg->bpdu_guard_do_disable = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "path_cost") + { + msg->path_cost = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "priority") + { + msg->priority = stoi(fvValue(i).c_str()); + } + else if (fvField(i) == "portfast") + { + msg->portfast = (fvValue(i) == "true") ? 1 : 0; + } + else if (fvField(i) == "uplink_fast") + { + msg->uplink_fast = (fvValue(i) == "true") ? 1 : 0; + } + } + } + else if (op == DEL_COMMAND) + { + msg->opcode = STP_DEL_COMMAND; + msg->enabled = 0; + } + + sendMsgStpd(STP_PORT_CONFIG, len, (void *)msg); + if (msg) + free(msg); +} + +void StpMgr::doStpPortTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (stpGlobalTask == false) + return; + + if (stpPortTask == false) + stpPortTask = true; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + if (isLagEmpty(key)) + { + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + if (l2ProtoEnabled == L2_NONE) + { + // Wait till STP is configured + it++; + continue; + } + } + else + { + if (l2ProtoEnabled == L2_NONE) + { + it = consumer.m_toSync.erase(it); + continue; + } + } + + SWSS_LOG_INFO("STP port key:%s op:%s", key.c_str(), op.c_str()); + processStpPortAttr(op, kfvFieldsValues(t), key); + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::doVlanMemUpdateTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + STP_VLAN_MEM_CONFIG_MSG msg; + memset(&msg, 0, sizeof(STP_VLAN_MEM_CONFIG_MSG)); + + KeyOpFieldsValuesTuple t = it->second; + + auto key = kfvKey(t); + auto op = kfvOp(t); + + string vlanKey = key.substr(4); // Remove Vlan prefix + size_t found = vlanKey.find(CONFIGDB_KEY_SEPARATOR); + + int vlan_id; + string intfName; + if (found != string::npos) + { + vlan_id = stoi(vlanKey.substr(0, found)); + intfName = vlanKey.substr(found+1); + } + else + { + SWSS_LOG_ERROR("Invalid key format. No member port is presented: %s", kfvKey(t).c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + SWSS_LOG_INFO("STP vlan mem key:%s op:%s inst:%d", key.c_str(), op.c_str(), m_vlanInstMap[vlan_id]); + // If STP is running on this VLAN, notify STPd + if (m_vlanInstMap[vlan_id] != INVALID_INSTANCE && !isLagEmpty(intfName)) + { + int8_t tagging_mode = TAGGED_MODE; + + if (op == SET_COMMAND) + { + tagging_mode = getVlanMemMode(key); + if (tagging_mode == INVALID_MODE) + { + SWSS_LOG_ERROR("invalid mode %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + SWSS_LOG_DEBUG("mode %d key %s", tagging_mode, key.c_str()); + + msg.enabled = isStpEnabled(intfName); + + vector stpVlanPortEntry; + if (m_cfgStpVlanPortTable.get(key, stpVlanPortEntry)) + { + for (auto entry : stpVlanPortEntry) + { + if (entry.first == "priority") + msg.priority = stoi(entry.second); + else if (entry.first == "path_cost") + msg.path_cost = stoi(entry.second); + } + } + } + + msg.opcode = (op == SET_COMMAND) ? STP_SET_COMMAND : STP_DEL_COMMAND; + msg.vlan_id = vlan_id; + msg.inst_id = m_vlanInstMap[vlan_id]; + msg.mode = tagging_mode; + msg.priority = -1; + msg.path_cost = 0; + + strncpy(msg.intf_name, intfName.c_str(), IFNAMSIZ-1); + + sendMsgStpd(STP_VLAN_MEM_CONFIG, sizeof(msg), (void *)&msg); + } + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::doLagMemUpdateTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + bool notifyStpd = false; + + auto key = kfvKey(t); + auto op = kfvOp(t); + + string po_name; + string po_mem; + size_t found = key.find(CONFIGDB_KEY_SEPARATOR); + + if (found != string::npos) + { + po_name = key.substr(0, found); + po_mem = key.substr(found+1); + } + else + { + SWSS_LOG_ERROR("Invalid key format %s", key.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + + if (op == SET_COMMAND) + { + if (!isLagStateOk(po_name)) + { + it++; + continue; + } + + auto elm = m_lagMap.find(po_name); + if (elm == m_lagMap.end()) + { + // First Member added to the LAG + m_lagMap[po_name] = 1; + notifyStpd = true; + } + else + { + elm->second++; + } + } + else if (op == DEL_COMMAND) + { + auto elm = m_lagMap.find(po_name); + if (elm != m_lagMap.end()) + { + elm->second--; + + if (elm->second == 0) + { + // Last Member deleted from the LAG + m_lagMap.erase(po_name); + //notifyStpd = true; + } + } + else + SWSS_LOG_ERROR("PO not found %s", po_name.c_str()); + } + + if (notifyStpd && l2ProtoEnabled != L2_NONE) + { + vector vlan_list; + vector tupEntry; + + if (m_cfgStpPortTable.get(po_name, tupEntry)) + { + //Push STP_PORT configs for this port + processStpPortAttr(op, tupEntry, po_name); + + getAllPortVlan(po_name, vlan_list); + //Push STP_VLAN_PORT configs for this port + for (auto p = vlan_list.begin(); p != vlan_list.end(); p++) + { + vector vlanPortTup; + + string vlanPortKey = "Vlan" + to_string(p->vlan_id) + "|" + po_name; + if (m_cfgStpVlanPortTable.get(vlanPortKey, vlanPortTup)) + processStpVlanPortAttr(op, p->vlan_id, po_name, vlanPortTup); + } + } + } + + SWSS_LOG_DEBUG("LagMap"); + for (auto itr = m_lagMap.begin(); itr != m_lagMap.end(); ++itr) { + SWSS_LOG_DEBUG("PO: %s Cnt:%d", itr->first.c_str(), itr->second); + } + + it = consumer.m_toSync.erase(it); + } +} + +void StpMgr::ipcInitStpd() +{ + int ret; + struct sockaddr_un addr; + + unlink(STPMGRD_SOCK_NAME); + // create socket + stpd_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + if (!stpd_fd) { + SWSS_LOG_ERROR("socket error %s", strerror(errno)); + return; + } + + // setup socket address structure + bzero(&addr, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, STPMGRD_SOCK_NAME, sizeof(addr.sun_path)-1); + + ret = (int)bind(stpd_fd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)); + if (ret == -1) + { + SWSS_LOG_ERROR("ipc bind error %s", strerror(errno)); + close(stpd_fd); + return; + } +} + +int StpMgr::allocL2Instance(uint32_t vlan_id) +{ + int idx = 0; + + if (!IS_INST_ID_AVAILABLE()) + { + SWSS_LOG_ERROR("No instance available"); + return -1; + } + + if (l2ProtoEnabled == L2_PVSTP) + { + GET_FIRST_FREE_INST_ID(idx); + } + else + { + SWSS_LOG_ERROR("invalid proto %d for vlan %d", l2ProtoEnabled, vlan_id); + return -1; + } + + //Set VLAN to Instance mapping + m_vlanInstMap[vlan_id] = idx; + SWSS_LOG_INFO("Allocated Id: %d Vlan %d", m_vlanInstMap[vlan_id], vlan_id); + + return idx; +} + +void StpMgr::deallocL2Instance(uint32_t vlan_id) +{ + int idx = 0; + + if (l2ProtoEnabled == L2_PVSTP) + { + idx = m_vlanInstMap[vlan_id]; + FREE_INST_ID(idx); + } + else + { + SWSS_LOG_ERROR("invalid proto %d for vlan %d", l2ProtoEnabled, vlan_id); + } + + m_vlanInstMap[vlan_id] = INVALID_INSTANCE; + SWSS_LOG_INFO("Deallocated Id: %d Vlan %d", m_vlanInstMap[vlan_id], vlan_id); +} + + +int StpMgr::getAllVlanMem(const string &vlanKey, vector&port_list) +{ + PORT_ATTR port_id; + vector vmEntry; + + vector vmKeys; + m_stateVlanMemberTable.getKeys(vmKeys); + + SWSS_LOG_INFO("VLAN Key: %s", vlanKey.c_str()); + for (auto key : vmKeys) + { + size_t found = key.find(CONFIGDB_KEY_SEPARATOR); //split VLAN and interface + + string vlanName; + string intfName; + if (found != string::npos) + { + vlanName = key.substr(0, found); + intfName = key.substr(found+1); + } + else + { + SWSS_LOG_ERROR("Invalid Key: %s", key.c_str()); + continue; + } + + if (vlanKey == vlanName && !isLagEmpty(intfName)) + { + port_id.mode = getVlanMemMode(key); + if (port_id.mode == INVALID_MODE) + { + SWSS_LOG_ERROR("invalid mode %s", key.c_str()); + continue; + } + + port_id.enabled = isStpEnabled(intfName); + strncpy(port_id.intf_name, intfName.c_str(), IFNAMSIZ-1); + port_list.push_back(port_id); + SWSS_LOG_DEBUG("MemIntf: %s", intfName.c_str()); + } + } + + return (int)port_list.size(); +} + +int StpMgr::getAllPortVlan(const string &intfKey, vector&vlan_list) +{ + VLAN_ATTR vlan; + vector vmEntry; + + vector vmKeys; + m_stateVlanMemberTable.getKeys(vmKeys); + + SWSS_LOG_INFO("Intf Key: %s", intfKey.c_str()); + for (auto key : vmKeys) + { + string vlanKey = key.substr(4); // Remove Vlan prefix + size_t found = vlanKey.find(CONFIGDB_KEY_SEPARATOR); //split VLAN and interface + SWSS_LOG_DEBUG("Vlan mem Key: %s", key.c_str()); + + int vlan_id; + string intfName; + if (found != string::npos) + { + vlan_id = stoi(vlanKey.substr(0, found)); + intfName = vlanKey.substr(found+1); + + if (intfName == intfKey) + { + if (m_vlanInstMap[vlan_id] != INVALID_INSTANCE) + { + vlan.mode = getVlanMemMode(key); + if (vlan.mode == INVALID_MODE) + { + SWSS_LOG_ERROR("invalid mode %s", key.c_str()); + continue; + } + + vlan.vlan_id = vlan_id; + vlan.inst_id = m_vlanInstMap[vlan_id]; + vlan_list.push_back(vlan); + SWSS_LOG_DEBUG("Matched vlan key: %s intf key %s", intfName.c_str(), intfKey.c_str()); + } + } + } + } + + return (int)vlan_list.size(); +} + +// Send Message to STPd +int StpMgr::sendMsgStpd(STP_MSG_TYPE msgType, uint32_t msgLen, void *data) +{ + STP_IPC_MSG *tx_msg; + size_t len = 0; + struct sockaddr_un addr; + int rc; + + len = msgLen + (offsetof(struct STP_IPC_MSG, data)); + SWSS_LOG_INFO("tx_msg len %d msglen %d", (int)len, msgLen); + + tx_msg = (STP_IPC_MSG *)calloc(1, len); + if (tx_msg == NULL) + { + SWSS_LOG_ERROR("tx_msg mem alloc error\n"); + return -1; + } + + tx_msg->msg_type = msgType; + tx_msg->msg_len = msgLen; + memcpy(tx_msg->data, data, msgLen); + + bzero(&addr, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, STPD_SOCK_NAME, sizeof(addr.sun_path)-1); + + rc = (int)sendto(stpd_fd, (void*)tx_msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)); + if (rc == -1) + { + SWSS_LOG_ERROR("tx_msg send error\n"); + } + else + { + SWSS_LOG_INFO("tx_msg sent %d", rc); + } + + free(tx_msg); + return rc; +} + +bool StpMgr::isPortInitDone(DBConnector *app_db) +{ + bool portInit = 0; + long cnt = 0; + + while(!portInit) { + Table portTable(app_db, APP_PORT_TABLE_NAME); + std::vector tuples; + portInit = portTable.get("PortInitDone", tuples); + + if(portInit) + break; + sleep(1); + cnt++; + } + SWSS_LOG_NOTICE("PORT_INIT_DONE : %d %ld", portInit, cnt); + return portInit; +} + +bool StpMgr::isVlanStateOk(const string &alias) +{ + vector temp; + + if (!alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) + { + if (m_stateVlanTable.get(alias, temp)) + { + SWSS_LOG_DEBUG("%s is ready", alias.c_str()); + return true; + } + } + SWSS_LOG_DEBUG("%s is not ready", alias.c_str()); + return false; +} + +bool StpMgr::isLagStateOk(const string &alias) +{ + vector temp; + + if (m_stateLagTable.get(alias, temp)) + { + SWSS_LOG_DEBUG("%s is ready", alias.c_str()); + return true; + } + + SWSS_LOG_DEBUG("%s is not ready", alias.c_str()); + return false; +} + +bool StpMgr::isLagEmpty(const string &key) +{ + size_t po_find = key.find("PortChannel"); + if (po_find != string::npos) + { + // If Lag, check if members present + auto elm = m_lagMap.find(key); + if (elm == m_lagMap.end()) + { + // Lag has no member + SWSS_LOG_DEBUG("%s empty", key.c_str()); + return true; + } + SWSS_LOG_DEBUG("%s not empty", key.c_str()); + } + // Else: Interface not PO + + return false; +} + +bool StpMgr::isStpPortEmpty() +{ + vector portKeys; + m_cfgStpPortTable.getKeys(portKeys); + + if (portKeys.empty()) + { + SWSS_LOG_NOTICE("stp port empty"); + return true; + } + + SWSS_LOG_NOTICE("stp port not empty"); + return false; +} + +bool StpMgr::isStpEnabled(const string &intf_name) +{ + vector temp; + + if (m_cfgStpPortTable.get(intf_name, temp)) + { + for (auto entry : temp) + { + if (entry.first == "enabled" && entry.second == "true") + { + SWSS_LOG_NOTICE("STP enabled on %s", intf_name.c_str()); + return true; + } + } + } + + SWSS_LOG_NOTICE("STP NOT enabled on %s", intf_name.c_str()); + return false; +} + +int8_t StpMgr::getVlanMemMode(const string &key) +{ + int8_t mode = -1; + vector vmEntry; + + if (m_cfgVlanMemberTable.get(key, vmEntry)) + { + for (auto entry : vmEntry) + { + if (entry.first == "tagging_mode") + mode = (entry.second == "untagged") ? UNTAGGED_MODE : TAGGED_MODE; + SWSS_LOG_INFO("mode %d for %s", mode, key.c_str()); + } + } + else + SWSS_LOG_ERROR("config vlan_member table fetch failed %s", key.c_str()); + + return mode; +} + +uint16_t StpMgr::getStpMaxInstances(void) +{ + vector vmEntry; + uint16_t max_delay = 60; + string key; + + key = "GLOBAL"; + + while(max_delay) + { + if (m_stateStpTable.get(key, vmEntry)) + { + for (auto entry : vmEntry) + { + if (entry.first == "max_stp_inst") + { + max_stp_instances = (uint16_t)stoi(entry.second.c_str()); + SWSS_LOG_NOTICE("max stp instance %d count %d", max_stp_instances, (60-max_delay)); + } + } + break; + } + sleep(1); + max_delay--; + } + + if(max_stp_instances == 0) + { + max_stp_instances = STP_DEFAULT_MAX_INSTANCES; + SWSS_LOG_NOTICE("set default max stp instance %d", max_stp_instances); + } + + return max_stp_instances; +} diff --git a/cfgmgr/stpmgr.h b/cfgmgr/stpmgr.h new file mode 100644 index 0000000000..8eb7ffdf80 --- /dev/null +++ b/cfgmgr/stpmgr.h @@ -0,0 +1,225 @@ +#ifndef __STPMGR__ +#define __STPMGR__ + +#include +#include +#include +#include +#include +#include + +#include "dbconnector.h" +#include "netmsg.h" +#include "orch.h" +#include "producerstatetable.h" +#include +#include + +#define STPMGRD_SOCK_NAME "/var/run/stpmgrd.sock" + +#define TAGGED_MODE 1 +#define UNTAGGED_MODE 0 +#define INVALID_MODE -1 + +#define MAX_VLANS 4096 + +// Maximum number of instances supported +#define L2_INSTANCE_MAX MAX_VLANS +#define STP_DEFAULT_MAX_INSTANCES 255 +#define INVALID_INSTANCE -1 + + +#define GET_FIRST_FREE_INST_ID(_idx) \ + while (_idx < (int)l2InstPool.size() && l2InstPool.test(_idx)) ++_idx; \ + l2InstPool.set(_idx) + +#define FREE_INST_ID(_idx) l2InstPool.reset(_idx) + +#define FREE_ALL_INST_ID() l2InstPool.reset() + +#define IS_INST_ID_AVAILABLE() (l2InstPool.count() < max_stp_instances) ? true : false + +#define STPD_SOCK_NAME "/var/run/stpipc.sock" + +typedef enum L2_PROTO_MODE{ + L2_NONE, + L2_PVSTP, +}L2_PROTO_MODE; + +typedef enum STP_MSG_TYPE { + STP_INVALID_MSG, + STP_INIT_READY, + STP_BRIDGE_CONFIG, + STP_VLAN_CONFIG, + STP_VLAN_PORT_CONFIG, + STP_PORT_CONFIG, + STP_VLAN_MEM_CONFIG, + STP_STPCTL_MSG, + STP_MAX_MSG +}STP_MSG_TYPE; + +typedef enum STP_CTL_TYPE { + STP_CTL_HELP, + STP_CTL_DUMP_ALL, + STP_CTL_DUMP_GLOBAL, + STP_CTL_DUMP_VLAN_ALL, + STP_CTL_DUMP_VLAN, + STP_CTL_DUMP_INTF, + STP_CTL_SET_LOG_LVL, + STP_CTL_DUMP_NL_DB, + STP_CTL_DUMP_NL_DB_INTF, + STP_CTL_DUMP_LIBEV_STATS, + STP_CTL_SET_DBG, + STP_CTL_CLEAR_ALL, + STP_CTL_CLEAR_VLAN, + STP_CTL_CLEAR_INTF, + STP_CTL_CLEAR_VLAN_INTF, + STP_CTL_MAX +}STP_CTL_TYPE; + +typedef struct STP_IPC_MSG { + int msg_type; + unsigned int msg_len; + char data[0]; +}STP_IPC_MSG; + +#define STP_SET_COMMAND 1 +#define STP_DEL_COMMAND 0 + +typedef struct STP_INIT_READY_MSG { + uint8_t opcode; // enable/disable + uint16_t max_stp_instances; +}__attribute__ ((packed))STP_INIT_READY_MSG; + +typedef struct STP_BRIDGE_CONFIG_MSG { + uint8_t opcode; // enable/disable + uint8_t stp_mode; + int rootguard_timeout; + uint8_t base_mac_addr[6]; +}__attribute__ ((packed))STP_BRIDGE_CONFIG_MSG; + +typedef struct PORT_ATTR { + char intf_name[IFNAMSIZ]; + int8_t mode; + uint8_t enabled; +}PORT_ATTR; + +typedef struct STP_VLAN_CONFIG_MSG { + uint8_t opcode; // enable/disable + uint8_t newInstance; + int vlan_id; + int inst_id; + int forward_delay; + int hello_time; + int max_age; + int priority; + int count; + PORT_ATTR port_list[0]; +}__attribute__ ((packed))STP_VLAN_CONFIG_MSG; + +typedef struct STP_VLAN_PORT_CONFIG_MSG { + uint8_t opcode; // enable/disable + int vlan_id; + char intf_name[IFNAMSIZ]; + int inst_id; + int path_cost; + int priority; +}__attribute__ ((packed))STP_VLAN_PORT_CONFIG_MSG; + +typedef struct VLAN_ATTR { + int inst_id; + int vlan_id; + int8_t mode; +}VLAN_ATTR; + +typedef struct STP_PORT_CONFIG_MSG { + uint8_t opcode; // enable/disable + char intf_name[IFNAMSIZ]; + uint8_t enabled; + uint8_t root_guard; + uint8_t bpdu_guard; + uint8_t bpdu_guard_do_disable; + uint8_t portfast; + uint8_t uplink_fast; + int path_cost; + int priority; + int count; + VLAN_ATTR vlan_list[0]; +}__attribute__ ((packed))STP_PORT_CONFIG_MSG; + +typedef struct STP_VLAN_MEM_CONFIG_MSG { + uint8_t opcode; // enable/disable + int vlan_id; + int inst_id; + char intf_name[IFNAMSIZ]; + uint8_t enabled; + int8_t mode; + int path_cost; + int priority; +}__attribute__ ((packed))STP_VLAN_MEM_CONFIG_MSG; + +namespace swss { + +class StpMgr : public Orch +{ +public: + StpMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, + const std::vector &tables); + + using Orch::doTask; + void ipcInitStpd(); + int sendMsgStpd(STP_MSG_TYPE msgType, uint32_t msgLen, void *data); + MacAddress macAddress; + bool isPortInitDone(DBConnector *app_db); + uint16_t getStpMaxInstances(void); + +private: + Table m_cfgStpGlobalTable; + Table m_cfgStpVlanTable; + Table m_cfgStpVlanPortTable; + Table m_cfgStpPortTable; + Table m_cfgLagMemberTable; + Table m_cfgVlanMemberTable; + Table m_stateVlanTable; + Table m_stateVlanMemberTable; + Table m_stateLagTable; + Table m_stateStpTable; + + std::bitset l2InstPool; + int stpd_fd; + enum L2_PROTO_MODE l2ProtoEnabled; + int m_vlanInstMap[MAX_VLANS]; + bool portCfgDone; + uint16_t max_stp_instances; + std::map m_lagMap; + + bool stpGlobalTask; + bool stpVlanTask; + bool stpVlanPortTask; + bool stpPortTask; + + void doTask(Consumer &consumer); + void doStpGlobalTask(Consumer &consumer); + void doStpVlanTask(Consumer &consumer); + void doStpVlanPortTask(Consumer &consumer); + void doStpPortTask(Consumer &consumer); + void doVlanMemUpdateTask(Consumer &consumer); + void doLagMemUpdateTask(Consumer &consumer); + + bool isVlanStateOk(const std::string &alias); + bool isLagStateOk(const std::string &alias); + bool isStpPortEmpty(); + bool isStpEnabled(const std::string &intf_name); + int getAllVlanMem(const std::string &vlanKey, std::vector&port_list); + int getAllPortVlan(const std::string &intfKey, std::vector&vlan_list); + int8_t getVlanMemMode(const std::string &key); + int allocL2Instance(uint32_t vlan_id); + void deallocL2Instance(uint32_t vlan_id); + bool isLagEmpty(const std::string &key); + void processStpPortAttr(const std::string op, std::vector&tupEntry, const std::string intfName); + void processStpVlanPortAttr(const std::string op, uint32_t vlan_id, const std::string intfName, + std::vector&tupEntry); +}; + +} +#endif diff --git a/cfgmgr/stpmgrd.cpp b/cfgmgr/stpmgrd.cpp new file mode 100644 index 0000000000..bd20b2a3fc --- /dev/null +++ b/cfgmgr/stpmgrd.cpp @@ -0,0 +1,119 @@ +#include + +#include "stpmgr.h" +#include "netdispatcher.h" +#include "netlink.h" +#include "select.h" +#include "warm_restart.h" + +using namespace std; +using namespace swss; + +bool gSwssRecord = false; +bool gLogRotate = false; +ofstream gRecordOfs; +string gRecordFile; + +#define SELECT_TIMEOUT 1000 + +int main(int argc, char **argv) +{ + Logger::linkToDbNative("stpmgrd"); + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("--- Starting stpmgrd ---"); + + if (fopen("/stpmgrd_dbg_reload", "r")) + { + Logger::setMinPrio(Logger::SWSS_DEBUG); + } + + try + { + DBConnector conf_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector app_db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector state_db(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + + WarmStart::initialize("stpmgrd", "stpd"); + WarmStart::checkWarmStart("stpmgrd", "stpd"); + + // Config DB Tables + TableConnector conf_stp_global_table(&conf_db, CFG_STP_GLOBAL_TABLE_NAME); + TableConnector conf_stp_vlan_table(&conf_db, CFG_STP_VLAN_TABLE_NAME); + TableConnector conf_stp_vlan_port_table(&conf_db, CFG_STP_VLAN_PORT_TABLE_NAME); + TableConnector conf_stp_port_table(&conf_db, CFG_STP_PORT_TABLE_NAME); + + // VLAN DB Tables + TableConnector state_vlan_member_table(&state_db, STATE_VLAN_MEMBER_TABLE_NAME); + + // LAG Tables + TableConnector conf_lag_member_table(&conf_db, CFG_LAG_MEMBER_TABLE_NAME); + + vector tables = { + conf_stp_global_table, + conf_stp_vlan_table, + conf_stp_vlan_port_table, + conf_stp_port_table, + conf_lag_member_table, + state_vlan_member_table + }; + + + StpMgr stpmgr(&conf_db, &app_db, &state_db, tables); + + // Open a Unix Domain Socket with STPd for communication + stpmgr.ipcInitStpd(); + stpmgr.isPortInitDone(&app_db); + + // Get max STP instances from state DB and send to stpd + STP_INIT_READY_MSG msg; + memset(&msg, 0, sizeof(STP_INIT_READY_MSG)); + msg.max_stp_instances = stpmgr.getStpMaxInstances(); + stpmgr.sendMsgStpd(STP_INIT_READY, sizeof(msg), (void *)&msg); + + // Get Base MAC + Table table(&conf_db, "DEVICE_METADATA"); + std::vector ovalues; + table.get("localhost", ovalues); + auto it = std::find_if( ovalues.begin(), ovalues.end(), [](const FieldValueTuple& t){ return t.first == "mac";} ); + if ( it == ovalues.end() ) { + throw runtime_error("couldn't find MAC address of the device from config DB"); + } + stpmgr.macAddress = MacAddress(it->second); + + vector cfgOrchList = {&stpmgr}; + + Select s; + for (Orch *o: cfgOrchList) + { + s.addSelectables(o->getSelectables()); + } + + while (true) + { + Selectable *sel; + int ret; + + ret = s.select(&sel, SELECT_TIMEOUT); + if (ret == Select::ERROR) + { + SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); + continue; + } + if (ret == Select::TIMEOUT) + { + stpmgr.doTask(); + continue; + } + + auto *c = (Executor *)sel; + c->execute(); + } + } + catch (const exception &e) + { + SWSS_LOG_ERROR("Runtime error: %s", e.what()); + } + + return -1; +} diff --git a/cfgmgr/vlanmgr.cpp b/cfgmgr/vlanmgr.cpp index ffef1e4148..09f9d3e9f3 100644 --- a/cfgmgr/vlanmgr.cpp +++ b/cfgmgr/vlanmgr.cpp @@ -21,8 +21,9 @@ using namespace swss; extern MacAddress gMacAddress; -VlanMgr::VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : - Orch(cfgDb, tableNames), +VlanMgr::VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames, + const vector &stateTableNames) : + Orch(cfgDb, stateDb, tableNames, stateTableNames), m_cfgVlanTable(cfgDb, CFG_VLAN_TABLE_NAME), m_cfgVlanMemberTable(cfgDb, CFG_VLAN_MEMBER_TABLE_NAME), m_statePortTable(stateDb, STATE_PORT_TABLE_NAME), @@ -31,6 +32,8 @@ VlanMgr::VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c m_stateVlanMemberTable(stateDb, STATE_VLAN_MEMBER_TABLE_NAME), m_appVlanTableProducer(appDb, APP_VLAN_TABLE_NAME), m_appVlanMemberTableProducer(appDb, APP_VLAN_MEMBER_TABLE_NAME), + m_appFdbTableProducer(appDb, APP_FDB_TABLE_NAME), + m_appPortTableProducer(appDb, APP_PORT_TABLE_NAME), replayDone(false) { SWSS_LOG_ENTER(); @@ -229,10 +232,16 @@ bool VlanMgr::addHostVlanMember(int vlan_id, const string &port_alias, const str } catch (const std::runtime_error& e) { - if (!isMemberStateOk(port_alias)) + // Race conidtion can happen with portchannel removal might happen + // but state db is not updated yet so we can do retry instead of sending exception + if (!port_alias.compare(0, strlen(LAG_PREFIX), LAG_PREFIX)) + { return false; + } else + { EXEC_WITH_ERROR_THROW(cmds.str(), res); + } } return true; @@ -643,6 +652,7 @@ void VlanMgr::doVlanMemberTask(Consumer &consumer) m_stateVlanMemberTable.set(kfvKey(t), fvVector); m_vlanMemberReplay.erase(kfvKey(t)); + m_PortVlanMember[port_alias][vlan_alias] = tagging_mode; } else { @@ -661,6 +671,7 @@ void VlanMgr::doVlanMemberTask(Consumer &consumer) key += port_alias; m_appVlanMemberTableProducer.del(key); m_stateVlanMemberTable.del(kfvKey(t)); + m_PortVlanMember[port_alias].erase(vlan_alias); } else { @@ -687,6 +698,257 @@ void VlanMgr::doVlanMemberTask(Consumer &consumer) } } +void VlanMgr::doVlanPacPortTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto &t = it->second; + string alias = kfvKey(t); + string op = kfvOp(t); + + SWSS_LOG_DEBUG("processing %s operation %s", alias.c_str(), + op.empty() ? "none" : op.c_str()); + + if (op == SET_COMMAND) + { + string learn_mode; + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "learn_mode") + { + learn_mode = fvValue(i); + } + } + if (!learn_mode.empty()) + { + SWSS_LOG_NOTICE("set port learn mode port %s learn_mode %s\n", alias.c_str(), learn_mode.c_str()); + vector fvVector; + FieldValueTuple portLearnMode("learn_mode", learn_mode); + fvVector.push_back(portLearnMode); + m_appPortTableProducer.set(alias, fvVector); + } + } + else if (op == DEL_COMMAND) + { + if (isMemberStateOk(alias)) + { + vector fvVector; + FieldValueTuple portLearnMode("learn_mode", "hardware"); + fvVector.push_back(portLearnMode); + m_appPortTableProducer.set(alias, fvVector); + } + } + it = consumer.m_toSync.erase(it); + } +} + +void VlanMgr::doVlanPacFdbTask(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); + + SWSS_LOG_NOTICE("VlanMgr process static MAC vlan: %s mac: %s ", keys[0].c_str(), keys[1].c_str()); + + int vlan_id; + vlan_id = stoi(keys[0].substr(4)); + + if (!m_vlans.count(keys[0])) + { + SWSS_LOG_NOTICE("Vlan %s not available yet, mac %s", keys[0].c_str(), keys[1].c_str()); + 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, discard = "false", type = "static"; + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "port") + { + port = fvValue(i); + } + if (fvField(i) == "discard") + { + discard = fvValue(i); + } + if (fvField(i) == "type") + { + type = fvValue(i); + } + } + SWSS_LOG_NOTICE("PAC FDB SET %s port %s discard %s type %s\n", + key.c_str(), port.c_str(), discard.c_str(), type.c_str()); + vector fvVector; + FieldValueTuple p("port", port); + fvVector.push_back(p); + FieldValueTuple t("type", type); + fvVector.push_back(t); + FieldValueTuple d("discard", discard); + fvVector.push_back(d); + + m_appFdbTableProducer.set(key, fvVector); + } + else if (op == DEL_COMMAND) + { + m_appFdbTableProducer.del(key); + } + it = consumer.m_toSync.erase(it); + } +} + +void VlanMgr::doVlanPacVlanMemberTask(Consumer &consumer) +{ + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto &t = it->second; + + string key = kfvKey(t); + + key = key.substr(4); + size_t found = key.find(CONFIGDB_KEY_SEPARATOR); + int vlan_id = 0; + string vlan_alias, port_alias; + if (found != string::npos) + { + vlan_id = stoi(key.substr(0, found)); + port_alias = key.substr(found+1); + } + + vlan_alias = VLAN_PREFIX + to_string(vlan_id); + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + /* Don't proceed if member port/lag is not ready yet */ + if (!isMemberStateOk(port_alias) || !isVlanStateOk(vlan_alias)) + { + SWSS_LOG_DEBUG("%s not ready, delaying", kfvKey(t).c_str()); + it++; + continue; + } + string tagging_mode = "untagged"; + auto vlans = m_PortVlanMember[port_alias]; + for (const auto& vlan : vlans) + { + string vlan_alias = vlan.first; + removePortFromVlan(port_alias, vlan_alias); + } + SWSS_LOG_NOTICE("Add Vlan Member key: %s", kfvKey(t).c_str()); + if (addHostVlanMember(vlan_id, port_alias, tagging_mode)) + { + key = VLAN_PREFIX + to_string(vlan_id); + key += DEFAULT_KEY_SEPARATOR; + key += port_alias; + vector fvVector = kfvFieldsValues(t); + FieldValueTuple s("dynamic", "yes"); + fvVector.push_back(s); + m_appVlanMemberTableProducer.set(key, fvVector); + + vector fvVector1; + FieldValueTuple s1("state", "ok"); + fvVector.push_back(s1); + m_stateVlanMemberTable.set(kfvKey(t), fvVector); + } + } + else if (op == DEL_COMMAND) + { + if (isVlanMemberStateOk(kfvKey(t))) + { + SWSS_LOG_NOTICE("Remove Vlan Member key: %s", kfvKey(t).c_str()); + removeHostVlanMember(vlan_id, port_alias); + key = VLAN_PREFIX + to_string(vlan_id); + key += DEFAULT_KEY_SEPARATOR; + key += port_alias; + m_appVlanMemberTableProducer.del(key); + m_stateVlanMemberTable.del(kfvKey(t)); + } + + auto vlans = m_PortVlanMember[port_alias]; + for (const auto& vlan : vlans) + { + string vlan_alias = vlan.first; + string tagging_mode = vlan.second; + SWSS_LOG_NOTICE("Add Vlan Member vlan: %s port %s tagging_mode %s", + vlan_alias.c_str(), port_alias.c_str(), tagging_mode.c_str()); + addPortToVlan(port_alias, vlan_alias, tagging_mode); + } + } + /* Other than the case of member port/lag is not ready, no retry will be performed */ + it = consumer.m_toSync.erase(it); + } +} + +void VlanMgr::addPortToVlan(const std::string& membername, const std::string& vlan_alias, + const std::string& tagging_mode) +{ + SWSS_LOG_NOTICE("member %s vlan %s tagging_mode %s", + membername.c_str(), vlan_alias.c_str(), tagging_mode.c_str()); + int vlan_id = stoi(vlan_alias.substr(4)); + if (addHostVlanMember(vlan_id, membername, tagging_mode)) + { + std::string key = VLAN_PREFIX + to_string(vlan_id); + key += DEFAULT_KEY_SEPARATOR; + key += membername; + vector fvVector; + FieldValueTuple s("tagging_mode", tagging_mode); + fvVector.push_back(s); + FieldValueTuple s1("dynamic", "no"); + fvVector.push_back(s1); + SWSS_LOG_INFO("key: %s\n", key.c_str()); + m_appVlanMemberTableProducer.set(key, fvVector); + + vector fvVector1; + FieldValueTuple s2("state", "ok"); + fvVector1.push_back(s2); + key = VLAN_PREFIX + to_string(vlan_id); + key += '|'; + key += membername; + m_stateVlanMemberTable.set(key, fvVector1); + } +} + +void VlanMgr::removePortFromVlan(const std::string& membername, const std::string& vlan_alias) +{ + SWSS_LOG_NOTICE("member %s vlan %s", + membername.c_str(), vlan_alias.c_str()); + int vlan_id = stoi(vlan_alias.substr(4)); + std::string key = VLAN_PREFIX + to_string(vlan_id); + key += '|'; + key += membername; + if (isVlanMemberStateOk(key)) + { + key = VLAN_PREFIX + to_string(vlan_id); + key += ':'; + key += membername; + SWSS_LOG_INFO("key: %s\n", key.c_str()); + m_appVlanMemberTableProducer.del(key); + + key = VLAN_PREFIX + to_string(vlan_id); + key += '|'; + key += membername; + m_stateVlanMemberTable.del(key); + } +} + void VlanMgr::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -701,6 +963,18 @@ void VlanMgr::doTask(Consumer &consumer) { doVlanMemberTask(consumer); } + else if (table_name == STATE_OPER_PORT_TABLE_NAME) + { + doVlanPacPortTask(consumer); + } + else if (table_name == STATE_OPER_FDB_TABLE_NAME) + { + doVlanPacFdbTask(consumer); + } + else if (table_name == STATE_OPER_VLAN_MEMBER_TABLE_NAME) + { + doVlanPacVlanMemberTask(consumer); + } else { SWSS_LOG_ERROR("Unknown config table %s ", table_name.c_str()); diff --git a/cfgmgr/vlanmgr.h b/cfgmgr/vlanmgr.h index 8cf467f41c..7fce59ce65 100644 --- a/cfgmgr/vlanmgr.h +++ b/cfgmgr/vlanmgr.h @@ -14,11 +14,13 @@ namespace swss { class VlanMgr : public Orch { public: - VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const std::vector &tableNames); + VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const std::vector &tableNames, + const std::vector &stateTableNames); using Orch::doTask; private: ProducerStateTable m_appVlanTableProducer, m_appVlanMemberTableProducer; + ProducerStateTable m_appFdbTableProducer, m_appPortTableProducer; Table m_cfgVlanTable, m_cfgVlanMemberTable; Table m_statePortTable, m_stateLagTable; Table m_stateVlanTable, m_stateVlanMemberTable; @@ -26,6 +28,7 @@ class VlanMgr : public Orch std::set m_vlanReplay; std::set m_vlanMemberReplay; bool replayDone; + std::unordered_map> m_PortVlanMember; void doTask(Consumer &consumer); void doVlanTask(Consumer &consumer); @@ -43,6 +46,11 @@ class VlanMgr : public Orch bool isVlanStateOk(const std::string &alias); bool isVlanMacOk(); bool isVlanMemberStateOk(const std::string &vlanMemberKey); + void doVlanPacPortTask(Consumer &consumer); + void doVlanPacFdbTask(Consumer &consumer); + void doVlanPacVlanMemberTask(Consumer &consumer); + void addPortToVlan(const std::string& port_alias, const std::string& vlan_alias, const std::string& tagging_mode); + void removePortFromVlan(const std::string& port_alias, const std::string& vlan_alias); }; } diff --git a/cfgmgr/vlanmgrd.cpp b/cfgmgr/vlanmgrd.cpp index 84bc19cf08..d430063247 100644 --- a/cfgmgr/vlanmgrd.cpp +++ b/cfgmgr/vlanmgrd.cpp @@ -36,7 +36,11 @@ int main(int argc, char **argv) CFG_VLAN_TABLE_NAME, CFG_VLAN_MEMBER_TABLE_NAME, }; - + vector state_vlan_tables = { + STATE_OPER_PORT_TABLE_NAME, + STATE_OPER_FDB_TABLE_NAME, + STATE_OPER_VLAN_MEMBER_TABLE_NAME + }; DBConnector cfgDb("CONFIG_DB", 0); DBConnector appDb("APPL_DB", 0); DBConnector stateDb("STATE_DB", 0); @@ -58,7 +62,7 @@ int main(int argc, char **argv) } gMacAddress = MacAddress(it->second); - VlanMgr vlanmgr(&cfgDb, &appDb, &stateDb, cfg_vlan_tables); + VlanMgr vlanmgr(&cfgDb, &appDb, &stateDb, cfg_vlan_tables, state_vlan_tables); std::vector cfgOrchList = {&vlanmgr}; diff --git a/configure.ac b/configure.ac index 6edc02da91..145231749c 100644 --- a/configure.ac +++ b/configure.ac @@ -20,6 +20,11 @@ AC_CHECK_LIB([team], [team_alloc], PKG_CHECK_MODULES([JANSSON], [jansson]) +AC_CHECK_FILE([/usr/include/stp_ipc.h], + AM_CONDITIONAL(HAVE_STP, true), + [AC_MSG_WARN([stp is not installed.]) + AM_CONDITIONAL(HAVE_STP, false)]) + AC_CHECK_LIB([sai], [sai_object_type_query], AM_CONDITIONAL(HAVE_SAI, true), [AC_MSG_WARN([libsai is not installed.]) @@ -54,7 +59,7 @@ AC_CHECK_LIB([nl-genl-3], [nl_socket_get_cb]) AC_CHECK_LIB([nl-route-3], [rtnl_route_nh_get_encap_mpls_dst]) AC_CHECK_LIB([nl-nf-3], [nfnl_connect]) -CFLAGS_COMMON="-std=c++14 -Wall -fPIC -Wno-write-strings -I/usr/include/swss" +CFLAGS_COMMON="-std=c++14 -Wall -fPIC -Wno-write-strings -I/usr/include/swss -I/usr/include" AC_ARG_WITH(libnl-3.0-inc, [ --with-libnl-3.0-inc=DIR @@ -101,6 +106,7 @@ CFLAGS_COMMON+=" -Wvariadic-macros" CFLAGS_COMMON+=" -Wno-switch-default" CFLAGS_COMMON+=" -Wno-long-long" CFLAGS_COMMON+=" -Wno-redundant-decls" +CFLAGS_COMMON+=" -Wno-error=missing-field-initializers" # Code testing coverage with gcov AC_MSG_CHECKING(whether to build with gcov testing) diff --git a/dev/Dockerfile.yml b/dev/Dockerfile.yml new file mode 100644 index 0000000000..acb0d9054b --- /dev/null +++ b/dev/Dockerfile.yml @@ -0,0 +1,92 @@ +ARG DEBIAN_VERSION="bookworm" +FROM sonicdev-microsoft.azurecr.io:443/sonic-slave-${DEBIAN_VERSION}:latest + +ARG UID=1000 +ARG GID=1000 + +RUN groupadd -g ${GID} sonicdev && \ + useradd -u ${UID} -g ${GID} -ms /bin/bash sonicdev + +RUN mkdir -p /workspace && \ + mkdir -p /workspace/debs && \ + mkdir -p /workspace/tools && \ + chown -R sonicdev:sonicdev /workspace + +ENV PATH="${PATH}:/workspace/tools" + +RUN apt-get update && \ + sudo apt-get install -y \ + libhiredis-dev \ + libzmq3-dev \ + swig4.0 \ + libdbus-1-dev \ + libteam-dev \ + protobuf-compiler \ + libprotobuf-dev && \ + sudo pip3 install lcov_cobertura + +COPY dev/download_artifact.sh /workspace/tools/download_artifact.sh + +WORKDIR /workspace/debs + +ARG BRANCH_NAME="master" +ARG PLATFORM="amd64" +ARG DEBIAN_VERSION + +# SWSS COMMON + +ARG SWSS_COMMON_PROJECT_NAME="Azure.sonic-swss-common" +ARG SWSS_COMMON_ARTIFACT_NAME="sonic-swss-common" +ARG SWSS_COMMON_FILE_PATHS="/libswsscommon_1.0.0_${PLATFORM}.deb /libswsscommon-dev_1.0.0_${PLATFORM}.deb" + +RUN download_artifact.sh "${SWSS_COMMON_PROJECT_NAME}" "${BRANCH_NAME}" "${SWSS_COMMON_ARTIFACT_NAME}" "${SWSS_COMMON_FILE_PATHS}" + +# SAIREDIS + +ARG SAIREDIS_PROJECT_NAME="Azure.sonic-sairedis" +ARG SAIREDIS_ARTIFACT_NAME="sonic-sairedis" +ARG SAIREDIS_FILE_PATHS="\ + /libsaivs_1.0.0_${PLATFORM}.deb \ + /libsaivs-dev_1.0.0_${PLATFORM}.deb \ + /libsairedis_1.0.0_${PLATFORM}.deb \ + /libsairedis-dev_1.0.0_${PLATFORM}.deb \ + /libsaimetadata_1.0.0_${PLATFORM}.deb \ + /libsaimetadata-dev_1.0.0_${PLATFORM}.deb \ + /syncd-vs_1.0.0_${PLATFORM}.deb \ + " + +RUN download_artifact.sh "${SAIREDIS_PROJECT_NAME}" "${BRANCH_NAME}" "${SAIREDIS_ARTIFACT_NAME}" "${SAIREDIS_FILE_PATHS}" + +# COMMON LIB + +ARG COMMON_LIB_PROJECT_NAME="Azure.sonic-buildimage.common_libs" +ARG COMMON_LIB_ARTIFACT_NAME="common-lib" +ARG COMMON_LIB_FILE_PATHS="\ + /target/debs/${DEBIAN_VERSION}/libnl-3-200_3.7.0-0.2%2Bb1sonic1_${PLATFORM}.deb \ + /target/debs/${DEBIAN_VERSION}/libnl-3-dev_3.7.0-0.2%2Bb1sonic1_${PLATFORM}.deb \ + /target/debs/${DEBIAN_VERSION}/libnl-genl-3-200_3.7.0-0.2%2Bb1sonic1_${PLATFORM}.deb \ + /target/debs/${DEBIAN_VERSION}/libnl-genl-3-dev_3.7.0-0.2%2Bb1sonic1_${PLATFORM}.deb \ + /target/debs/${DEBIAN_VERSION}/libnl-route-3-200_3.7.0-0.2%2Bb1sonic1_${PLATFORM}.deb \ + /target/debs/${DEBIAN_VERSION}/libnl-route-3-dev_3.7.0-0.2%2Bb1sonic1_${PLATFORM}.deb \ + /target/debs/${DEBIAN_VERSION}/libnl-nf-3-200_3.7.0-0.2%2Bb1sonic1_${PLATFORM}.deb \ + /target/debs/${DEBIAN_VERSION}/libnl-nf-3-dev_3.7.0-0.2%2Bb1sonic1_${PLATFORM}.deb \ + /target/debs/${DEBIAN_VERSION}/libyang_1.0.73_${PLATFORM}.deb \ + " + +RUN download_artifact.sh "${COMMON_LIB_PROJECT_NAME}" "${BRANCH_NAME}" "${COMMON_LIB_ARTIFACT_NAME}" "${COMMON_LIB_FILE_PATHS}" + +# DASH API + +ARG DASH_API_PROJECT_NAME="sonic-net.sonic-dash-api" +ARG DASH_API_ARTIFACT_NAME="sonic-dash-api" +ARG DASH_API_FILE_PATHS="/libdashapi_1.0.0_${PLATFORM}.deb" + +RUN download_artifact.sh "${DASH_API_PROJECT_NAME}" "${BRANCH_NAME}" "${DASH_API_ARTIFACT_NAME}" "${DASH_API_FILE_PATHS}" + +RUN dpkg -i *.deb + +WORKDIR /workspace + +USER sonicdev + +ENTRYPOINT [ "bash" ] diff --git a/dev/docker-compose.yml b/dev/docker-compose.yml new file mode 100644 index 0000000000..ce51eb6781 --- /dev/null +++ b/dev/docker-compose.yml @@ -0,0 +1,18 @@ +services: + sonicdev: + container_name: sonicdev + build: + context: .. + dockerfile: dev/Dockerfile.yml + args: + - DEBIAN_VERSION + - UID + - GID + - BRANCH_NAME + - PLATFORM + volumes: + - ..:/workspace/sonic-swss + init: true + privileged: true + working_dir: /workspace/sonic-swss + diff --git a/dev/download_artifact.sh b/dev/download_artifact.sh new file mode 100755 index 0000000000..282ca50475 --- /dev/null +++ b/dev/download_artifact.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# download_artifact.sh +# +# targetPaths: space separated list of target paths to download from the artifact +# e.g. +# ./download_artifact.sh "Azure.sonic-swss-common" "master" "sonic-swss-common" "/libswsscommon-dev_1.0.0_amd64.deb /libswsscommon_1.0.0_amd64.deb" + +set -x -e + +pipelineName=${1} +branchName=${2} +artifactName=${3} +targetPaths=${4} + +queryPipelinesUrl="https://dev.azure.com/mssonic/build/_apis/pipelines" + +definitions=$(curl -s "${queryPipelinesUrl}" | jq -r ".value[] | select (.name == \"${pipelineName}\").id") + +queryBuildsUrl="https://dev.azure.com/mssonic/build/_apis/build/builds?definitions=${definitions}&branchName=refs/heads/${branchName}&resultFilter=succeeded&statusFilter=completed&api-version=6.0" + +buildId=$(curl -s ${queryBuildsUrl} | jq -r '.value[0].id') + +queryArtifactUrl="https://dev.azure.com/mssonic/build/_apis/build/builds/${buildId}/artifacts?artifactName=${artifactName}&api-version=6.0" + +function download_artifact { + + target_path=${1} + output_file=$(sed 's/.*\///' <<< ${target_path}) + + download_artifact_url=$(curl -s ${queryArtifactUrl} | jq -r '.resource.downloadUrl') + download_artifact_url=$(sed 's/zip$/file/' <<< ${download_artifact_url}) + download_artifact_url="$download_artifact_url&subPath=${target_path}" + + wget -O ${output_file} ${download_artifact_url} +} + +function download_artifacts { + target_paths_array=(${targetPaths}) + for target_path in "${target_paths_array[@]}" + do + download_artifact ${target_path} + done +} + +download_artifacts diff --git a/fpmsyncd/fpm/fpm.h b/fpmsyncd/fpm/fpm.h index 8af9b30ae9..11f13b168a 100644 --- a/fpmsyncd/fpm/fpm.h +++ b/fpmsyncd/fpm/fpm.h @@ -92,7 +92,7 @@ /* * Largest message that can be sent to or received from the FPM. */ -#define FPM_MAX_MSG_LEN 4096 +#define FPM_MAX_MSG_LEN 16384 /* * Header that precedes each fpm message to/from the FPM. diff --git a/fpmsyncd/fpmsyncd.cpp b/fpmsyncd/fpmsyncd.cpp index c3479efc84..f01bebe203 100644 --- a/fpmsyncd/fpmsyncd.cpp +++ b/fpmsyncd/fpmsyncd.cpp @@ -98,6 +98,7 @@ int main(int argc, char **argv) NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, &sync); rtnl_route_read_protocol_names(DefaultRtProtoPath); + nlmsg_set_default_size(FPM_MAX_MSG_LEN); std::string suppressionEnabledStr; deviceMetadataTable.hget("localhost", "suppress-fib-pending", suppressionEnabledStr); diff --git a/fpmsyncd/routesync.cpp b/fpmsyncd/routesync.cpp index 4d6aa708ab..cea63dc42f 100644 --- a/fpmsyncd/routesync.cpp +++ b/fpmsyncd/routesync.cpp @@ -2235,12 +2235,23 @@ bool RouteSync::sendOffloadReply(struct nlmsghdr* hdr) bool RouteSync::sendOffloadReply(struct rtnl_route* route_obj) { SWSS_LOG_ENTER(); + int ret = 0; nl_msg* msg{}; - rtnl_route_build_add_request(route_obj, NLM_F_CREATE, &msg); + ret = rtnl_route_build_add_request(route_obj, NLM_F_CREATE, &msg); + if (ret !=0) + { + SWSS_LOG_ERROR("Route build add returned %d", ret); + return false; + } auto nlMsg = makeUniqueWithDestructor(msg, nlmsg_free); + if (nlMsg.get() == NULL) + { + SWSS_LOG_ERROR("Error in allocation for sending offload reply"); + return false; + } return sendOffloadReply(nlmsg_hdr(nlMsg.get())); } diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index becdb9db89..f94e6d3bfa 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -114,7 +114,8 @@ orchagent_SOURCES = \ dash/dashaclgroupmgr.cpp \ dash/dashtagmgr.cpp \ dash/pbutils.cpp \ - twamporch.cpp + twamporch.cpp \ + stporch.cpp orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp flex_counter/flow_counter_handler.cpp flex_counter/flowcounterrouteorch.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp diff --git a/orchagent/bufferorch.cpp b/orchagent/bufferorch.cpp index ccbc8e9b25..fb38cfe447 100644 --- a/orchagent/bufferorch.cpp +++ b/orchagent/bufferorch.cpp @@ -360,6 +360,25 @@ const object_reference_map &BufferOrch::getBufferPoolNameOidMap(void) return *m_buffer_type_maps[APP_BUFFER_POOL_TABLE_NAME]; } +void BufferOrch::getBufferObjectsWithNonZeroProfile(vector &nonZeroQueues, const string &table) +{ + for (auto &&queueRef: (*m_buffer_type_maps[table])) + { + for (auto &&profileRef: queueRef.second.m_objsReferencingByMe) + { + if (profileRef.second.find("_zero_") == std::string::npos) + { + SWSS_LOG_INFO("Selected key %s with profile %s", queueRef.first.c_str(), profileRef.second.c_str()); + nonZeroQueues.push_back(queueRef.first); + } + else + { + SWSS_LOG_INFO("Skipped key %s with profile %s", queueRef.first.c_str(), profileRef.second.c_str()); + } + } + } +} + task_process_status BufferOrch::processBufferPool(KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); @@ -797,6 +816,9 @@ task_process_status BufferOrch::processQueue(KeyOpFieldsValuesTuple &tuple) sai_uint32_t range_low, range_high; bool need_update_sai = true; bool local_port = false; + bool counter_was_added = false; + bool counter_needs_to_add = false; + string old_buffer_profile_name; string local_port_name; SWSS_LOG_DEBUG("Processing:%s", key.c_str()); @@ -856,7 +878,6 @@ task_process_status BufferOrch::processQueue(KeyOpFieldsValuesTuple &tuple) return task_process_status::task_failed; } - string old_buffer_profile_name; if (doesObjectExist(m_buffer_type_maps, APP_BUFFER_QUEUE_TABLE_NAME, key, buffer_profile_field_name, old_buffer_profile_name) && (old_buffer_profile_name == buffer_profile_name)) { @@ -874,11 +895,14 @@ task_process_status BufferOrch::processQueue(KeyOpFieldsValuesTuple &tuple) SWSS_LOG_NOTICE("Set buffer queue %s to %s", key.c_str(), buffer_profile_name.c_str()); setObjectReference(m_buffer_type_maps, APP_BUFFER_QUEUE_TABLE_NAME, key, buffer_profile_field_name, buffer_profile_name); + + // Counter operation + counter_needs_to_add = buffer_profile_name.find("_zero_") == std::string::npos; + SWSS_LOG_INFO("%s to create counter for %s with new profile %s", counter_needs_to_add ? "Need" : "No need", key.c_str(), buffer_profile_name.c_str()); } else if (op == DEL_COMMAND) { - auto &typemap = (*m_buffer_type_maps[APP_BUFFER_QUEUE_TABLE_NAME]); - if (typemap.find(key) == typemap.end()) + if (!doesObjectExist(m_buffer_type_maps, APP_BUFFER_QUEUE_TABLE_NAME, key, buffer_profile_field_name, old_buffer_profile_name)) { SWSS_LOG_INFO("%s doesn't not exist, don't need to notfiy SAI", key.c_str()); need_update_sai = false; @@ -887,6 +911,7 @@ task_process_status BufferOrch::processQueue(KeyOpFieldsValuesTuple &tuple) SWSS_LOG_NOTICE("Remove buffer queue %s", key.c_str()); removeObject(m_buffer_type_maps, APP_BUFFER_QUEUE_TABLE_NAME, key); m_partiallyAppliedQueues.erase(key); + counter_needs_to_add = false; } else { @@ -894,6 +919,9 @@ task_process_status BufferOrch::processQueue(KeyOpFieldsValuesTuple &tuple) return task_process_status::task_invalid_entry; } + counter_was_added = !old_buffer_profile_name.empty() && old_buffer_profile_name.find("_zero_") == std::string::npos; + SWSS_LOG_INFO("%s to remove counter for %s with old profile %s", counter_was_added ? "Need" : "No need", key.c_str(), old_buffer_profile_name.c_str()); + sai_attribute_t attr; attr.id = SAI_QUEUE_ATTR_BUFFER_PROFILE_ID; attr.value.oid = sai_buffer_profile; @@ -963,14 +991,16 @@ task_process_status BufferOrch::processQueue(KeyOpFieldsValuesTuple &tuple) { auto flexCounterOrch = gDirectory.get(); auto queues = tokens[1]; - if (op == SET_COMMAND && + if (!counter_was_added && counter_needs_to_add && (flexCounterOrch->getQueueCountersState() || flexCounterOrch->getQueueWatermarkCountersState())) { + SWSS_LOG_INFO("Creating counters for %s %zd", port_name.c_str(), ind); gPortsOrch->createPortBufferQueueCounters(port, queues); } - else if (op == DEL_COMMAND && + else if (counter_was_added && !counter_needs_to_add && (flexCounterOrch->getQueueCountersState() || flexCounterOrch->getQueueWatermarkCountersState())) { + SWSS_LOG_INFO("Removing counters for %s %zd", port_name.c_str(), ind); gPortsOrch->removePortBufferQueueCounters(port, queues); } } @@ -1057,6 +1087,9 @@ task_process_status BufferOrch::processPriorityGroup(KeyOpFieldsValuesTuple &tup vector tokens; sai_uint32_t range_low, range_high; bool need_update_sai = true; + bool counter_was_added = false; + bool counter_needs_to_add = false; + string old_buffer_profile_name; SWSS_LOG_DEBUG("processing:%s", key.c_str()); tokens = tokenize(key, delimiter); @@ -1089,7 +1122,6 @@ task_process_status BufferOrch::processPriorityGroup(KeyOpFieldsValuesTuple &tup return task_process_status::task_failed; } - string old_buffer_profile_name; if (doesObjectExist(m_buffer_type_maps, APP_BUFFER_PG_TABLE_NAME, key, buffer_profile_field_name, old_buffer_profile_name) && (old_buffer_profile_name == buffer_profile_name)) { @@ -1100,11 +1132,14 @@ task_process_status BufferOrch::processPriorityGroup(KeyOpFieldsValuesTuple &tup SWSS_LOG_NOTICE("Set buffer PG %s to %s", key.c_str(), buffer_profile_name.c_str()); setObjectReference(m_buffer_type_maps, APP_BUFFER_PG_TABLE_NAME, key, buffer_profile_field_name, buffer_profile_name); + + // Counter operation + counter_needs_to_add = buffer_profile_name.find("_zero_") == std::string::npos; + SWSS_LOG_INFO("%s to create counter for priority group %s with new profile %s", counter_needs_to_add ? "Need" : "No need", key.c_str(), buffer_profile_name.c_str()); } else if (op == DEL_COMMAND) { - auto &typemap = (*m_buffer_type_maps[APP_BUFFER_PG_TABLE_NAME]); - if (typemap.find(key) == typemap.end()) + if (!doesObjectExist(m_buffer_type_maps, APP_BUFFER_PG_TABLE_NAME, key, buffer_profile_field_name, old_buffer_profile_name)) { SWSS_LOG_INFO("%s doesn't not exist, don't need to notfiy SAI", key.c_str()); need_update_sai = false; @@ -1119,6 +1154,9 @@ task_process_status BufferOrch::processPriorityGroup(KeyOpFieldsValuesTuple &tup return task_process_status::task_invalid_entry; } + counter_was_added = !old_buffer_profile_name.empty() && old_buffer_profile_name.find("_zero_") == std::string::npos; + SWSS_LOG_INFO("%s to remove counter for priority group %s with old profile %s", counter_was_added ? "Need" : "No need", key.c_str(), old_buffer_profile_name.c_str()); + sai_attribute_t attr; attr.id = SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE; attr.value.oid = sai_buffer_profile; @@ -1161,14 +1199,16 @@ task_process_status BufferOrch::processPriorityGroup(KeyOpFieldsValuesTuple &tup { auto flexCounterOrch = gDirectory.get(); auto pgs = tokens[1]; - if (op == SET_COMMAND && + if (!counter_was_added && counter_needs_to_add && (flexCounterOrch->getPgCountersState() || flexCounterOrch->getPgWatermarkCountersState())) { + SWSS_LOG_INFO("Creating counters for priority group %s %zd", port_name.c_str(), ind); gPortsOrch->createPortBufferPgCounters(port, pgs); } - else if (op == DEL_COMMAND && + else if (counter_was_added && !counter_needs_to_add && (flexCounterOrch->getPgCountersState() || flexCounterOrch->getPgWatermarkCountersState())) { + SWSS_LOG_INFO("Removing counters for priority group %s %zd", port_name.c_str(), ind); gPortsOrch->removePortBufferPgCounters(port, pgs); } } diff --git a/orchagent/bufferorch.h b/orchagent/bufferorch.h index f78fe05318..3d255b87dd 100644 --- a/orchagent/bufferorch.h +++ b/orchagent/bufferorch.h @@ -38,6 +38,7 @@ class BufferOrch : public Orch static type_map m_buffer_type_maps; void generateBufferPoolWatermarkCounterIdList(void); const object_reference_map &getBufferPoolNameOidMap(void); + void getBufferObjectsWithNonZeroProfile(vector &nonZeroQueues, const string &table); private: typedef task_process_status (BufferOrch::*buffer_table_handler)(KeyOpFieldsValuesTuple &tuple); diff --git a/orchagent/dash/dashorch.cpp b/orchagent/dash/dashorch.cpp index 03bb69be4b..d9aac53ce4 100644 --- a/orchagent/dash/dashorch.cpp +++ b/orchagent/dash/dashorch.cpp @@ -140,11 +140,19 @@ bool DashOrch::addApplianceEntry(const string& appliance_id, const dash::applian } sai_direction_lookup_entry_t direction_lookup_entry; + vector direction_lookup_attrs; direction_lookup_entry.switch_id = gSwitchId; direction_lookup_entry.vni = entry.vm_vni(); appliance_attr.id = SAI_DIRECTION_LOOKUP_ENTRY_ATTR_ACTION; appliance_attr.value.u32 = SAI_DIRECTION_LOOKUP_ENTRY_ACTION_SET_OUTBOUND_DIRECTION; - status = sai_dash_direction_lookup_api->create_direction_lookup_entry(&direction_lookup_entry, attr_count, &appliance_attr); + direction_lookup_attrs.push_back(appliance_attr); + + appliance_attr.id = SAI_DIRECTION_LOOKUP_ENTRY_ATTR_DASH_ENI_MAC_OVERRIDE_TYPE; + appliance_attr.value.u32 = SAI_DASH_ENI_MAC_OVERRIDE_TYPE_DST_MAC; + direction_lookup_attrs.push_back(appliance_attr); + + status = sai_dash_direction_lookup_api->create_direction_lookup_entry(&direction_lookup_entry, + (uint32_t)direction_lookup_attrs.size(), direction_lookup_attrs.data()); if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to create direction lookup entry for %s", appliance_id.c_str()); diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index 03c854fee3..98236be7d5 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -772,6 +772,7 @@ void FdbOrch::doTask(Consumer& consumer) string esi = ""; unsigned int vni = 0; string sticky = ""; + string discard = "false"; for (auto i : kfvFieldsValues(t)) { @@ -784,6 +785,10 @@ void FdbOrch::doTask(Consumer& consumer) { type = fvValue(i); } + if (fvField(i) == "discard") + { + discard = fvValue(i); + } if(origin == FDB_ORIGIN_VXLAN_ADVERTIZED) { @@ -859,6 +864,7 @@ void FdbOrch::doTask(Consumer& consumer) fdbData.esi = esi; fdbData.vni = vni; fdbData.is_flush_pending = false; + fdbData.discard = discard; if (addFdbEntry(entry, port, fdbData)) { if (origin == FDB_ORIGIN_MCLAG_ADVERTIZED) @@ -1138,6 +1144,35 @@ void FdbOrch::flushFDBEntries(sai_object_id_t bridge_port_oid, } } } +void FdbOrch::flushFdbByVlan(const string &alias) +{ + sai_status_t status; + swss::Port vlan; + sai_attribute_t vlan_attr[2]; + + if (!m_portsOrch->getPort(alias, vlan)) + { + return; + } + + vlan_attr[0].id = SAI_FDB_FLUSH_ATTR_BV_ID; + vlan_attr[0].value.oid = vlan.m_vlan_info.vlan_oid; + 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); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); + } + else + { + SWSS_LOG_INFO("Flush by vlan %s vlan_oid 0x%" PRIx64 "", + alias.c_str(), vlan.m_vlan_info.vlan_oid); + } + + return; +} void FdbOrch::notifyObserversFDBFlush(Port &port, sai_object_id_t& bvid) { @@ -1451,7 +1486,9 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, attrs.push_back(attr); } } - + attr.id = SAI_FDB_ENTRY_ATTR_PACKET_ACTION; + attr.value.s32 = (fdbData.discard == "true") ? SAI_PACKET_ACTION_DROP: SAI_PACKET_ACTION_FORWARD; + attrs.push_back(attr); if (macUpdate) { SWSS_LOG_INFO("MAC-Update FDB %s in %s on from-%s:to-%s from-%s:to-%s origin-%d-to-%d", diff --git a/orchagent/fdborch.h b/orchagent/fdborch.h index 9e71bc8c6b..ef912b8400 100644 --- a/orchagent/fdborch.h +++ b/orchagent/fdborch.h @@ -65,6 +65,7 @@ struct FdbData string esi; unsigned int vni; sai_fdb_entry_type_t sai_fdb_type; + string discard; }; struct SavedFdbEntry @@ -102,6 +103,7 @@ class FdbOrch: public Orch, public Subject, public Observer static const int fdborch_pri; void flushFDBEntries(sai_object_id_t bridge_port_oid, sai_object_id_t vlan_oid); + void flushFdbByVlan(const string &); void notifyObserversFDBFlush(Port &p, sai_object_id_t&); private: diff --git a/orchagent/flexcounterorch.cpp b/orchagent/flexcounterorch.cpp index 0567848e14..bd7f0524e0 100644 --- a/orchagent/flexcounterorch.cpp +++ b/orchagent/flexcounterorch.cpp @@ -377,11 +377,11 @@ map FlexCounterOrch::getQueueConfigurations() } std::vector portQueueKeys; - m_bufferQueueConfigTable.getKeys(portQueueKeys); + gBufferOrch->getBufferObjectsWithNonZeroProfile(portQueueKeys, APP_BUFFER_QUEUE_TABLE_NAME); for (const auto& portQueueKey : portQueueKeys) { - auto toks = tokenize(portQueueKey, '|'); + auto toks = tokenize(portQueueKey, ':'); if (toks.size() != 2) { SWSS_LOG_ERROR("Invalid BUFFER_QUEUE key: [%s]", portQueueKey.c_str()); @@ -446,11 +446,11 @@ map FlexCounterOrch::getPgConfigurations() } std::vector portPgKeys; - m_bufferPgConfigTable.getKeys(portPgKeys); + gBufferOrch->getBufferObjectsWithNonZeroProfile(portPgKeys, APP_BUFFER_PG_TABLE_NAME); for (const auto& portPgKey : portPgKeys) { - auto toks = tokenize(portPgKey, '|'); + auto toks = tokenize(portPgKey, ':'); if (toks.size() != 2) { SWSS_LOG_ERROR("Invalid BUFFER_PG key: [%s]", portPgKey.c_str()); diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index 6c6b2afa78..edcda386bd 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -30,6 +30,20 @@ Orch::Orch(DBConnector *db, const vector &tableNames) } } +Orch::Orch(swss::DBConnector *db1, swss::DBConnector *db2, + const std::vector &tableNames_1, const std::vector &tableNames_2) +{ + for(auto it : tableNames_1) + { + addConsumer(db1, it, default_orch_pri); + } + + for(auto it : tableNames_2) + { + addConsumer(db2, it, default_orch_pri); + } +} + Orch::Orch(DBConnector *db, const vector &tableNames_with_pri) { for (const auto& it : tableNames_with_pri) diff --git a/orchagent/orch.h b/orchagent/orch.h index e960db7e48..cca6b62fae 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -221,6 +221,8 @@ class Orch public: Orch(swss::DBConnector *db, const std::string tableName, int pri = default_orch_pri); Orch(swss::DBConnector *db, const std::vector &tableNames); + Orch(swss::DBConnector *db1, swss::DBConnector *db2, + const std::vector &tableNames_1, const std::vector &tableNames_2); Orch(swss::DBConnector *db, const std::vector &tableNameWithPri); Orch(const std::vector& tables); virtual ~Orch() = default; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 30d7df8e72..13ef89c487 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -11,6 +11,7 @@ #define SAI_SWITCH_ATTR_CUSTOM_RANGE_BASE SAI_SWITCH_ATTR_CUSTOM_RANGE_START #include "sairedis.h" #include "chassisorch.h" +#include "stporch.h" using namespace std; using namespace swss; @@ -64,6 +65,7 @@ FlowCounterRouteOrch *gFlowCounterRouteOrch; DebugCounterOrch *gDebugCounterOrch; MonitorOrch *gMonitorOrch; TunnelDecapOrch *gTunneldecapOrch; +StpOrch *gStpOrch; MuxOrch *gMuxOrch; bool gIsNatSupported = false; @@ -167,6 +169,14 @@ bool OrchDaemon::init() gFlowCounterRouteOrch = new FlowCounterRouteOrch(m_configDb, route_pattern_tables); gDirectory.set(gFlowCounterRouteOrch); + vector stp_tables = { + APP_STP_VLAN_INSTANCE_TABLE_NAME, + APP_STP_PORT_STATE_TABLE_NAME, + APP_STP_FASTAGEING_FLUSH_TABLE_NAME + }; + gStpOrch = new StpOrch(m_applDb, m_stateDb, stp_tables); + gDirectory.set(gStpOrch); + vector vnet_tables = { APP_VNET_RT_TABLE_NAME, APP_VNET_RT_TUNNEL_TABLE_NAME @@ -420,7 +430,7 @@ bool OrchDaemon::init() * when iterating ConsumerMap. This is ensured implicitly by the order of keys in ordered map. * For cases when Orch has to process tables in specific order, like PortsOrch during warm start, it has to override Orch::doTask() */ - m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, gFlowCounterRouteOrch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, gPolicerOrch, gTunneldecapOrch, sflow_orch, gDebugCounterOrch, gMacsecOrch, bgp_global_state_orch, gBfdOrch, gSrv6Orch, gMuxOrch, mux_cb_orch, gMonitorOrch}; + m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, gFlowCounterRouteOrch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, gPolicerOrch, gTunneldecapOrch, sflow_orch, gDebugCounterOrch, gMacsecOrch, bgp_global_state_orch, gBfdOrch, gSrv6Orch, gMuxOrch, mux_cb_orch, gMonitorOrch, gStpOrch}; bool initialize_dtel = false; if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 2473848bf5..6a1c0b999a 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -47,6 +47,7 @@ #include "srv6orch.h" #include "nvgreorch.h" #include "twamporch.h" +#include "stporch.h" #include "dash/dashaclorch.h" #include "dash/dashorch.h" #include "dash/dashrouteorch.h" diff --git a/orchagent/p4orch/tests/mock_sai_stp.h b/orchagent/p4orch/tests/mock_sai_stp.h new file mode 100644 index 0000000000..5ac6397c9c --- /dev/null +++ b/orchagent/p4orch/tests/mock_sai_stp.h @@ -0,0 +1,110 @@ +#ifndef MOCK_SAI_STP_H +#define MOCK_SAI_STP_H + +#include +extern "C" +{ +#include "sai.h" +} + +// Mock class for SAI STP APIs +class MockSaiStp { +public: + // Mock method for creating an STP instance + MOCK_METHOD4(create_stp, + sai_status_t(_Out_ sai_object_id_t *stp_instance_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list)); + + // Mock method for removing an STP instance + MOCK_METHOD1(remove_stp, sai_status_t(_In_ sai_object_id_t stp_instance_id)); + + // Mock method for setting STP instance attributes + MOCK_METHOD2(set_stp_attribute, + sai_status_t(_In_ sai_object_id_t stp_instance_id, + _In_ const sai_attribute_t *attr)); + + // Mock method for getting STP instance attributes + MOCK_METHOD3(get_stp_attribute, + sai_status_t(_Out_ sai_object_id_t stp_instance_id, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list)); + + // Mock method for creating an STP port + MOCK_METHOD4(create_stp_port, + sai_status_t(_Out_ sai_object_id_t *stp_port_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list)); + + // Mock method for removing an STP port + MOCK_METHOD1(remove_stp_port, + sai_status_t(_In_ sai_object_id_t stp_port_id)); + + // Mock method for setting STP port attributes + MOCK_METHOD2(set_stp_port_attribute, + sai_status_t(_Out_ sai_object_id_t stp_port_id, + _In_ const sai_attribute_t *attr)); + + // Mock method for getting STP port attributes + MOCK_METHOD3(get_stp_port_attribute, + sai_status_t(_Out_ sai_object_id_t stp_port_id, + _In_ uint32_t attr_count, + _In_ sai_attribute_t *attr_list)); +}; + +// Global mock object for SAI STP APIs +MockSaiStp *mock_sai_stp; + +sai_status_t mock_create_stp(_Out_ sai_object_id_t *stp_instance_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + return mock_sai_stp->create_stp(stp_instance_id, switch_id, attr_count, attr_list); +} + +sai_status_t mock_remove_stp(_In_ sai_object_id_t stp_instance_id) +{ + return mock_sai_stp->remove_stp(stp_instance_id); +} + +sai_status_t mock_set_stp_attribute(_In_ sai_object_id_t stp_instance_id, _In_ const sai_attribute_t *attr) +{ + return mock_sai_stp->set_stp_attribute(stp_instance_id, attr); +} + +sai_status_t mock_get_stp_attribute(_Out_ sai_object_id_t stp_instance_id, + _In_ uint32_t attr_count, _Inout_ sai_attribute_t *attr_list) +{ + return mock_sai_stp->get_stp_attribute(stp_instance_id, attr_count, attr_list); +} +sai_status_t mock_create_stp_port(_Out_ sai_object_id_t *stp_port_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) +{ + return mock_sai_stp->create_stp_port(stp_port_id, switch_id,attr_count, attr_list); +} + +sai_status_t mock_remove_stp_port(_In_ sai_object_id_t stp_port_id) +{ + return mock_sai_stp->remove_stp_port(stp_port_id); +} + +sai_status_t mock_set_stp_port_attribute(_In_ sai_object_id_t stp_port_id, + _In_ const sai_attribute_t *attr) +{ + return mock_sai_stp->set_stp_port_attribute(stp_port_id, attr); +} + +sai_status_t mock_get_stp_port_attribute(_Out_ sai_object_id_t stp_port_id, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) +{ + return mock_sai_stp->get_stp_port_attribute(stp_port_id, attr_count, attr_list); +} + +#endif // MOCK_SAI_STP_H + diff --git a/orchagent/port.h b/orchagent/port.h index 1c7c34999f..946b562aec 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -76,6 +76,7 @@ struct SystemLagInfo int32_t spa_id = 0; }; +typedef std::map stp_port_ids_t; class PortOperErrorEvent { public: @@ -234,6 +235,8 @@ class Port sai_object_id_t m_system_side_id = 0; sai_object_id_t m_line_side_id = 0; + stp_port_ids_t m_stp_port_ids; //STP Port object ids for each STP instance + sai_int16_t m_stp_id = -1; //STP instance for the VLAN /* Port oper error status to event map*/ std::unordered_map m_portOperErrorToEvent; diff --git a/orchagent/port/porthlpr.cpp b/orchagent/port/porthlpr.cpp index 181fef9f69..3adb7ba9e9 100644 --- a/orchagent/port/porthlpr.cpp +++ b/orchagent/port/porthlpr.cpp @@ -1229,6 +1229,12 @@ bool PortHelper::parsePortConfig(PortConfig &port) const return false; } } + else if (field == PORT_MODE) + { + /* Placeholder to prevent warning. Not needed to be parsed here. + * Setting exists in sonic-port.yang with possible values: routed|access|trunk + */ + } else { SWSS_LOG_WARN("Unknown field(%s): skipping ...", field.c_str()); diff --git a/orchagent/port/portschema.h b/orchagent/port/portschema.h index 8dd7f79200..3e48b16d8c 100644 --- a/orchagent/port/portschema.h +++ b/orchagent/port/portschema.h @@ -101,3 +101,4 @@ #define PORT_SUPPRESS_THRESHOLD "suppress_threshold" #define PORT_REUSE_THRESHOLD "reuse_threshold" #define PORT_FLAP_PENALTY "flap_penalty" +#define PORT_MODE "mode" diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index ac3524d006..4a96d74473 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -7,6 +7,7 @@ #include "directory.h" #include "subintf.h" #include "notifications.h" +#include "stporch.h" #include #include @@ -55,6 +56,7 @@ extern CrmOrch *gCrmOrch; extern BufferOrch *gBufferOrch; extern FdbOrch *gFdbOrch; extern SwitchOrch *gSwitchOrch; +extern StpOrch *gStpOrch; extern Directory gDirectory; extern sai_system_port_api_t *sai_system_port_api; extern string gMySwitchType; @@ -6245,6 +6247,9 @@ bool PortsOrch::removeBridgePort(Port &port) hostif_vlan_tag[SAI_HOSTIF_VLAN_TAG_STRIP], port.m_alias.c_str()); return false; } + + /* Remove STP ports before bridge port deletion*/ + gStpOrch->removeStpPorts(port); //Flush the FDB entires corresponding to the port gFdbOrch->flushFDBEntries(port.m_bridge_port_id, SAI_NULL_OBJECT_ID); @@ -6387,6 +6392,12 @@ bool PortsOrch::removeVlan(Port vlan) return false; } + /* If STP instance is associated with VLAN remove VLAN from STP before deletion */ + if(vlan.m_stp_id != -1) + { + gStpOrch->removeVlanFromStpInstance(vlan.m_alias, 0); + } + sai_status_t status = sai_vlan_api->remove_vlan(vlan.m_vlan_info.vlan_oid); if (status != SAI_STATUS_SUCCESS) { diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 2903cd0342..ef6da962e3 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -1332,7 +1332,11 @@ bool RouteOrch::addNextHopGroup(const NextHopGroupKey &nexthops) nhopgroup_shared_set[next_hop_id].insert(it); } } - + if (!next_hop_ids.size()) + { + SWSS_LOG_INFO("Skipping creation of nexthop group as none of nexthop are active"); + return false; + } sai_attribute_t nhg_attr; vector nhg_attrs; @@ -1717,9 +1721,15 @@ void RouteOrch::addTempRoute(RouteBulkContext& ctx, const NextHopGroupKey &nextH SWSS_LOG_INFO("Failed to get next hop %s for %s", (*it).to_string().c_str(), ipPrefix.to_string().c_str()); it = next_hop_set.erase(it); + continue; } - else - it++; + if(m_neighOrch->isNextHopFlagSet(*it, NHFLAGS_IFDOWN)) + { + SWSS_LOG_INFO("Interface down for NH %s, skip this NH", (*it).to_string().c_str()); + it = next_hop_set.erase(it); + continue; + } + it++; } /* Return if next_hop_set is empty */ @@ -2423,8 +2433,14 @@ bool RouteOrch::removeRoute(RouteBulkContext& ctx) size_t creating = gRouteBulker.creating_entries_count(route_entry); if (it_route == it_route_table->second.end() && creating == 0) { - SWSS_LOG_INFO("Failed to find route entry, vrf_id 0x%" PRIx64 ", prefix %s\n", vrf_id, - ipPrefix.to_string().c_str()); + if (it_route_table->second.size() == 0) + { + m_syncdRoutes.erase(vrf_id); + m_vrfOrch->decreaseVrfRefCount(vrf_id); + } + SWSS_LOG_INFO("Failed to find route entry, vrf_id 0x%" PRIx64 ", prefix %s\n", vrf_id, + ipPrefix.to_string().c_str()); + return true; } diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 27ac9463cb..e7cf7fb018 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -86,6 +86,7 @@ sai_dash_vip_api_t* sai_dash_vip_api; sai_dash_direction_lookup_api_t* sai_dash_direction_lookup_api; sai_twamp_api_t* sai_twamp_api; sai_tam_api_t* sai_tam_api; +sai_stp_api_t* sai_stp_api; extern sai_object_id_t gSwitchId; extern bool gTraditionalFlexCounter; @@ -234,6 +235,7 @@ void initSaiApi() sai_api_query((sai_api_t)SAI_API_DASH_DIRECTION_LOOKUP, (void**)&sai_dash_direction_lookup_api); sai_api_query(SAI_API_TWAMP, (void **)&sai_twamp_api); sai_api_query(SAI_API_TAM, (void **)&sai_tam_api); + sai_api_query(SAI_API_STP, (void **)&sai_stp_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -275,6 +277,7 @@ void initSaiApi() sai_log_set(SAI_API_GENERIC_PROGRAMMABLE, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_TWAMP, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_TAM, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_STP, SAI_LOG_LEVEL_NOTICE); } void initFlexCounterTables() diff --git a/orchagent/stporch.cpp b/orchagent/stporch.cpp new file mode 100644 index 0000000000..38f502f321 --- /dev/null +++ b/orchagent/stporch.cpp @@ -0,0 +1,514 @@ +#include +#include "portsorch.h" +#include "logger.h" +#include "fdborch.h" +#include "stporch.h" + +extern sai_stp_api_t *sai_stp_api; +extern sai_vlan_api_t *sai_vlan_api; +extern sai_switch_api_t *sai_switch_api; + +extern FdbOrch *gFdbOrch; +extern PortsOrch *gPortsOrch; + +extern sai_object_id_t gSwitchId; + +StpOrch::StpOrch(DBConnector * db, DBConnector * stateDb, vector &tableNames) : + Orch(db, tableNames) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + sai_status_t status; + + m_stpTable = unique_ptr(new Table(stateDb, STATE_STP_TABLE_NAME)); + + vector attrs; + attr.id = SAI_SWITCH_ATTR_DEFAULT_STP_INST_ID; + attrs.push_back(attr); + + status = sai_switch_api->get_switch_attribute(gSwitchId, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + throw runtime_error("StpOrch initialization failure"); + } + + m_defaultStpId = attrs[0].value.oid; +}; + + +sai_object_id_t StpOrch::getStpInstanceOid(sai_uint16_t stp_instance) +{ + std::map::iterator it; + + it = m_stpInstToOid.find(stp_instance); + if (it == m_stpInstToOid.end()) + { + return SAI_NULL_OBJECT_ID; + } + + return it->second; +} + +sai_object_id_t StpOrch::addStpInstance(sai_uint16_t stp_instance) +{ + sai_object_id_t stp_oid; + sai_attribute_t attr; + + attr.id = 0; + attr.value.u32 = 0; + + sai_status_t status = sai_stp_api->create_stp(&stp_oid, gSwitchId, 0, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create STP instance %u status %u", stp_instance, status); + return SAI_NULL_OBJECT_ID; + } + + m_stpInstToOid[stp_instance] = stp_oid; + SWSS_LOG_INFO("Added STP instance:%hu oid:%" PRIx64 "", stp_instance, stp_oid); + return stp_oid; +} + +bool StpOrch::removeStpInstance(sai_uint16_t stp_instance) +{ + sai_object_id_t stp_oid; + + stp_oid = getStpInstanceOid(stp_instance); + if (stp_oid == SAI_NULL_OBJECT_ID) + { + return false; + } + + /* Remove all STP ports before deleting the STP instance */ + auto portList = gPortsOrch->getAllPorts(); + for (auto &it: portList) + { + auto &port = it.second; + if (port.m_type == Port::PHY || port.m_type == Port::LAG) + { + if(port.m_stp_port_ids.find(stp_instance) == port.m_stp_port_ids.end()) + continue; + + removeStpPort(port, stp_instance); + } + } + + sai_status_t status = sai_stp_api->remove_stp(stp_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove STP instance %u oid %" PRIx64 "status %u", stp_instance, stp_oid, status); + return false; + } + + m_stpInstToOid.erase(stp_instance); + SWSS_LOG_INFO("Removed STP instance:%hu oid:%" PRIx64 "", stp_instance, stp_oid); + return true; +} + +bool StpOrch::addVlanToStpInstance(string vlan_alias, sai_uint16_t stp_instance) +{ + SWSS_LOG_ENTER(); + + Port vlan; + sai_object_id_t stp_oid; + sai_attribute_t attr; + + if (!gPortsOrch->getPort(vlan_alias, vlan)) + { + return false; + } + + stp_oid = getStpInstanceOid(stp_instance); + if (stp_oid == SAI_NULL_OBJECT_ID) + { + stp_oid = addStpInstance(stp_instance); + if(stp_oid == SAI_NULL_OBJECT_ID) + return false; + } + + attr.id = SAI_VLAN_ATTR_STP_INSTANCE; + attr.value.oid = stp_oid; + + sai_status_t status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to add VLAN %s to STP instance:%hu status %u", vlan_alias.c_str(), stp_instance, status); + return false; + } + + vlan.m_stp_id = stp_instance; + gPortsOrch->setPort(vlan_alias, vlan); + SWSS_LOG_INFO("Add VLAN %s to STP instance:%hu m_stp_id:%d", vlan_alias.c_str(), stp_instance, vlan.m_stp_id); + return true; +} + +bool StpOrch::removeVlanFromStpInstance(string vlan_alias, sai_uint16_t stp_instance) +{ + SWSS_LOG_ENTER(); + + Port vlan; + sai_attribute_t attr; + + if (!gPortsOrch->getPort(vlan_alias, vlan)) + { + return false; + } + + attr.id = SAI_VLAN_ATTR_STP_INSTANCE; + attr.value.oid = m_defaultStpId; + + sai_status_t status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to add VLAN %s to STP instance:%d status %u", vlan_alias.c_str(), vlan.m_stp_id, status); + return false; + } + + SWSS_LOG_INFO("Remove %s from instance:%d add instance:%" PRIx64 "", vlan_alias.c_str(), vlan.m_stp_id, m_defaultStpId); + + removeStpInstance(vlan.m_stp_id); + vlan.m_stp_id = -1; + gPortsOrch->setPort(vlan_alias, vlan); + return true; +} + +/* If STP Port exists return else create a new STP Port */ +sai_object_id_t StpOrch::addStpPort(Port &port, sai_uint16_t stp_instance) +{ + sai_object_id_t stp_port_id = SAI_NULL_OBJECT_ID; + sai_object_id_t stp_id = SAI_NULL_OBJECT_ID; + sai_attribute_t attr[3]; + + if(port.m_stp_port_ids.find(stp_instance) != port.m_stp_port_ids.end()) + { + return port.m_stp_port_ids[stp_instance]; + } + + if(port.m_bridge_port_id == SAI_NULL_OBJECT_ID) + { + gPortsOrch->addBridgePort(port); + + if(port.m_bridge_port_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Failed to add STP port %s invalid bridge port id STP instance %d", port.m_alias.c_str(), stp_instance); + return SAI_NULL_OBJECT_ID; + } + } + attr[0].id = SAI_STP_PORT_ATTR_BRIDGE_PORT; + attr[0].value.oid = port.m_bridge_port_id; + + stp_id = getStpInstanceOid(stp_instance); + if(stp_id == SAI_NULL_OBJECT_ID) + { + stp_id = addStpInstance(stp_instance); + if(stp_id == SAI_NULL_OBJECT_ID) + { + return SAI_NULL_OBJECT_ID; + } + } + + attr[1].id = SAI_STP_PORT_ATTR_STP; + attr[1].value.oid = stp_id; + + attr[2].id = SAI_STP_PORT_ATTR_STATE; + attr[2].value.s32 = SAI_STP_PORT_STATE_BLOCKING; + + sai_status_t status = sai_stp_api->create_stp_port(&stp_port_id, gSwitchId, 3, attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to add STP port %s instance %d status %u", port.m_alias.c_str(), stp_instance, status); + return SAI_NULL_OBJECT_ID; + } + + SWSS_LOG_INFO("Add STP port %s instance %d oid %" PRIx64 " size %zu", port.m_alias.c_str(), stp_instance, stp_port_id, port.m_stp_port_ids.size()); + port.m_stp_port_ids[stp_instance] = stp_port_id; + gPortsOrch->setPort(port.m_alias, port); + return stp_port_id; +} + +bool StpOrch::removeStpPort(Port &port, sai_uint16_t stp_instance) +{ + if(port.m_stp_port_ids.find(stp_instance) == port.m_stp_port_ids.end()) + { + /* Deletion could have already happened as part of other flows, so ignore this msg*/ + return true; + } + + sai_status_t status = sai_stp_api->remove_stp_port(port.m_stp_port_ids[stp_instance]); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove STP port %s instance %d oid %" PRIx64 " status %x", port.m_alias.c_str(), stp_instance, + port.m_stp_port_ids[stp_instance], status); + return false; + } + + SWSS_LOG_INFO("Remove STP port %s instance %d oid %" PRIx64 " size %zu", port.m_alias.c_str(), stp_instance, + port.m_stp_port_ids[stp_instance], port.m_stp_port_ids.size()); + port.m_stp_port_ids.erase(stp_instance); + gPortsOrch->setPort(port.m_alias, port); + return true; +} + +bool StpOrch::removeStpPorts(Port &port) +{ + if(port.m_stp_port_ids.empty()) + return true; + + for(auto stp_port_id: port.m_stp_port_ids) + { + uint16_t stp_instance = stp_port_id.first; + sai_object_id_t stp_port_oid = stp_port_id.second; + + if(stp_port_oid == SAI_NULL_OBJECT_ID) + { + continue; + } + + sai_status_t status = sai_stp_api->remove_stp_port(stp_port_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove STP port %s instance %d oid %" PRIx64 " status %x", port.m_alias.c_str(), stp_instance, stp_port_oid, status); + } + else + { + SWSS_LOG_INFO("Remove STP port %s instance %d oid %" PRIx64 "", port.m_alias.c_str(), stp_instance, stp_port_oid); + } + } + + port.m_stp_port_ids.clear(); + gPortsOrch->setPort(port.m_alias, port); + return true; +} + +sai_stp_port_state_t StpOrch::getStpSaiState(sai_uint8_t stp_state) +{ + sai_stp_port_state_t state = SAI_STP_PORT_STATE_BLOCKING; + + switch(stp_state) + { + case STP_STATE_DISABLED: + case STP_STATE_BLOCKING: + case STP_STATE_LISTENING: + state = SAI_STP_PORT_STATE_BLOCKING; + break; + + case STP_STATE_LEARNING: + state = SAI_STP_PORT_STATE_LEARNING; + break; + + case STP_STATE_FORWARDING: + state = SAI_STP_PORT_STATE_FORWARDING; + break; + } + return state; +} + +bool StpOrch::updateStpPortState(Port &port, sai_uint16_t stp_instance, sai_uint8_t stp_state) +{ + sai_attribute_t attr[1]; + sai_object_id_t stp_port_oid; + + stp_port_oid = addStpPort(port, stp_instance); + if (stp_port_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Failed to get STP port oid port %s instance %d state %d ", port.m_alias.c_str(), stp_instance, stp_state); + return true; + } + attr[0].id = SAI_STP_PORT_ATTR_STATE; + attr[0].value.u32 = getStpSaiState(stp_state); + + sai_status_t status = sai_stp_api->set_stp_port_attribute(stp_port_oid, attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set STP port state %s instance %d state %d status %x", port.m_alias.c_str(), stp_instance, stp_state, status); + return false; + } + + SWSS_LOG_INFO("Set STP port state %s instance %d state %d ", port.m_alias.c_str(), stp_instance, stp_state); + + return true; +} + +bool StpOrch::stpVlanFdbFlush(string vlan_alias) +{ + SWSS_LOG_ENTER(); + + Port vlan; + + if (!gPortsOrch->getPort(vlan_alias, vlan)) + { + return false; + } + + gFdbOrch->flushFdbByVlan(vlan_alias); + + SWSS_LOG_INFO("Set STP FDB flush vlan %s ", vlan_alias.c_str()); + return true; +} + +void StpOrch::doStpTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto &t = it->second; + + string vlan_alias = kfvKey(t); + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + uint16_t instance = STP_INVALID_INSTANCE; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "stp_instance") + { + instance = (uint16_t)std::stoi(fvValue(i)); + } + } + + if(instance == STP_INVALID_INSTANCE) + { + SWSS_LOG_ERROR("No instance found for VLAN %s", vlan_alias.c_str()); + } + else + { + if(!addVlanToStpInstance(vlan_alias, instance)) + { + it++; + continue; + } + } + } + else if (op == DEL_COMMAND) + { + if(!removeVlanFromStpInstance(vlan_alias, 0)) + { + it++; + continue; + } + } + it = consumer.m_toSync.erase(it); + } +} + +void StpOrch::doStpPortStateTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + auto &t = it->second; + string key = kfvKey(t); + size_t found = key.find(':'); + /* Return if the format of key is wrong */ + if (found == string::npos) + { + return; + } + string port_alias = key.substr(0, found); + string stp_instance = key.substr(found+1); + uint16_t instance = (uint16_t)std::stoi(stp_instance); + Port port; + + if (!gPortsOrch->getPort(port_alias, port)) + { + return; + } + + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + uint8_t state = STP_STATE_INVALID; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "state") + { + state = (uint8_t)std::stoi(fvValue(i)); + } + } + if(state != STP_STATE_INVALID) + { + if(!updateStpPortState(port, instance, state)) + { + it++; + continue; + } + } + } + else if (op == DEL_COMMAND) + { + if(!removeStpPort(port, instance)) + { + it++; + continue; + } + } + it = consumer.m_toSync.erase(it); + } +} + +void StpOrch::doStpFastageTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + for (auto it = consumer.m_toSync.begin(); it != consumer.m_toSync.end(); ) + { + auto &t = it->second; + string op = kfvOp(t); + string vlan_alias = kfvKey(t); + + if (op == SET_COMMAND) + { + string state; + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "state") + state = fvValue(i); + } + + if(state.compare("true") == 0) + { + stpVlanFdbFlush(vlan_alias); + } + } + else if (op == DEL_COMMAND) + { + // no operation + } + + it = consumer.m_toSync.erase(it); + } +} + +void StpOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (!gPortsOrch->allPortsReady()) + { + return; + } + + string table_name = consumer.getTableName(); + if (table_name == APP_STP_VLAN_INSTANCE_TABLE_NAME) + { + doStpTask(consumer); + } + else if (table_name == APP_STP_PORT_STATE_TABLE_NAME) + { + doStpPortStateTask(consumer); + } + else if (table_name == APP_STP_FASTAGEING_FLUSH_TABLE_NAME) + { + doStpFastageTask(consumer); + } +} + diff --git a/orchagent/stporch.h b/orchagent/stporch.h new file mode 100644 index 0000000000..154ac7695b --- /dev/null +++ b/orchagent/stporch.h @@ -0,0 +1,53 @@ +#ifndef SWSS_STPORCH_H +#define SWSS_STPORCH_H + +#include +#include +#include "orch.h" + +#define STP_INVALID_INSTANCE 0xFFFF + +typedef enum _stp_state +{ + STP_STATE_DISABLED = 0, + STP_STATE_BLOCKING = 1, + STP_STATE_LISTENING = 2, + STP_STATE_LEARNING = 3, + STP_STATE_FORWARDING = 4, + STP_STATE_INVALID = 5 +}stp_state; + + +class StpOrch : public Orch +{ +public: + StpOrch(DBConnector *db, DBConnector *stateDb, vector &tableNames); + bool stpVlanFdbFlush(string vlan_alias); + bool updateMaxStpInstance(uint32_t max_stp_instance); + bool removeStpPorts(Port &port); + bool removeVlanFromStpInstance(string vlan, sai_uint16_t stp_instance); + +private: + unique_ptr
m_stpTable; + std::map m_stpInstToOid;//Mapping from STP instance id to corresponding object id + sai_object_id_t m_defaultStpId; + + void doStpTask(Consumer &consumer); + void doStpPortStateTask(Consumer &consumer); + void doStpFastageTask(Consumer &consumer); + void doStpVlanIntfFlushTask(Consumer &consumer); + + sai_object_id_t addStpInstance(sai_uint16_t stp_instance); + bool removeStpInstance(sai_uint16_t stp_instance); + bool addVlanToStpInstance(string vlan, sai_uint16_t stp_instance); + sai_object_id_t getStpInstanceOid(sai_uint16_t stp_instance); + + sai_object_id_t addStpPort(Port &port, sai_uint16_t stp_instance); + bool removeStpPort(Port &port, sai_uint16_t stp_instance); + sai_stp_port_state_t getStpSaiState(sai_uint8_t stp_state); + bool updateStpPortState(Port &port, sai_uint16_t stp_instance, sai_uint8_t stp_state); + + void doTask(Consumer& consumer); +}; +#endif /* SWSS_STPORCH_H */ + diff --git a/tests/conftest.py b/tests/conftest.py index abf9955cd7..20e3a219d1 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -737,6 +737,21 @@ def stop_zebra(self): self.runcmd(['sh', '-c', 'pkill -9 zebra']) time.sleep(5) + def stop_teamsyncd(self): + self.runcmd(['sh', '-c', 'pkill -9 teamsyncd']) + + time.sleep(5) + + def start_teamsyncd(self): + self.runcmd(['sh', '-c', 'supervisorctl start teamsyncd']) + + time.sleep(5) + + def restart_teammgrd(self): + self.runcmd(['sh', '-c', 'supervisorctl restart teammgrd']) + + time.sleep(5) + # deps: warm_reboot def start_fpmsyncd(self): self.runcmd(['sh', '-c', 'supervisorctl start fpmsyncd']) diff --git a/tests/dash/test_dash_vnet.py b/tests/dash/test_dash_vnet.py index fa8f457bb8..8409db7ce6 100644 --- a/tests/dash/test_dash_vnet.py +++ b/tests/dash/test_dash_vnet.py @@ -44,6 +44,7 @@ def test_appliance(self, dash_db: DashDB): direction_keys = dash_db.wait_for_asic_db_keys(ASIC_DIRECTION_LOOKUP_TABLE) dl_attrs = dash_db.get_asic_db_entry(ASIC_DIRECTION_LOOKUP_TABLE, direction_keys[0]) assert_sai_attribute_exists("SAI_DIRECTION_LOOKUP_ENTRY_ATTR_ACTION", dl_attrs, "SAI_DIRECTION_LOOKUP_ENTRY_ACTION_SET_OUTBOUND_DIRECTION") + assert_sai_attribute_exists("SAI_DIRECTION_LOOKUP_ENTRY_ATTR_DASH_ENI_MAC_OVERRIDE_TYPE", dl_attrs, "SAI_DASH_ENI_MAC_OVERRIDE_TYPE_DST_MAC") vip_keys = dash_db.wait_for_asic_db_keys(ASIC_VIP_TABLE) vip_attrs = dash_db.get_asic_db_entry(ASIC_VIP_TABLE, vip_keys[0]) diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 126f4e88c5..72f0fdf6ee 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -63,6 +63,7 @@ tests_SOURCES = aclorch_ut.cpp \ neighorch_ut.cpp \ dashorch_ut.cpp \ twamporch_ut.cpp \ + stporch_ut.cpp \ flexcounter_ut.cpp \ mock_orch_test.cpp \ $(top_srcdir)/warmrestart/warmRestartHelper.cpp \ @@ -139,7 +140,8 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/warmrestart/warmRestartAssist.cpp \ $(top_srcdir)/orchagent/dash/pbutils.cpp \ $(top_srcdir)/cfgmgr/coppmgr.cpp \ - $(top_srcdir)/orchagent/twamporch.cpp + $(top_srcdir)/orchagent/twamporch.cpp \ + $(top_srcdir)/orchagent/stporch.cpp tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp $(FLEX_CTR_DIR)/flow_counter_handler.cpp $(FLEX_CTR_DIR)/flowcounterrouteorch.cpp tests_SOURCES += $(DEBUG_CTR_DIR)/debug_counter.cpp $(DEBUG_CTR_DIR)/drop_counter.cpp diff --git a/tests/mock_tests/fake_netlink.cpp b/tests/mock_tests/fake_netlink.cpp index 2370e13129..54eb197493 100644 --- a/tests/mock_tests/fake_netlink.cpp +++ b/tests/mock_tests/fake_netlink.cpp @@ -1,5 +1,6 @@ #include #include +#include static rtnl_link* g_fakeLink = [](){ auto fakeLink = rtnl_link_alloc(); @@ -7,6 +8,8 @@ static rtnl_link* g_fakeLink = [](){ return fakeLink; }(); +extern int rt_build_ret; +extern bool nlmsg_alloc_ret; extern "C" { @@ -15,4 +18,34 @@ struct rtnl_link* rtnl_link_get_by_name(struct nl_cache *cache, const char *name return g_fakeLink; } +static int build_route_msg(struct rtnl_route *tmpl, int cmd, int flags, + struct nl_msg **result) +{ + struct nl_msg *msg; + int err; + if (!(msg = nlmsg_alloc_simple(cmd, flags))) + return -NLE_NOMEM; + if ((err = rtnl_route_build_msg(msg, tmpl)) < 0) { + nlmsg_free(msg); + return err; + } + *result = msg; + return 0; +} + +int rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags, + struct nl_msg **result) +{ + if (rt_build_ret != 0) + { + return rt_build_ret; + } + else if (!nlmsg_alloc_ret) + { + *result = NULL; + return 0; + } + return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags, + result); +} } diff --git a/tests/mock_tests/fpmsyncd/test_routesync.cpp b/tests/mock_tests/fpmsyncd/test_routesync.cpp index a8de78859f..b1c23aca85 100644 --- a/tests/mock_tests/fpmsyncd/test_routesync.cpp +++ b/tests/mock_tests/fpmsyncd/test_routesync.cpp @@ -12,6 +12,8 @@ using namespace swss; using ::testing::_; +int rt_build_ret = 0; +bool nlmsg_alloc_ret = true; class MockRouteSync : public RouteSync { public: @@ -232,3 +234,18 @@ TEST_F(FpmSyncdResponseTest, testEvpn) ASSERT_EQ(value.get(), "0xc8"); } + +TEST_F(FpmSyncdResponseTest, testSendOffloadReply) +{ + + rt_build_ret = 1; + rtnl_route* routeObject{}; + + + ASSERT_EQ(m_routeSync.sendOffloadReply(routeObject), false); + rt_build_ret = 0; + nlmsg_alloc_ret = false; + ASSERT_EQ(m_routeSync.sendOffloadReply(routeObject), false); + nlmsg_alloc_ret = true; + +} diff --git a/tests/mock_tests/mock_orchagent_main.h b/tests/mock_tests/mock_orchagent_main.h index f2469a09ef..5ffda0dd41 100644 --- a/tests/mock_tests/mock_orchagent_main.h +++ b/tests/mock_tests/mock_orchagent_main.h @@ -29,6 +29,9 @@ #include "nhgorch.h" #include "copporch.h" #include "twamporch.h" +#define private public +#include "stporch.h" +#undef private #include "directory.h" extern int gBatchSize; @@ -60,6 +63,7 @@ extern BfdOrch *gBfdOrch; extern AclOrch *gAclOrch; extern PolicerOrch *gPolicerOrch; extern TunnelDecapOrch *gTunneldecapOrch; +extern StpOrch *gStpOrch; extern Directory gDirectory; extern sai_acl_api_t *sai_acl_api; @@ -94,3 +98,4 @@ extern sai_tam_api_t* sai_tam_api; extern sai_dash_vip_api_t* sai_dash_vip_api; extern sai_dash_direction_lookup_api_t* sai_dash_direction_lookup_api; extern sai_dash_eni_api_t* sai_dash_eni_api; +extern sai_stp_api_t* sai_stp_api; diff --git a/tests/mock_tests/routeorch_ut.cpp b/tests/mock_tests/routeorch_ut.cpp index fe24cf1f29..043b7b6b0a 100644 --- a/tests/mock_tests/routeorch_ut.cpp +++ b/tests/mock_tests/routeorch_ut.cpp @@ -281,6 +281,7 @@ namespace routeorch_test for (const auto &it : ports) { portTable.set(it.first, it.second); + portTable.set(it.first, {{ "oper_status", "up" }}); } // Set PortConfigDone diff --git a/tests/mock_tests/stporch_ut.cpp b/tests/mock_tests/stporch_ut.cpp new file mode 100644 index 0000000000..847e02b304 --- /dev/null +++ b/tests/mock_tests/stporch_ut.cpp @@ -0,0 +1,246 @@ +#include +#include + +#define private public // make Directory::m_values available to clean it. +#include "directory.h" +#undef private +#define protected public +#include "orch.h" +#undef protected +#include "ut_helper.h" +#include "dbconnector.h" +#include "mock_orchagent_main.h" +#include "mock_sai_api.h" +#include "mock_orch_test.h" +#include "mock_table.h" +#define private public +#include "stporch.h" +#undef private +#include "mock_sai_stp.h" + + +namespace stporch_test +{ + using namespace std; + using namespace swss; + using namespace mock_orch_test; + using ::testing::StrictMock; + + using ::testing::_; + using ::testing::Return; + + sai_status_t _ut_stub_sai_set_vlan_attribute(_In_ sai_object_id_t vlan_oid, + _In_ const sai_attribute_t *attr) + { + return SAI_STATUS_SUCCESS; + } + + sai_status_t _ut_stub_sai_flush_fdb_entries(_In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, _In_ const sai_attribute_t *attr_list) + { + return SAI_STATUS_SUCCESS; + } + + class StpOrchTest : public MockOrchTest { + protected: + void ApplyInitialConfigs() + { + Table port_table = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + Table vlan_table = Table(m_app_db.get(), APP_VLAN_TABLE_NAME); + Table vlan_member_table = Table(m_app_db.get(), APP_VLAN_MEMBER_TABLE_NAME); + + auto ports = ut_helper::getInitialSaiPorts(); + port_table.set(ETHERNET0, ports[ETHERNET0]); + port_table.set(ETHERNET4, ports[ETHERNET4]); + port_table.set(ETHERNET8, ports[ETHERNET8]); + port_table.set("PortConfigDone", { { "count", to_string(1) } }); + port_table.set("PortInitDone", { {} }); + + vlan_table.set(VLAN_1000, { { "admin_status", "up" }, + { "mtu", "9100" }, + { "mac", "00:aa:bb:cc:dd:ee" } }); + vlan_member_table.set( + VLAN_1000 + vlan_member_table.getTableNameSeparator() + ETHERNET0, + { { "tagging_mode", "untagged" } }); + + gPortsOrch->addExistingData(&port_table); + gPortsOrch->addExistingData(&vlan_table); + gPortsOrch->addExistingData(&vlan_member_table); + static_cast(gPortsOrch)->doTask(); + } + void PostSetUp() override + { + vector tableNames = + {"STP_TABLE", + "STP_VLAN_INSTANCE_TABLE", + "STP_PORT_STATE_TABLE", + "STP_FASTAGEING_FLUSH_TABLE"}; + gStpOrch = new StpOrch(m_app_db.get(), m_state_db.get(), tableNames); + } + void PreTearDown() override + { + delete gStpOrch; + gStpOrch = nullptr; + } + + sai_stp_api_t ut_sai_stp_api; + sai_stp_api_t *org_sai_stp_api; + + void _hook_sai_stp_api() + { + ut_sai_stp_api = *sai_stp_api; + org_sai_stp_api = sai_stp_api; + sai_stp_api = &ut_sai_stp_api; + } + + void _unhook_sai_stp_api() + { + sai_stp_api = org_sai_stp_api; + } + + sai_vlan_api_t ut_sai_vlan_api; + sai_vlan_api_t *org_sai_vlan_api; + + void _hook_sai_vlan_api() + { + ut_sai_vlan_api = *sai_vlan_api; + org_sai_vlan_api = sai_vlan_api; + ut_sai_vlan_api.set_vlan_attribute = _ut_stub_sai_set_vlan_attribute; + sai_vlan_api = &ut_sai_vlan_api; + } + + void _unhook_sai_vlan_api() + { + sai_vlan_api = org_sai_vlan_api; + } + + sai_fdb_api_t ut_sai_fdb_api; + sai_fdb_api_t *org_sai_fdb_api; + void _hook_sai_fdb_api() + { + ut_sai_fdb_api = *sai_fdb_api; + org_sai_fdb_api = sai_fdb_api; + ut_sai_fdb_api.flush_fdb_entries = _ut_stub_sai_flush_fdb_entries; + sai_fdb_api = &ut_sai_fdb_api; + } + + void _unhook_sai_fdb_api() + { + sai_fdb_api = org_sai_fdb_api; + } + }; + + TEST_F(StpOrchTest, TestAddRemoveStpPort) { + _hook_sai_stp_api(); + _hook_sai_vlan_api(); + _hook_sai_fdb_api(); + + StrictMock mock_sai_stp_; + mock_sai_stp = &mock_sai_stp_; + sai_stp_api->create_stp = mock_create_stp; + sai_stp_api->remove_stp = mock_remove_stp; + sai_stp_api->create_stp_port = mock_create_stp_port; + sai_stp_api->remove_stp_port = mock_remove_stp_port; + sai_stp_api->set_stp_port_attribute = mock_set_stp_port_attribute; + + Port port; + Port port1; + sai_uint16_t stp_instance = 1; + sai_object_id_t stp_port_oid = 67890; + sai_object_id_t stp_oid = 98765; + bool result; + + ASSERT_TRUE(gPortsOrch->getPort(ETHERNET0, port)); + ASSERT_TRUE(gPortsOrch->getPort(ETHERNET4, port1)); + + EXPECT_CALL(mock_sai_stp_, + create_stp(_, _, _, _)).WillOnce(::testing::DoAll(::testing::SetArgPointee<0>(stp_oid), + ::testing::Return(SAI_STATUS_SUCCESS))); + result = gStpOrch->addVlanToStpInstance(VLAN_1000, stp_instance); + ASSERT_TRUE(result); + + EXPECT_CALL(mock_sai_stp_, + create_stp_port(_, _, 3, _)).WillOnce(::testing::DoAll(::testing::SetArgPointee<0>(stp_port_oid), + ::testing::Return(SAI_STATUS_SUCCESS))); + EXPECT_CALL(mock_sai_stp_, + set_stp_port_attribute(_,_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); + port.m_bridge_port_id = 1234; + result = gStpOrch->updateStpPortState(port, stp_instance, STP_STATE_FORWARDING); + ASSERT_TRUE(result); + + result = gStpOrch->stpVlanFdbFlush(VLAN_1000); + ASSERT_TRUE(result); + + EXPECT_CALL(mock_sai_stp_, + remove_stp_port(_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); + result = gStpOrch->removeStpPort(port, stp_instance); + ASSERT_TRUE(result); + + EXPECT_CALL(mock_sai_stp_, + create_stp_port(_, _, 3, _)).WillOnce(::testing::DoAll(::testing::SetArgPointee<0>(stp_port_oid), + ::testing::Return(SAI_STATUS_SUCCESS))); + EXPECT_CALL(mock_sai_stp_, + set_stp_port_attribute(_,_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); + port1.m_bridge_port_id = 1111; + result = gStpOrch->updateStpPortState(port1, stp_instance, STP_STATE_BLOCKING); + ASSERT_TRUE(result); + + EXPECT_CALL(mock_sai_stp_, + remove_stp_port(_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); + result = gStpOrch->removeStpPorts(port1); + ASSERT_TRUE(result); + + EXPECT_CALL(mock_sai_stp_, + remove_stp(_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); + result = gStpOrch->removeVlanFromStpInstance(VLAN_1000, stp_instance); + ASSERT_TRUE(result); + + std::deque entries; + entries.push_back({"Vlan1000", "SET", { {"stp_instance", "1"}}}); + EXPECT_CALL(mock_sai_stp_, + create_stp(_, _, _, _)).WillOnce(::testing::DoAll(::testing::SetArgPointee<0>(stp_oid), + ::testing::Return(SAI_STATUS_SUCCESS))); + + auto consumer = dynamic_cast(gStpOrch->getExecutor("STP_VLAN_INSTANCE_TABLE")); + consumer->addToSync(entries); + static_cast(gStpOrch)->doTask(); + + entries.clear(); + EXPECT_CALL(mock_sai_stp_, + create_stp_port(_, _, 3, _)).WillOnce(::testing::DoAll(::testing::SetArgPointee<0>(stp_port_oid), + ::testing::Return(SAI_STATUS_SUCCESS))); + EXPECT_CALL(mock_sai_stp_, + set_stp_port_attribute(_,_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); + entries.push_back({"Ethernet0:1", "SET", { {"state", "4"}}}); + consumer = dynamic_cast(gStpOrch->getExecutor("STP_PORT_STATE_TABLE")); + consumer->addToSync(entries); + static_cast(gStpOrch)->doTask(); + + entries.clear(); + entries.push_back({"Ethernet0:1", "SET", { {"state", "true"}}}); + consumer = dynamic_cast(gStpOrch->getExecutor("STP_FASTAGEING_FLUSH_TABLE")); + consumer->addToSync(entries); + static_cast(gStpOrch)->doTask(); + + + entries.clear(); + entries.push_back({"Ethernet0:1", "DEL", { {} }}); + EXPECT_CALL(mock_sai_stp_, + remove_stp_port(_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); + consumer = dynamic_cast(gStpOrch->getExecutor("STP_PORT_STATE_TABLE")); + consumer->addToSync(entries); + static_cast(gStpOrch)->doTask(); + + entries.clear(); + entries.push_back({"Vlan1000", "DEL", { {} }}); + EXPECT_CALL(mock_sai_stp_, + remove_stp(_)).WillOnce(::testing::Return(SAI_STATUS_SUCCESS)); + consumer = dynamic_cast(gStpOrch->getExecutor("STP_VLAN_INSTANCE_TABLE")); + consumer->addToSync(entries); + static_cast(gStpOrch)->doTask(); + + _unhook_sai_stp_api(); + _unhook_sai_vlan_api(); + _unhook_sai_fdb_api(); + } +} \ No newline at end of file diff --git a/tests/mock_tests/ut_saihelper.cpp b/tests/mock_tests/ut_saihelper.cpp index 269f54f06b..4f7d7e714b 100644 --- a/tests/mock_tests/ut_saihelper.cpp +++ b/tests/mock_tests/ut_saihelper.cpp @@ -94,7 +94,7 @@ namespace ut_helper sai_api_query((sai_api_t)SAI_API_DASH_VIP, (void**)&sai_dash_vip_api); sai_api_query((sai_api_t)SAI_API_DASH_DIRECTION_LOOKUP, (void**)&sai_dash_direction_lookup_api); sai_api_query((sai_api_t)SAI_API_DASH_ENI, (void**)&sai_dash_eni_api); - + sai_api_query(SAI_API_STP, (void**)&sai_stp_api); return SAI_STATUS_SUCCESS; } @@ -128,6 +128,7 @@ namespace ut_helper sai_dash_vip_api = nullptr; sai_dash_direction_lookup_api = nullptr; sai_dash_eni_api = nullptr; + sai_stp_api = nullptr; return SAI_STATUS_SUCCESS; } diff --git a/tests/test_pac.py b/tests/test_pac.py new file mode 100644 index 0000000000..a913fddfc9 --- /dev/null +++ b/tests/test_pac.py @@ -0,0 +1,209 @@ +import time + +from swsscommon import swsscommon + +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, key): + tbl._del(key) + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def remove_entry_tbl(db, table, key): + tbl = swsscommon.Table(db, table) + remove_entry(tbl, key) + +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 get_port_oid(db, port_name): + port_map_tbl = swsscommon.Table(db, 'COUNTERS_PORT_NAME_MAP') + for k in port_map_tbl.get('')[1]: + if k[0] == port_name: + return k[1] + return None + +def get_bridge_port_oid(db, port_oid): + tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + for key in tbl.getKeys(): + status, data = tbl.get(key) + assert status + values = dict(data) + if port_oid == values["SAI_BRIDGE_PORT_ATTR_PORT_ID"]: + return key + return None + +def check_learn_mode_in_asicdb(db, interface_oid, learn_mode): + # Get bridge port oid + bridge_port_oid = get_bridge_port_oid(db, interface_oid) + assert bridge_port_oid is not None + + tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + (status, fvs) = tbl.get(bridge_port_oid) + assert status == True + values = dict(fvs) + if values["SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE"] == learn_mode: + return True + else: + return False + +class TestPac(object): + def test_PacvlanMemberAndFDBAddRemove(self, dvs, testlog): + dvs.setup_db() + time.sleep(2) + + 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") + time.sleep(1) + + # Get bvid from vlanid + ok, bvid = dvs.get_vlan_oid(dvs.adb, "2") + assert ok, bvid + + dvs.create_vlan("3") + time.sleep(1) + + # create vlan member + dvs.create_vlan_member("3", "Ethernet0") + time.sleep(1) + + # create a Vlan member entry in Oper State DB + create_entry_tbl( + dvs.sdb, + "OPER_VLAN_MEMBER", "Vlan2|Ethernet0", + [ + ("tagging_mode", "untagged"), + ] + ) + + # 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 == 2, "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" + + # Add FDB entry in Oper State DB + create_entry_tbl( + dvs.sdb, + "OPER_FDB", "Vlan2|00:00:00:00:00:01", + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ("discard", "false"), + ] + ) + # 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 + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "00:00:00:00:00:01"), ("bvid", bvid)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_PACKET_ACTION", "SAI_PACKET_ACTION_FORWARD"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])]) + + assert ok, str(extra) + + # Remove FDB entry in Oper State DB + remove_entry_tbl( + dvs.sdb, + "OPER_FDB", "Vlan2|00:00:00:00:00:01" + ) + + # check that the FDB entry was removed from ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "00:00:00:00:00:01"), ("bvid", bvid)], []) + assert ok == False, "The fdb entry still exists in ASIC" + + # remove Vlan member entry in Oper State DB + remove_entry_tbl( + dvs.sdb, + "OPER_VLAN_MEMBER", "Vlan2|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 == 2, "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" + + dvs.remove_vlan("2") + dvs.remove_vlan_member("3", "Ethernet0") + dvs.remove_vlan("3") + + 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 == 0, "The Vlan2 wasn't removed" + assert bp_after - bp_before == 0, "The bridge port wasn't removed" + assert vm_after - vm_before == 0, "The vlan member wasn't removed" + + def test_PacPortLearnMode(self, dvs, testlog): + dvs.setup_db() + time.sleep(2) + + # create vlan + dvs.create_vlan("2") + time.sleep(1) + + # create vlan member + dvs.create_vlan_member("2", "Ethernet0") + time.sleep(1) + + cntdb = swsscommon.DBConnector(swsscommon.COUNTERS_DB, dvs.redis_sock, 0) + # get port oid + port_oid = get_port_oid(cntdb, "Ethernet0") + assert port_oid is not None + + # check asicdb before setting mac learn mode; The default learn_mode value is SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW. + status = check_learn_mode_in_asicdb(dvs.adb, port_oid, "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW") + assert status == True + + # Set port learn mode to CPU + create_entry_tbl( + dvs.sdb, + "OPER_PORT", "Ethernet0", + [ + ("learn_mode", "cpu_trap"), + ] + ) + status = check_learn_mode_in_asicdb(dvs.adb, port_oid, "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_CPU_TRAP") + assert status == True + + # Set port learn mode back to default + remove_entry_tbl( + dvs.sdb, + "OPER_PORT", "Ethernet0" + ) + status = check_learn_mode_in_asicdb(dvs.adb, port_oid, "SAI_BRIDGE_PORT_FDB_LEARNING_MODE_HW") + assert status == True + dvs.remove_vlan_member("2", "Ethernet0") + dvs.remove_vlan("2") + +# Add Dummy always-pass test at end as workaroud +# for issue when Flaky fail on final test it invokes module tear-down before retrying +def test_nonflaky_dummy(): + pass diff --git a/tests/test_sub_port_intf.py b/tests/test_sub_port_intf.py index ec76ec13bb..3c9edea5c6 100644 --- a/tests/test_sub_port_intf.py +++ b/tests/test_sub_port_intf.py @@ -393,7 +393,7 @@ def get_default_vrf_oid(self): assert len(oids) == 1, "Wrong # of default vrfs: %d, expected #: 1." % (len(oids)) return oids[0] - def get_ip_prefix_nhg_oid(self, ip_prefix, vrf_oid=None): + def get_ip_prefix_nhg_oid(self, ip_prefix, vrf_oid=None, prefix_present=True): if vrf_oid is None: vrf_oid = self.default_vrf_oid @@ -407,18 +407,24 @@ def _access_function(): route_entry_found = True assert route_entry_key["vr"] == vrf_oid break - - return (route_entry_found, raw_route_entry_key) + if prefix_present: + return (route_entry_found, raw_route_entry_key) + else: + return (not route_entry_found, None) (route_entry_found, raw_route_entry_key) = wait_for_result(_access_function) - fvs = self.asic_db.get_entry(ASIC_ROUTE_ENTRY_TABLE, raw_route_entry_key) + if not prefix_present: + assert raw_route_entry_key == None + return None + else: + fvs = self.asic_db.get_entry(ASIC_ROUTE_ENTRY_TABLE, raw_route_entry_key) - nhg_oid = fvs.get("SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID", "") - assert nhg_oid != "" - assert nhg_oid != "oid:0x0" + nhg_oid = fvs.get("SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID", "") + assert nhg_oid != "" + assert nhg_oid != "oid:0x0" - return nhg_oid + return nhg_oid def check_sub_port_intf_key_existence(self, db, table_name, key): db.wait_for_matching_keys(table_name, [key]) @@ -1543,21 +1549,26 @@ def _test_sub_port_intf_oper_down_with_pending_neigh_route_tasks(self, dvs, sub_ self.add_route_appl_db(ip_prefix, nhop_ips, ifnames, vrf_name) # Verify route entry created in ASIC_DB and get next hop group oid - nhg_oid = self.get_ip_prefix_nhg_oid(ip_prefix, vrf_oid) - - # Verify next hop group of the specified oid created in ASIC_DB - self.check_sub_port_intf_key_existence(self.asic_db, ASIC_NEXT_HOP_GROUP_TABLE, nhg_oid) + nhg_oid = self.get_ip_prefix_nhg_oid(ip_prefix, vrf_oid, prefix_present = i < (nhop_num - 1)) - # Verify next hop group member # created in ASIC_DB - nhg_member_oids = self.asic_db.wait_for_n_keys(ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, - (nhop_num - 1) - i if create_intf_on_parent_port == False else ((nhop_num - 1) - i) * 2) + if i < (nhop_num - 1): + # Verify next hop group of the specified oid created in ASIC_DB + self.check_sub_port_intf_key_existence(self.asic_db, ASIC_NEXT_HOP_GROUP_TABLE, nhg_oid) - # Verify that next hop group members all belong to the next hop group of the specified oid - fv_dict = { - "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": nhg_oid, - } - for nhg_member_oid in nhg_member_oids: - self.check_sub_port_intf_fvs(self.asic_db, ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, nhg_member_oid, fv_dict) + # Verify next hop group member # created in ASIC_DB + nhg_member_oids = self.asic_db.wait_for_n_keys(ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, + (nhop_num - 1) - i if create_intf_on_parent_port == False \ + else ((nhop_num - 1) - i) * 2) + + # Verify that next hop group members all belong to the next hop group of the specified oid + fv_dict = { + "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": nhg_oid, + } + for nhg_member_oid in nhg_member_oids: + self.check_sub_port_intf_fvs(self.asic_db, ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, nhg_member_oid, fv_dict) + else: + assert nhg_oid == None + self.asic_db.wait_for_n_keys(ASIC_NEXT_HOP_GROUP_MEMBER_TABLE, 0) nhop_cnt = len(self.asic_db.get_keys(ASIC_NEXT_HOP_TABLE)) # Remove next hop objects on sub port interfaces diff --git a/tests/test_vlan.py b/tests/test_vlan.py index 28d3de3a29..03755cda04 100644 --- a/tests/test_vlan.py +++ b/tests/test_vlan.py @@ -233,8 +233,58 @@ def test_AddPortChannelToVlan(self, dvs): self.dvs_vlan.create_vlan(vlan) self.dvs_vlan.get_and_verify_vlan_ids(1) + + self.dvs_vlan.create_vlan_member(vlan, lag_interface, "tagged") + self.dvs_vlan.get_and_verify_vlan_member_ids(1) + + self.dvs_vlan.remove_vlan_member(vlan, lag_interface) + self.dvs_vlan.get_and_verify_vlan_member_ids(0) + + self.dvs_vlan.remove_vlan(vlan) + self.dvs_vlan.get_and_verify_vlan_ids(0) + + self.dvs_lag.remove_port_channel_member(lag_id, lag_member) + self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER", 0) + + self.dvs_lag.remove_port_channel(lag_id) + self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG", 0) + + def test_AddPortChannelToVlanRaceCondition(self, dvs): + + vlan = "2" + lag_member = "Ethernet0" + lag_id = "0001" + lag_interface = "PortChannel{}".format(lag_id) + + self.dvs_lag.create_port_channel(lag_id) + lag_entries = self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG", 1) + + self.dvs_lag.create_port_channel_member(lag_id, lag_member) + + # Verify the LAG has been initialized properly + lag_member_entries = self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER", 1) + fvs = self.dvs_vlan.asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_LAG_MEMBER", lag_member_entries[0]) + assert len(fvs) == 4 + assert fvs.get("SAI_LAG_MEMBER_ATTR_LAG_ID") == lag_entries[0] + assert self.dvs_vlan.asic_db.port_to_id_map[fvs.get("SAI_LAG_MEMBER_ATTR_PORT_ID")] == lag_member + + self.dvs_vlan.create_vlan(vlan) + self.dvs_vlan.get_and_verify_vlan_ids(1) + # Kill teamsyncd + dvs.stop_teamsyncd() + + # Delete netdevice + dvs.runcmd("ip link del PortChannel" + lag_id) self.dvs_vlan.create_vlan_member(vlan, lag_interface, "tagged") + + self.dvs_vlan.get_and_verify_vlan_member_ids(0) + #Start teamsyncd + dvs.start_teamsyncd() + + #Start teammgrd + dvs.restart_teammgrd() + self.dvs_vlan.get_and_verify_vlan_member_ids(1) self.dvs_vlan.remove_vlan_member(vlan, lag_interface) @@ -249,6 +299,8 @@ def test_AddPortChannelToVlan(self, dvs): self.dvs_lag.remove_port_channel(lag_id) self.dvs_vlan.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_LAG", 0) + + def test_AddVlanMemberWithNonExistVlan(self, dvs): vlan = "2"