diff --git a/.azure-pipelines/docker-sonic-vs/Dockerfile b/.azure-pipelines/docker-sonic-vs/Dockerfile index d425a6ffea..6feb8a7dfe 100644 --- a/.azure-pipelines/docker-sonic-vs/Dockerfile +++ b/.azure-pipelines/docker-sonic-vs/Dockerfile @@ -4,6 +4,15 @@ ARG docker_container_name ADD ["debs", "/debs"] +RUN dpkg --purge python-swsscommon +RUN dpkg --purge python3-swsscommon +RUN dpkg --purge swss +RUN dpkg --purge libsairedis +RUN dpkg --purge libswsscommon +RUN dpkg --purge libsaimetadata +RUN dpkg --purge libsaivs +RUN dpkg --purge syncd-vs + RUN dpkg -i /debs/libswsscommon_1.0.0_amd64.deb RUN dpkg -i /debs/python-swsscommon_1.0.0_amd64.deb RUN dpkg -i /debs/python3-swsscommon_1.0.0_amd64.deb diff --git a/.gitignore b/.gitignore index 439a0999f3..bfba272305 100644 --- a/.gitignore +++ b/.gitignore @@ -32,6 +32,11 @@ stamp-h1 **/Makefile autom4te.cache +# Temp files # +############## +*~ +*.swp + # Dependency Folder # ##################### deps/ @@ -52,6 +57,8 @@ cfgmgr/vxlanmgrd cfgmgr/natmgrd cfgmgr/sflowmgrd cfgmgr/macsecmgrd +cfgmgr/coppmgrd +cfgmgr/tunnelmgrd fpmsyncd/fpmsyncd gearsyncd/gearsyncd mclagsyncd/mclagsyncd @@ -68,6 +75,7 @@ tlm_teamd/tlm_teamd teamsyncd/teamsyncd tests/tests + # Test Files # ############## tests/log diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index d1ea5978c1..68bec2d3a0 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -3,6 +3,7 @@ CFLAGS_SAI = -I /usr/include/sai LIBNL_CFLAGS = -I/usr/include/libnl3 LIBNL_LIBS = -lnl-genl-3 -lnl-route-3 -lnl-3 SAIMETA_LIBS = -lsaimeta -lsaimetadata +COMMON_LIBS = -lswsscommon bin_PROGRAMS = vlanmgrd teammgrd portmgrd intfmgrd buffermgrd vrfmgrd nbrmgrd vxlanmgrd sflowmgrd natmgrd coppmgrd tunnelmgrd macsecmgrd @@ -25,64 +26,64 @@ endif vlanmgrd_SOURCES = vlanmgrd.cpp vlanmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h vlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vlanmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +vlanmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) teammgrd_SOURCES = teammgrd.cpp teammgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h teammgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) teammgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -teammgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +teammgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) portmgrd_SOURCES = portmgrd.cpp portmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h portmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) portmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -portmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +portmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) intfmgrd_SOURCES = intfmgrd.cpp intfmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h intfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) intfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -intfmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +intfmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) buffermgrd_SOURCES = buffermgrd.cpp buffermgr.cpp buffermgrdyn.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h buffermgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) buffermgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -buffermgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +buffermgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) vrfmgrd_SOURCES = vrfmgrd.cpp vrfmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h vrfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vrfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vrfmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +vrfmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) nbrmgrd_SOURCES = nbrmgrd.cpp nbrmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h nbrmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(LIBNL_CFLAGS) nbrmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(LIBNL_CPPFLAGS) -nbrmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) $(LIBNL_LIBS) +nbrmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) $(LIBNL_LIBS) vxlanmgrd_SOURCES = vxlanmgrd.cpp vxlanmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h vxlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vxlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vxlanmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +vxlanmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) sflowmgrd_SOURCES = sflowmgrd.cpp sflowmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h sflowmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) sflowmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -sflowmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +sflowmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) natmgrd_SOURCES = natmgrd.cpp natmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h natmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) natmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -natmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +natmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) coppmgrd_SOURCES = coppmgrd.cpp coppmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h coppmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) coppmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -coppmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +coppmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) tunnelmgrd_SOURCES = tunnelmgrd.cpp tunnelmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h tunnelmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) tunnelmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -tunnelmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +tunnelmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) macsecmgrd_SOURCES = macsecmgrd.cpp macsecmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h macsecmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) macsecmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -macsecmgrd_LDADD = -lswsscommon $(SAIMETA_LIBS) +macsecmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) diff --git a/cfgmgr/buffermgr.cpp b/cfgmgr/buffermgr.cpp index 7b1b73faff..684b17f31d 100644 --- a/cfgmgr/buffermgr.cpp +++ b/cfgmgr/buffermgr.cpp @@ -86,6 +86,7 @@ void BufferMgr::readPgProfileLookupFile(string file) task_process_status BufferMgr::doCableTask(string port, string cable_length) { m_cableLenLookup[port] = cable_length; + SWSS_LOG_INFO("Cable length set to %s for port %s", m_cableLenLookup[port].c_str(), port.c_str()); return task_process_status::task_success; } @@ -120,10 +121,11 @@ Create/update two tables: profile (in m_cfgBufferProfileTable) and port buffer ( } } */ -task_process_status BufferMgr::doSpeedUpdateTask(string port, string speed) +task_process_status BufferMgr::doSpeedUpdateTask(string port) { vector fvVector; string cable; + string speed; if (m_cableLenLookup.count(port) == 0) { @@ -132,7 +134,13 @@ task_process_status BufferMgr::doSpeedUpdateTask(string port, string speed) } cable = m_cableLenLookup[port]; + if (cable == "0m") + { + SWSS_LOG_NOTICE("Not creating/updating PG profile for port %s. Cable length is set to %s", port.c_str(), cable.c_str()); + return task_process_status::task_success; + } + speed = m_speedLookup[port]; if (m_pgProfileLookup.count(speed) == 0 || m_pgProfileLookup[speed].count(cable) == 0) { SWSS_LOG_ERROR("Unable to create/update PG profile for port %s. No PG profile configured for speed %s and cable length %s", @@ -368,11 +376,18 @@ void BufferMgr::doTask(Consumer &consumer) // receive and cache cable length table task_status = doCableTask(fvField(i), fvValue(i)); } - // In case of PORT table update, Buffer Manager is interested in speed update only - if (m_pgfile_processed && table_name == CFG_PORT_TABLE_NAME && fvField(i) == "speed") + if (m_pgfile_processed && table_name == CFG_PORT_TABLE_NAME && (fvField(i) == "speed" || fvField(i) == "admin_status")) { - // create/update profile for port - task_status = doSpeedUpdateTask(port, fvValue(i)); + if (fvField(i) == "speed") + { + m_speedLookup[port] = fvValue(i); + } + + if (m_speedLookup.count(port) != 0) + { + // create/update profile for port + task_status = doSpeedUpdateTask(port); + } } if (task_status != task_process_status::task_success) { diff --git a/cfgmgr/buffermgr.h b/cfgmgr/buffermgr.h index 5ad88f8901..c9777d2918 100644 --- a/cfgmgr/buffermgr.h +++ b/cfgmgr/buffermgr.h @@ -27,6 +27,7 @@ typedef std::map speed_map_t; typedef std::map pg_profile_lookup_t; typedef std::map port_cable_length_t; +typedef std::map port_speed_t; class BufferMgr : public Orch { @@ -52,10 +53,11 @@ class BufferMgr : public Orch pg_profile_lookup_t m_pgProfileLookup; port_cable_length_t m_cableLenLookup; + port_speed_t m_speedLookup; std::string getPgPoolMode(); void readPgProfileLookupFile(std::string); task_process_status doCableTask(std::string port, std::string cable_length); - task_process_status doSpeedUpdateTask(std::string port, std::string speed); + task_process_status doSpeedUpdateTask(std::string port); void doBufferTableTask(Consumer &consumer, ProducerStateTable &applTable); void transformSeperator(std::string &name); diff --git a/cfgmgr/buffermgrdyn.cpp b/cfgmgr/buffermgrdyn.cpp index 7b4b19388d..071fec6f78 100644 --- a/cfgmgr/buffermgrdyn.cpp +++ b/cfgmgr/buffermgrdyn.cpp @@ -987,8 +987,6 @@ task_process_status BufferMgrDynamic::refreshPgsForPort(const string &port, cons SWSS_LOG_DEBUG("Nothing to do for port %s since no PG configured on it", port.c_str()); } - portInfo.state = PORT_READY; - // Remove the old profile which is probably not referenced anymore. if (!profilesToBeReleased.empty()) { @@ -1217,6 +1215,9 @@ task_process_status BufferMgrDynamic::doUpdateBufferProfileForDynamicTh(buffer_p SWSS_LOG_DEBUG("Checking PG %s for dynamic profile %s", key.c_str(), profileName.c_str()); portsChecked.insert(portName); + if (port.state != PORT_READY) + continue; + rc = refreshPgsForPort(portName, port.effective_speed, port.cable_length, port.mtu); if (task_process_status::task_success != rc) { @@ -1452,6 +1453,7 @@ task_process_status BufferMgrDynamic::handlePortStateTable(KeyOpFieldsValuesTupl { if (isNonZero(portInfo.cable_length) && portInfo.state != PORT_ADMIN_DOWN) { + portInfo.state = PORT_READY; refreshPgsForPort(port, portInfo.effective_speed, portInfo.cable_length, portInfo.mtu); } } diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index b40f55692a..3e0ed862be 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -476,6 +476,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, string proxy_arp = ""; string grat_arp = ""; string mpls = ""; + string ipv6_link_local_mode = ""; for (auto idx : data) { @@ -506,11 +507,14 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, { mpls = value; } - - if (field == "nat_zone") + else if (field == "nat_zone") { nat_zone = value; } + else if (field == "ipv6_use_link_local_only") + { + ipv6_link_local_mode = value; + } } if (op == SET_COMMAND) @@ -551,6 +555,7 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, FieldValueTuple fvTuple("nat_zone", nat_zone); data.push_back(fvTuple); } + /* Set mpls */ if (!setIntfMpls(alias, mpls)) { @@ -562,6 +567,13 @@ bool IntfMgr::doIntfGeneralTask(const vector& keys, FieldValueTuple fvTuple("mpls", mpls); data.push_back(fvTuple); } + + /* Set ipv6 mode */ + if (!ipv6_link_local_mode.empty()) + { + FieldValueTuple fvTuple("ipv6_use_link_local_only", ipv6_link_local_mode); + data.push_back(fvTuple); + } } if (!parentAlias.empty()) @@ -731,8 +743,8 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, std::vector fvVector; FieldValueTuple f("family", ip_prefix.isV4() ? IPV4_NAME : IPV6_NAME); - // Don't send link local config to AppDB and Orchagent - if (ip_prefix.getIp().getAddrScope() != IpAddress::AddrScope::LINK_SCOPE) + // Don't send ipv4 link local config to AppDB and Orchagent + if ((ip_prefix.isV4() == false) || (ip_prefix.getIp().getAddrScope() != IpAddress::AddrScope::LINK_SCOPE)) { FieldValueTuple s("scope", "global"); fvVector.push_back(s); @@ -745,8 +757,8 @@ bool IntfMgr::doIntfAddrTask(const vector& keys, { setIntfIp(alias, "del", ip_prefix); - // Don't send link local config to AppDB and Orchagent - if (ip_prefix.getIp().getAddrScope() != IpAddress::AddrScope::LINK_SCOPE) + // Don't send ipv4 link local config to AppDB and Orchagent + if ((ip_prefix.isV4() == false) || (ip_prefix.getIp().getAddrScope() != IpAddress::AddrScope::LINK_SCOPE)) { m_appIntfTableProducer.del(appKey); m_stateIntfTable.del(keys[0] + state_db_key_delimiter + keys[1]); diff --git a/cfgmgr/vlanmgr.cpp b/cfgmgr/vlanmgr.cpp index e439420933..830f8d9d26 100644 --- a/cfgmgr/vlanmgr.cpp +++ b/cfgmgr/vlanmgr.cpp @@ -196,7 +196,8 @@ bool VlanMgr::setHostVlanMac(int vlan_id, const string &mac) // The command should be generated as: // /sbin/ip link set Vlan{{vlan_id}} address {{mac}} ostringstream cmds; - cmds << IP_CMD " link set " VLAN_PREFIX + std::to_string(vlan_id) + " address " << shellquote(mac); + cmds << IP_CMD " link set " VLAN_PREFIX + std::to_string(vlan_id) + " address " << shellquote(mac) << " && " + IP_CMD " link set " DOT1Q_BRIDGE_NAME " address " << shellquote(mac); std::string res; EXEC_WITH_ERROR_THROW(cmds.str(), res); diff --git a/configure.ac b/configure.ac index d7816b6f17..edca67de7c 100644 --- a/configure.ac +++ b/configure.ac @@ -13,8 +13,6 @@ AC_HEADER_STDC AC_CHECK_LIB([hiredis], [redisConnect],, AC_MSG_ERROR([libhiredis is not installed.])) -AC_CHECK_LIB([nl-genl-3], [genl_connect]) - AC_CHECK_LIB([team], [team_alloc], AM_CONDITIONAL(HAVE_LIBTEAM, true), [AC_MSG_WARN([libteam is not installed.]) @@ -46,7 +44,27 @@ AC_ARG_WITH(extra-lib, prefix where extra libraries are installed], [AC_SUBST(LDFLAGS, "$LDFLAGS -L${withval}")]) -CFLAGS_COMMON="-std=c++14 -Wall -fPIC -Wno-write-strings -I/usr/include/libnl3 -I/usr/include/swss" +AC_ARG_WITH(extra-usr-lib, +[ --with-extra-usr-lib=DIR + prefix where extra libraries are installed], +[AC_SUBST(LDFLAGS, "$LDFLAGS -L${withval}")]) + +AC_CHECK_LIB([nl-3], [nl_addr_alloc]) +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" + +AC_ARG_WITH(libnl-3.0-inc, +[ --with-libnl-3.0-inc=DIR + prefix where libnl-3.0 includes are installed], +[AC_SUBST(CPPFLAGS, "$CPPFLAGS -I${withval}") + AC_SUBST(LIBNL_INC_DIR, "${withval}")]) + +if test "${with_libnl_3_0_inc+set}" != set; then + CFLAGS_COMMON+=" -I/usr/include/libnl3" +fi CFLAGS_COMMON+=" -Werror" CFLAGS_COMMON+=" -Wno-reorder" diff --git a/fdbsyncd/Makefile.am b/fdbsyncd/Makefile.am index 0ad7d67df4..06beefaf22 100644 --- a/fdbsyncd/Makefile.am +++ b/fdbsyncd/Makefile.am @@ -1,16 +1,16 @@ -INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart - -bin_PROGRAMS = fdbsyncd - -if DEBUG -DBGFLAGS = -ggdb -DDEBUG -else -DBGFLAGS = -g -endif - -fdbsyncd_SOURCES = fdbsyncd.cpp fdbsync.cpp $(top_srcdir)/warmrestart/warmRestartAssist.cpp - -fdbsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) -fdbsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) -fdbsyncd_LDADD = -lnl-3 -lnl-route-3 -lswsscommon $(COV_LDFLAGS) - +INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart + +bin_PROGRAMS = fdbsyncd + +if DEBUG +DBGFLAGS = -ggdb -DDEBUG +else +DBGFLAGS = -g +endif + +fdbsyncd_SOURCES = fdbsyncd.cpp fdbsync.cpp $(top_srcdir)/warmrestart/warmRestartAssist.cpp + +fdbsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) +fdbsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) +fdbsyncd_LDADD = -lnl-3 -lnl-route-3 -lswsscommon $(COV_LDFLAGS) + diff --git a/fdbsyncd/fdbsync.cpp b/fdbsyncd/fdbsync.cpp index 30c9e2d54c..0cdcc63214 100644 --- a/fdbsyncd/fdbsync.cpp +++ b/fdbsyncd/fdbsync.cpp @@ -1,721 +1,881 @@ -#include -#include -#include -#include -#include -#include - -#include "logger.h" -#include "dbconnector.h" -#include "producerstatetable.h" -#include "ipaddress.h" -#include "netmsg.h" -#include "macaddress.h" -#include "exec.h" -#include "fdbsync.h" -#include "warm_restart.h" -#include "errno.h" - -using namespace std; -using namespace swss; - -#define VXLAN_BR_IF_NAME_PREFIX "Brvxlan" - -FdbSync::FdbSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *config_db) : - m_fdbTable(pipelineAppDB, APP_VXLAN_FDB_TABLE_NAME), - m_imetTable(pipelineAppDB, APP_VXLAN_REMOTE_VNI_TABLE_NAME), - m_fdbStateTable(stateDb, STATE_FDB_TABLE_NAME), - m_cfgEvpnNvoTable(config_db, CFG_VXLAN_EVPN_NVO_TABLE_NAME) -{ - m_AppRestartAssist = new AppRestartAssist(pipelineAppDB, "fdbsyncd", "swss", DEFAULT_FDBSYNC_WARMSTART_TIMER); - if (m_AppRestartAssist) - { - m_AppRestartAssist->registerAppTable(APP_VXLAN_FDB_TABLE_NAME, &m_fdbTable); - m_AppRestartAssist->registerAppTable(APP_VXLAN_REMOTE_VNI_TABLE_NAME, &m_imetTable); - } -} - -FdbSync::~FdbSync() -{ - if (m_AppRestartAssist) - { - delete m_AppRestartAssist; - } -} - - -// Check if interface entries are restored in kernel -bool FdbSync::isIntfRestoreDone() -{ - vector required_modules = { - "vxlanmgrd", - "intfmgrd", - "vlanmgrd", - "vrfmgrd" - }; - - for (string& module : required_modules) - { - WarmStart::WarmStartState state; - - WarmStart::getWarmStartState(module, state); - if (state == WarmStart::REPLAYED || state == WarmStart::RECONCILED) - { - SWSS_LOG_INFO("Module %s Replayed or Reconciled %d",module.c_str(), (int) state); - } - else - { - SWSS_LOG_INFO("Module %s NOT Replayed or Reconciled %d",module.c_str(), (int) state); - return false; - } - } - - return true; -} - -void FdbSync::processCfgEvpnNvo() -{ - std::deque entries; - m_cfgEvpnNvoTable.pops(entries); - bool lastNvoState = m_isEvpnNvoExist; - - for (auto entry: entries) - { - std::string op = kfvOp(entry); - - if (op == SET_COMMAND) - { - m_isEvpnNvoExist = true; - } - else if (op == DEL_COMMAND) - { - m_isEvpnNvoExist = false; - } - - if (lastNvoState != m_isEvpnNvoExist) - { - updateAllLocalMac(); - } - } - return; -} - -void FdbSync::updateAllLocalMac() -{ - for ( auto it = m_fdb_mac.begin(); it != m_fdb_mac.end(); ++it ) - { - if (m_isEvpnNvoExist) - { - /* Add the Local FDB entry into Kernel */ - addLocalMac(it->first, "replace"); - } - else - { - /* Delete the Local FDB entry from Kernel */ - addLocalMac(it->first, "del"); - } - } -} - -void FdbSync::processStateFdb() -{ - struct m_fdb_info info; - std::deque entries; - - m_fdbStateTable.pops(entries); - - int count =0 ; - for (auto entry: entries) - { - count++; - std::string key = kfvKey(entry); - std::string op = kfvOp(entry); - - std::size_t delimiter = key.find_first_of(":"); - auto vlan_name = key.substr(0, delimiter); - auto mac_address = key.substr(delimiter+1); - - info.vid = vlan_name; - info.mac = mac_address; - - if(op == "SET") - { - info.op_type = FDB_OPER_ADD ; - } - else - { - info.op_type = FDB_OPER_DEL ; - } - - SWSS_LOG_INFO("FDBSYNCD STATE FDB updates key=%s, operation=%s\n", key.c_str(), op.c_str()); - - for (auto i : kfvFieldsValues(entry)) - { - SWSS_LOG_INFO(" FDBSYNCD STATE FDB updates : " - "FvFiels %s, FvValues: %s \n", fvField(i).c_str(), fvValue(i).c_str()); - - if(fvField(i) == "port") - { - info.port_name = fvValue(i); - } - - if(fvField(i) == "type") - { - if(fvValue(i) == "dynamic") - { - info.type = FDB_TYPE_DYNAMIC; - } - else if (fvValue(i) == "static") - { - info.type = FDB_TYPE_STATIC; - } - } - } - - if (op != "SET" && macCheckSrcDB(&info) == false) - { - continue; - } - updateLocalMac(&info); - } -} - -void FdbSync::macUpdateCache(struct m_fdb_info *info) -{ - string key = info->vid + ":" + info->mac; - m_fdb_mac[key].port_name = info->port_name; - m_fdb_mac[key].type = info->type; - - return; -} - -bool FdbSync::macCheckSrcDB(struct m_fdb_info *info) -{ - string key = info->vid + ":" + info->mac; - if (m_fdb_mac.find(key) != m_fdb_mac.end()) - { - SWSS_LOG_INFO("DEL_KEY %s ", key.c_str()); - return true; - } - - return false; -} - -void FdbSync::macDelVxlanEntry(string auxkey, struct m_fdb_info *info) -{ - std::string vtep = m_mac[auxkey].vtep; - - const std::string cmds = std::string("") - + " bridge fdb del " + info->mac + " dev " - + m_mac[auxkey].ifname + " dst " + vtep + " vlan " + info->vid.substr(4); - - std::string res; - int ret = swss::exec(cmds, res); - if (ret != 0) - { - SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - } - - SWSS_LOG_INFO("Success cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - - return; -} - -void FdbSync::updateLocalMac (struct m_fdb_info *info) -{ - char *op; - char *type; - string port_name = ""; - string key = info->vid + ":" + info->mac; - short fdb_type; /*dynamic or static*/ - - if (info->op_type == FDB_OPER_ADD) - { - macUpdateCache(info); - op = "replace"; - port_name = info->port_name; - fdb_type = info->type; - /* Check if this vlan+key is also learned by vxlan neighbor then delete learned on */ - if (m_mac.find(key) != m_mac.end()) - { - macDelVxlanEntry(key, info); - SWSS_LOG_INFO("Local learn event deleting from VXLAN table DEL_KEY %s", key.c_str()); - macDelVxlan(key); - } - } - else - { - op = "del"; - port_name = m_fdb_mac[key].port_name; - fdb_type = m_fdb_mac[key].type; - m_fdb_mac.erase(key); - } - - if (!m_isEvpnNvoExist) - { - SWSS_LOG_INFO("Ignore kernel update EVPN NVO is not configured MAC %s", key.c_str()); - return; - } - - if (fdb_type == FDB_TYPE_DYNAMIC) - { - type = "dynamic"; - } - else - { - type = "static"; - } - - const std::string cmds = std::string("") - + " bridge fdb " + op + " " + info->mac + " dev " - + port_name + " master " + type + " vlan " + info->vid.substr(4); - - std::string res; - int ret = swss::exec(cmds, res); - - SWSS_LOG_INFO("cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - - return; -} - -void FdbSync::addLocalMac(string key, string op) -{ - char *type; - string port_name = ""; - string mac = ""; - string vlan = ""; - size_t str_loc = string::npos; - - str_loc = key.find(":"); - if (str_loc == string::npos) - { - SWSS_LOG_ERROR("Local MAC issue with Key:%s", key.c_str()); - return; - } - vlan = key.substr(4, str_loc-4); - mac = key.substr(str_loc+1, std::string::npos); - - SWSS_LOG_INFO("Local route Vlan:%s MAC:%s Key:%s Op:%s", vlan.c_str(), mac.c_str(), key.c_str(), op.c_str()); - - if (m_fdb_mac.find(key)!=m_fdb_mac.end()) - { - port_name = m_fdb_mac[key].port_name; - if (port_name.empty()) - { - SWSS_LOG_INFO("Port name not present MAC route Key:%s", key.c_str()); - return; - } - - if (m_fdb_mac[key].type == FDB_TYPE_DYNAMIC) - { - type = "dynamic"; - } - else - { - type = "static"; - } - - const std::string cmds = std::string("") - + " bridge fdb " + op + " " + mac + " dev " - + port_name + " master " + type + " vlan " + vlan; - - std::string res; - int ret = swss::exec(cmds, res); - if (ret != 0) - { - SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - } - - SWSS_LOG_INFO("Config triggered cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - } - return; -} - -/* - * This is a special case handling where mac is learned in the ASIC. - * Then MAC is learned in the Kernel, Since this mac is learned in the Kernel - * This MAC will age out, when MAC delete is received from the Kernel. - * If MAC is still present in the state DB cache then fdbsyncd will be - * re-programmed with MAC in the Kernel - */ -void FdbSync::macRefreshStateDB(int vlan, string kmac) -{ - string key = "Vlan" + to_string(vlan) + ":" + kmac; - char *type; - string port_name = ""; - - SWSS_LOG_INFO("Refreshing Vlan:%d MAC route MAC:%s Key %s", vlan, kmac.c_str(), key.c_str()); - - if (m_fdb_mac.find(key)!=m_fdb_mac.end()) - { - port_name = m_fdb_mac[key].port_name; - if (port_name.empty()) - { - SWSS_LOG_INFO("Port name not present MAC route Key:%s", key.c_str()); - return; - } - - if (m_fdb_mac[key].type == FDB_TYPE_DYNAMIC) - { - type = "dynamic"; - } - else - { - type = "static"; - } - - const std::string cmds = std::string("") - + " bridge fdb " + "replace" + " " + kmac + " dev " - + port_name + " master " + type + " vlan " + to_string(vlan); - - std::string res; - int ret = swss::exec(cmds, res); - if (ret != 0) - { - SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - } - - SWSS_LOG_INFO("Refreshing cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); - } - return; -} - -bool FdbSync::checkImetExist(string key, uint32_t vni) -{ - if (m_imet_route.find(key) != m_imet_route.end()) - { - SWSS_LOG_INFO("IMET exist key:%s Vni:%d", key.c_str(), vni); - return false; - } - m_imet_route[key].vni = vni; - return true; -} - -bool FdbSync::checkDelImet(string key, uint32_t vni) -{ - int ret = false; - - SWSS_LOG_INFO("Del IMET key:%s Vni:%d", key.c_str(), vni); - if (m_imet_route.find(key) != m_imet_route.end()) - { - ret = true; - m_imet_route.erase(key); - } - return ret; -} - -void FdbSync::imetAddRoute(struct in_addr vtep, string vlan_str, uint32_t vni) -{ - string vlan_id = "Vlan" + vlan_str; - string key = vlan_id + ":" + inet_ntoa(vtep); - - if (!checkImetExist(key, vni)) - { - return; - } - - SWSS_LOG_INFO("%sIMET Add route key:%s vtep:%s %s", - m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "", - key.c_str(), inet_ntoa(vtep), vlan_id.c_str()); - - std::vector fvVector; - FieldValueTuple f("vni", to_string(vni)); - fvVector.push_back(f); - - // If warmstart is in progress, we take all netlink changes into the cache map - if (m_AppRestartAssist->isWarmStartInProgress()) - { - m_AppRestartAssist->insertToMap(APP_VXLAN_REMOTE_VNI_TABLE_NAME, key, fvVector, false); - return; - } - - m_imetTable.set(key, fvVector); - return; -} - -void FdbSync::imetDelRoute(struct in_addr vtep, string vlan_str, uint32_t vni) -{ - string vlan_id = "Vlan" + vlan_str; - string key = vlan_id + ":" + inet_ntoa(vtep); - - if (!checkDelImet(key, vni)) - { - return; - } - - SWSS_LOG_INFO("%sIMET Del route key:%s vtep:%s %s", - m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "", - key.c_str(), inet_ntoa(vtep), vlan_id.c_str()); - - std::vector fvVector; - FieldValueTuple f("vni", to_string(vni)); - fvVector.push_back(f); - - // If warmstart is in progress, we take all netlink changes into the cache map - if (m_AppRestartAssist->isWarmStartInProgress()) - { - m_AppRestartAssist->insertToMap(APP_VXLAN_REMOTE_VNI_TABLE_NAME, key, fvVector, true); - return; - } - - m_imetTable.del(key); - return; -} - -void FdbSync::macDelVxlanDB(string key) -{ - string vtep = m_mac[key].vtep; - string type; - string vni = to_string(m_mac[key].vni); - type = m_mac[key].type; - - std::vector fvVector; - FieldValueTuple rv("remote_vtep", vtep); - FieldValueTuple t("type", type); - FieldValueTuple v("vni", vni); - fvVector.push_back(rv); - fvVector.push_back(t); - fvVector.push_back(v); - - SWSS_LOG_NOTICE("%sVXLAN_FDB_TABLE: DEL_KEY %s vtep:%s type:%s", - m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "" , - key.c_str(), vtep.c_str(), type.c_str()); - - // If warmstart is in progress, we take all netlink changes into the cache map - if (m_AppRestartAssist->isWarmStartInProgress()) - { - m_AppRestartAssist->insertToMap(APP_VXLAN_FDB_TABLE_NAME, key, fvVector, true); - return; - } - - m_fdbTable.del(key); - return; - -} - -void FdbSync::macAddVxlan(string key, struct in_addr vtep, string type, uint32_t vni, string intf_name) -{ - string svtep = inet_ntoa(vtep); - string svni = to_string(vni); - - /* Update the DB with Vxlan MAC */ - m_mac[key] = {svtep, type, vni, intf_name}; - - std::vector fvVector; - FieldValueTuple rv("remote_vtep", svtep); - FieldValueTuple t("type", type); - FieldValueTuple v("vni", svni); - fvVector.push_back(rv); - fvVector.push_back(t); - fvVector.push_back(v); - - SWSS_LOG_INFO("%sVXLAN_FDB_TABLE: ADD_KEY %s vtep:%s type:%s", - m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "" , - key.c_str(), svtep.c_str(), type.c_str()); - // If warmstart is in progress, we take all netlink changes into the cache map - if (m_AppRestartAssist->isWarmStartInProgress()) - { - m_AppRestartAssist->insertToMap(APP_VXLAN_FDB_TABLE_NAME, key, fvVector, false); - return; - } - - m_fdbTable.set(key, fvVector); - - return; -} - -void FdbSync::macDelVxlan(string key) -{ - if (m_mac.find(key) != m_mac.end()) - { - SWSS_LOG_INFO("DEL_KEY %s vtep:%s type:%s", key.c_str(), m_mac[key].vtep.c_str(), m_mac[key].type.c_str()); - macDelVxlanDB(key); - m_mac.erase(key); - } - return; -} - -void FdbSync::onMsgNbr(int nlmsg_type, struct nl_object *obj) -{ - char macStr[MAX_ADDR_SIZE + 1] = {0}; - struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj; - struct in_addr vtep = {0}; - int vlan = 0, ifindex = 0; - uint32_t vni = 0; - nl_addr *vtep_addr; - string ifname; - string key; - bool delete_key = false; - size_t str_loc = string::npos; - string type = ""; - string vlan_id = ""; - bool isVxlanIntf = false; - - if ((nlmsg_type != RTM_NEWNEIGH) && (nlmsg_type != RTM_GETNEIGH) && - (nlmsg_type != RTM_DELNEIGH)) - { - return; - } - - /* Only MAC route is to be supported */ - if (rtnl_neigh_get_family(neigh) != AF_BRIDGE) - { - return; - } - ifindex = rtnl_neigh_get_ifindex(neigh); - if (m_intf_info.find(ifindex) != m_intf_info.end()) - { - isVxlanIntf = true; - ifname = m_intf_info[ifindex].ifname; - } - - nl_addr2str(rtnl_neigh_get_lladdr(neigh), macStr, MAX_ADDR_SIZE); - - if (isVxlanIntf == false) - { - if (nlmsg_type != RTM_DELNEIGH) - { - return; - } - } - else - { - /* If this is for vnet bridge vxlan interface, then return */ - if (ifname.find(VXLAN_BR_IF_NAME_PREFIX) != string::npos) - { - return; - } - - /* VxLan netdevice should be in - format */ - str_loc = ifname.rfind("-"); - if (str_loc == string::npos) - { - return; - } - - vlan_id = "Vlan" + ifname.substr(str_loc+1, std::string::npos); - vni = m_intf_info[ifindex].vni; - } - - - if (isVxlanIntf == false) - { - vlan = rtnl_neigh_get_vlan(neigh); - if (m_isEvpnNvoExist) - { - macRefreshStateDB(vlan, macStr); - } - return; - } - - vtep_addr = rtnl_neigh_get_dst(neigh); - if (vtep_addr == NULL) - { - return; - } - else - { - /* Currently we only support ipv4 tunnel endpoints */ - vtep.s_addr = *(uint32_t *)nl_addr_get_binary_addr(vtep_addr); - SWSS_LOG_INFO("Tunnel IP %s Int%d", inet_ntoa(vtep), *(uint32_t *)nl_addr_get_binary_addr(vtep_addr)); - } - - int state = rtnl_neigh_get_state(neigh); - if ((nlmsg_type == RTM_DELNEIGH) || (state == NUD_INCOMPLETE) || - (state == NUD_FAILED)) - { - delete_key = true; - } - - if (state & NUD_NOARP) - { - /* This is a static route */ - type = "static"; - } - else - { - type = "dynamic"; - } - - /* Handling IMET routes */ - if (MacAddress(macStr) == MacAddress("00:00:00:00:00:00")) - { - if (vtep.s_addr) - { - string vlan_str = ifname.substr(str_loc+1, string::npos); - - if (!delete_key) - { - imetAddRoute(vtep, vlan_str, vni); - } - else - { - imetDelRoute(vtep, vlan_str, vni); - } - } - return; - } - - key+= vlan_id; - key+= ":"; - key+= macStr; - - if (!delete_key) - { - macAddVxlan(key, vtep, type, vni, ifname); - } - else - { - macDelVxlan(key); - } - return; -} - -void FdbSync::onMsgLink(int nlmsg_type, struct nl_object *obj) -{ - struct rtnl_link *link; - char *ifname = NULL; - char *nil = "NULL"; - int ifindex; - unsigned int vni; - - link = (struct rtnl_link *)obj; - ifname = rtnl_link_get_name(link); - ifindex = rtnl_link_get_ifindex(link); - if (rtnl_link_is_vxlan(link) == 0) - { - return; - } - - if (rtnl_link_vxlan_get_id(link, &vni) != 0) - { - SWSS_LOG_INFO("Op:%d VxLAN dev:%s index:%d vni:%d. Not found", nlmsg_type, ifname? ifname: nil, ifindex, vni); - return; - } - SWSS_LOG_INFO("Op:%d VxLAN dev %s index:%d vni:%d", nlmsg_type, ifname? ifname: nil, ifindex, vni); - if (nlmsg_type == RTM_NEWLINK) - { - m_intf_info[ifindex].vni = vni; - m_intf_info[ifindex].ifname = ifname; - } - return; -} - -void FdbSync::onMsg(int nlmsg_type, struct nl_object *obj) -{ - if ((nlmsg_type != RTM_NEWLINK) && - (nlmsg_type != RTM_NEWNEIGH) && (nlmsg_type != RTM_DELNEIGH)) - { - SWSS_LOG_DEBUG("netlink: unhandled event: %d", nlmsg_type); - return; - } - if (nlmsg_type == RTM_NEWLINK) - { - onMsgLink(nlmsg_type, obj); - } - else - { - onMsgNbr(nlmsg_type, obj); - } -} - +#include +#include +#include +#include +#include +#include + +#include "logger.h" +#include "dbconnector.h" +#include "producerstatetable.h" +#include "ipaddress.h" +#include "netmsg.h" +#include "macaddress.h" +#include "exec.h" +#include "fdbsync.h" +#include "warm_restart.h" +#include "errno.h" + +using namespace std; +using namespace swss; + +#define VXLAN_BR_IF_NAME_PREFIX "Brvxlan" + +FdbSync::FdbSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *config_db) : + m_fdbTable(pipelineAppDB, APP_VXLAN_FDB_TABLE_NAME), + m_imetTable(pipelineAppDB, APP_VXLAN_REMOTE_VNI_TABLE_NAME), + m_fdbStateTable(stateDb, STATE_FDB_TABLE_NAME), + m_mclagRemoteFdbStateTable(stateDb, STATE_MCLAG_REMOTE_FDB_TABLE_NAME), + m_cfgEvpnNvoTable(config_db, CFG_VXLAN_EVPN_NVO_TABLE_NAME) +{ + m_AppRestartAssist = new AppRestartAssist(pipelineAppDB, "fdbsyncd", "swss", DEFAULT_FDBSYNC_WARMSTART_TIMER); + if (m_AppRestartAssist) + { + m_AppRestartAssist->registerAppTable(APP_VXLAN_FDB_TABLE_NAME, &m_fdbTable); + m_AppRestartAssist->registerAppTable(APP_VXLAN_REMOTE_VNI_TABLE_NAME, &m_imetTable); + } +} + +FdbSync::~FdbSync() +{ + if (m_AppRestartAssist) + { + delete m_AppRestartAssist; + } +} + +// Check if interface entries are restored in kernel +bool FdbSync::isIntfRestoreDone() +{ + vector required_modules = { + "vxlanmgrd", + "intfmgrd", + "vlanmgrd", + "vrfmgrd" + }; + + for (string& module : required_modules) + { + WarmStart::WarmStartState state; + + WarmStart::getWarmStartState(module, state); + if (state == WarmStart::REPLAYED || state == WarmStart::RECONCILED) + { + SWSS_LOG_INFO("Module %s Replayed or Reconciled %d",module.c_str(), (int) state); + } + else + { + SWSS_LOG_INFO("Module %s NOT Replayed or Reconciled %d",module.c_str(), (int) state); + return false; + } + } + + return true; +} + +void FdbSync::processCfgEvpnNvo() +{ + std::deque entries; + m_cfgEvpnNvoTable.pops(entries); + bool lastNvoState = m_isEvpnNvoExist; + + for (auto entry: entries) + { + std::string op = kfvOp(entry); + + if (op == SET_COMMAND) + { + m_isEvpnNvoExist = true; + } + else if (op == DEL_COMMAND) + { + m_isEvpnNvoExist = false; + } + + if (lastNvoState != m_isEvpnNvoExist) + { + updateAllLocalMac(); + } + } + return; +} + +void FdbSync::updateAllLocalMac() +{ + for ( auto it = m_fdb_mac.begin(); it != m_fdb_mac.end(); ++it ) + { + if (m_isEvpnNvoExist) + { + /* Add the Local FDB entry into Kernel */ + addLocalMac(it->first, "replace"); + } + else + { + /* Delete the Local FDB entry from Kernel */ + addLocalMac(it->first, "del"); + } + } +} + +void FdbSync::processStateFdb() +{ + struct m_fdb_info info; + std::deque entries; + + m_fdbStateTable.pops(entries); + + int count =0 ; + for (auto entry: entries) + { + count++; + std::string key = kfvKey(entry); + std::string op = kfvOp(entry); + + std::size_t delimiter = key.find_first_of(":"); + auto vlan_name = key.substr(0, delimiter); + auto mac_address = key.substr(delimiter+1); + + info.vid = vlan_name; + info.mac = mac_address; + + if(op == "SET") + { + info.op_type = FDB_OPER_ADD ; + } + else + { + info.op_type = FDB_OPER_DEL ; + } + + SWSS_LOG_INFO("FDBSYNCD STATE FDB updates key=%s, operation=%s\n", key.c_str(), op.c_str()); + + for (auto i : kfvFieldsValues(entry)) + { + SWSS_LOG_INFO(" FDBSYNCD STATE FDB updates : " + "FvFiels %s, FvValues: %s \n", fvField(i).c_str(), fvValue(i).c_str()); + + if(fvField(i) == "port") + { + info.port_name = fvValue(i); + } + + if(fvField(i) == "type") + { + if(fvValue(i) == "dynamic") + { + info.type = FDB_TYPE_DYNAMIC; + } + else if (fvValue(i) == "static") + { + info.type = FDB_TYPE_STATIC; + } + } + } + + if (op != "SET" && macCheckSrcDB(&info) == false) + { + continue; + } + updateLocalMac(&info); + } +} + +void FdbSync::processStateMclagRemoteFdb() +{ + struct m_fdb_info info; + std::deque entries; + + m_mclagRemoteFdbStateTable.pops(entries); + + int count =0 ; + for (auto entry: entries) + { + count++; + std::string key = kfvKey(entry); + std::string op = kfvOp(entry); + + std::size_t delimiter = key.find_first_of(":"); + auto vlan_name = key.substr(0, delimiter); + auto mac_address = key.substr(delimiter+1); + + info.vid = vlan_name; + info.mac = mac_address; + + if(op == "SET") + { + info.op_type = FDB_OPER_ADD ; + } + else + { + info.op_type = FDB_OPER_DEL ; + } + + SWSS_LOG_INFO("FDBSYNCD STATE FDB updates key=%s, operation=%s\n", key.c_str(), op.c_str()); + + for (auto i : kfvFieldsValues(entry)) + { + SWSS_LOG_INFO(" FDBSYNCD STATE FDB updates : " + "FvFiels %s, FvValues: %s \n", fvField(i).c_str(), fvValue(i).c_str()); + + if(fvField(i) == "port") + { + info.port_name = fvValue(i); + } + + if(fvField(i) == "type") + { + if(fvValue(i) == "dynamic") + { + info.type = FDB_TYPE_DYNAMIC; + } + else if (fvValue(i) == "static") + { + info.type = FDB_TYPE_STATIC; + } + } + } + + if (op != "SET" && macCheckSrcDB(&info) == false) + { + continue; + } + updateMclagRemoteMac(&info); + } +} + +void FdbSync::macUpdateCache(struct m_fdb_info *info) +{ + string key = info->vid + ":" + info->mac; + m_fdb_mac[key].port_name = info->port_name; + m_fdb_mac[key].type = info->type; + + return; +} + +void FdbSync::macUpdateMclagRemoteCache(struct m_fdb_info *info) +{ + string key = info->vid + ":" + info->mac; + m_mclag_remote_fdb_mac[key].port_name = info->port_name; + m_mclag_remote_fdb_mac[key].type = info->type; + + return; +} + +bool FdbSync::macCheckSrcDB(struct m_fdb_info *info) +{ + string key = info->vid + ":" + info->mac; + if (m_fdb_mac.find(key) != m_fdb_mac.end()) + { + SWSS_LOG_INFO("DEL_KEY %s ", key.c_str()); + return true; + } + + return false; +} + +void FdbSync::macDelVxlanEntry(string auxkey, struct m_fdb_info *info) +{ + std::string vtep = m_mac[auxkey].vtep; + + const std::string cmds = std::string("") + + " bridge fdb del " + info->mac + " dev " + + m_mac[auxkey].ifname + " dst " + vtep + " vlan " + info->vid.substr(4); + + std::string res; + int ret = swss::exec(cmds, res); + if (ret != 0) + { + SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + + SWSS_LOG_INFO("Success cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + + return; +} + +void FdbSync::updateLocalMac (struct m_fdb_info *info) +{ + char *op; + char *type; + string port_name = ""; + string key = info->vid + ":" + info->mac; + short fdb_type; /*dynamic or static*/ + + if (info->op_type == FDB_OPER_ADD) + { + macUpdateCache(info); + op = "replace"; + port_name = info->port_name; + fdb_type = info->type; + /* Check if this vlan+key is also learned by vxlan neighbor then delete learned on */ + if (m_mac.find(key) != m_mac.end()) + { + macDelVxlanEntry(key, info); + SWSS_LOG_INFO("Local learn event deleting from VXLAN table DEL_KEY %s", key.c_str()); + macDelVxlan(key); + } + } + else + { + op = "del"; + port_name = m_fdb_mac[key].port_name; + fdb_type = m_fdb_mac[key].type; + m_fdb_mac.erase(key); + } + + if (!m_isEvpnNvoExist) + { + SWSS_LOG_INFO("Ignore kernel update EVPN NVO is not configured MAC %s", key.c_str()); + return; + } + + if (fdb_type == FDB_TYPE_DYNAMIC) + { + type = "dynamic"; + } + else + { + type = "static"; + } + + const std::string cmds = std::string("") + + " bridge fdb " + op + " " + info->mac + " dev " + + port_name + " master " + type + " vlan " + info->vid.substr(4); + + std::string res; + int ret = swss::exec(cmds, res); + + SWSS_LOG_INFO("cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + + return; +} + +void FdbSync::addLocalMac(string key, string op) +{ + char *type; + string port_name = ""; + string mac = ""; + string vlan = ""; + size_t str_loc = string::npos; + + str_loc = key.find(":"); + if (str_loc == string::npos) + { + SWSS_LOG_ERROR("Local MAC issue with Key:%s", key.c_str()); + return; + } + vlan = key.substr(4, str_loc-4); + mac = key.substr(str_loc+1, std::string::npos); + + SWSS_LOG_INFO("Local route Vlan:%s MAC:%s Key:%s Op:%s", vlan.c_str(), mac.c_str(), key.c_str(), op.c_str()); + + if (m_fdb_mac.find(key)!=m_fdb_mac.end()) + { + port_name = m_fdb_mac[key].port_name; + if (port_name.empty()) + { + SWSS_LOG_INFO("Port name not present MAC route Key:%s", key.c_str()); + return; + } + + if (m_fdb_mac[key].type == FDB_TYPE_DYNAMIC) + { + type = "dynamic"; + } + else + { + type = "static"; + } + + const std::string cmds = std::string("") + + " bridge fdb " + op + " " + mac + " dev " + + port_name + " master " + type + " vlan " + vlan; + + std::string res; + int ret = swss::exec(cmds, res); + if (ret != 0) + { + SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + + SWSS_LOG_INFO("Config triggered cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + return; +} + +void FdbSync::updateMclagRemoteMac (struct m_fdb_info *info) +{ + char *op; + char *type; + string port_name = ""; + string key = info->vid + ":" + info->mac; + short fdb_type; /*dynamic or static*/ + + if (info->op_type == FDB_OPER_ADD) + { + macUpdateMclagRemoteCache(info); + op = "replace"; + port_name = info->port_name; + fdb_type = info->type; + } + else + { + op = "del"; + port_name = m_mclag_remote_fdb_mac[key].port_name; + fdb_type = m_mclag_remote_fdb_mac[key].type; + m_mclag_remote_fdb_mac.erase(key); + } + + if (fdb_type == FDB_TYPE_DYNAMIC) + { + type = "dynamic"; + } + else + { + type = "static"; + } + + const std::string cmds = std::string("") + + " bridge fdb " + op + " " + info->mac + " dev " + + port_name + " master " + type + " vlan " + info->vid.substr(4); + + std::string res; + int ret = swss::exec(cmds, res); + + SWSS_LOG_INFO("cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + + return; +} + +void FdbSync::updateMclagRemoteMacPort(int ifindex, int vlan, std::string mac) +{ + string key = "Vlan" + to_string(vlan) + ":" + mac; + int type = 0; + string port_name = ""; + + SWSS_LOG_INFO("Updating Intf %d, Vlan:%d MAC:%s Key %s", ifindex, vlan, mac.c_str(), key.c_str()); + + if (m_mclag_remote_fdb_mac.find(key) != m_mclag_remote_fdb_mac.end()) + { + type = m_mclag_remote_fdb_mac[key].type; + port_name = m_mclag_remote_fdb_mac[key].port_name; + SWSS_LOG_INFO(" port %s, type %d\n", port_name.c_str(), type); + + if (type == FDB_TYPE_STATIC) + { + const std::string cmds = std::string("") + + " bridge fdb replace" + " " + mac + " dev " + + port_name + " master static vlan " + to_string(vlan); + + std::string res; + int ret = swss::exec(cmds, res); + if (ret != 0) + { + SWSS_LOG_NOTICE("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + return; + } + + SWSS_LOG_NOTICE("Update cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + } + return; +} + +/* + * This is a special case handling where mac is learned in the ASIC. + * Then MAC is learned in the Kernel, Since this mac is learned in the Kernel + * This MAC will age out, when MAC delete is received from the Kernel. + * If MAC is still present in the state DB cache then fdbsyncd will be + * re-programmed with MAC in the Kernel + */ +void FdbSync::macRefreshStateDB(int vlan, string kmac) +{ + string key = "Vlan" + to_string(vlan) + ":" + kmac; + char *type; + string port_name = ""; + + SWSS_LOG_INFO("Refreshing Vlan:%d MAC route MAC:%s Key %s", vlan, kmac.c_str(), key.c_str()); + + if (m_fdb_mac.find(key)!=m_fdb_mac.end()) + { + port_name = m_fdb_mac[key].port_name; + if (port_name.empty()) + { + SWSS_LOG_INFO("Port name not present MAC route Key:%s", key.c_str()); + return; + } + + if (m_fdb_mac[key].type == FDB_TYPE_DYNAMIC) + { + type = "dynamic"; + } + else + { + type = "static"; + } + + const std::string cmds = std::string("") + + " bridge fdb " + "replace" + " " + kmac + " dev " + + port_name + " master " + type + " vlan " + to_string(vlan); + + std::string res; + int ret = swss::exec(cmds, res); + if (ret != 0) + { + SWSS_LOG_INFO("Failed cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + + SWSS_LOG_INFO("Refreshing cmd:%s, res=%s, ret=%d", cmds.c_str(), res.c_str(), ret); + } + return; +} + +bool FdbSync::checkImetExist(string key, uint32_t vni) +{ + if (m_imet_route.find(key) != m_imet_route.end()) + { + SWSS_LOG_INFO("IMET exist key:%s Vni:%d", key.c_str(), vni); + return false; + } + m_imet_route[key].vni = vni; + return true; +} + +bool FdbSync::checkDelImet(string key, uint32_t vni) +{ + int ret = false; + + SWSS_LOG_INFO("Del IMET key:%s Vni:%d", key.c_str(), vni); + if (m_imet_route.find(key) != m_imet_route.end()) + { + ret = true; + m_imet_route.erase(key); + } + return ret; +} + +void FdbSync::imetAddRoute(struct in_addr vtep, string vlan_str, uint32_t vni) +{ + string vlan_id = "Vlan" + vlan_str; + string key = vlan_id + ":" + inet_ntoa(vtep); + + if (!checkImetExist(key, vni)) + { + return; + } + + SWSS_LOG_INFO("%sIMET Add route key:%s vtep:%s %s", + m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "", + key.c_str(), inet_ntoa(vtep), vlan_id.c_str()); + + std::vector fvVector; + FieldValueTuple f("vni", to_string(vni)); + fvVector.push_back(f); + + // If warmstart is in progress, we take all netlink changes into the cache map + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_VXLAN_REMOTE_VNI_TABLE_NAME, key, fvVector, false); + return; + } + + m_imetTable.set(key, fvVector); + return; +} + +void FdbSync::imetDelRoute(struct in_addr vtep, string vlan_str, uint32_t vni) +{ + string vlan_id = "Vlan" + vlan_str; + string key = vlan_id + ":" + inet_ntoa(vtep); + + if (!checkDelImet(key, vni)) + { + return; + } + + SWSS_LOG_INFO("%sIMET Del route key:%s vtep:%s %s", + m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "", + key.c_str(), inet_ntoa(vtep), vlan_id.c_str()); + + std::vector fvVector; + FieldValueTuple f("vni", to_string(vni)); + fvVector.push_back(f); + + // If warmstart is in progress, we take all netlink changes into the cache map + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_VXLAN_REMOTE_VNI_TABLE_NAME, key, fvVector, true); + return; + } + + m_imetTable.del(key); + return; +} + +void FdbSync::macDelVxlanDB(string key) +{ + string vtep = m_mac[key].vtep; + string type; + string vni = to_string(m_mac[key].vni); + type = m_mac[key].type; + + std::vector fvVector; + FieldValueTuple rv("remote_vtep", vtep); + FieldValueTuple t("type", type); + FieldValueTuple v("vni", vni); + fvVector.push_back(rv); + fvVector.push_back(t); + fvVector.push_back(v); + + SWSS_LOG_NOTICE("%sVXLAN_FDB_TABLE: DEL_KEY %s vtep:%s type:%s", + m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "" , + key.c_str(), vtep.c_str(), type.c_str()); + + // If warmstart is in progress, we take all netlink changes into the cache map + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_VXLAN_FDB_TABLE_NAME, key, fvVector, true); + return; + } + + m_fdbTable.del(key); + return; + +} + +void FdbSync::macAddVxlan(string key, struct in_addr vtep, string type, uint32_t vni, string intf_name) +{ + string svtep = inet_ntoa(vtep); + string svni = to_string(vni); + + /* Update the DB with Vxlan MAC */ + m_mac[key] = {svtep, type, vni, intf_name}; + + std::vector fvVector; + FieldValueTuple rv("remote_vtep", svtep); + FieldValueTuple t("type", type); + FieldValueTuple v("vni", svni); + fvVector.push_back(rv); + fvVector.push_back(t); + fvVector.push_back(v); + + SWSS_LOG_INFO("%sVXLAN_FDB_TABLE: ADD_KEY %s vtep:%s type:%s", + m_AppRestartAssist->isWarmStartInProgress() ? "WARM-RESTART:" : "" , + key.c_str(), svtep.c_str(), type.c_str()); + // If warmstart is in progress, we take all netlink changes into the cache map + if (m_AppRestartAssist->isWarmStartInProgress()) + { + m_AppRestartAssist->insertToMap(APP_VXLAN_FDB_TABLE_NAME, key, fvVector, false); + return; + } + + m_fdbTable.set(key, fvVector); + + return; +} + +void FdbSync::macDelVxlan(string key) +{ + if (m_mac.find(key) != m_mac.end()) + { + SWSS_LOG_INFO("DEL_KEY %s vtep:%s type:%s", key.c_str(), m_mac[key].vtep.c_str(), m_mac[key].type.c_str()); + macDelVxlanDB(key); + m_mac.erase(key); + } + return; +} + +void FdbSync::onMsgNbr(int nlmsg_type, struct nl_object *obj) +{ + char macStr[MAX_ADDR_SIZE + 1] = {0}; + struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj; + struct in_addr vtep = {0}; + int vlan = 0, ifindex = 0; + uint32_t vni = 0; + nl_addr *vtep_addr; + string ifname; + string key; + bool delete_key = false; + size_t str_loc = string::npos; + string type = ""; + string vlan_id = ""; + bool isVxlanIntf = false; + + if ((nlmsg_type != RTM_NEWNEIGH) && (nlmsg_type != RTM_GETNEIGH) && + (nlmsg_type != RTM_DELNEIGH)) + { + return; + } + + /* Only MAC route is to be supported */ + if (rtnl_neigh_get_family(neigh) != AF_BRIDGE) + { + return; + } + ifindex = rtnl_neigh_get_ifindex(neigh); + if (m_intf_info.find(ifindex) != m_intf_info.end()) + { + isVxlanIntf = true; + ifname = m_intf_info[ifindex].ifname; + } + + nl_addr2str(rtnl_neigh_get_lladdr(neigh), macStr, MAX_ADDR_SIZE); + + if (isVxlanIntf == false) + { + if (nlmsg_type == RTM_NEWNEIGH) + { + int vid = rtnl_neigh_get_vlan(neigh); + int state = rtnl_neigh_get_state(neigh); + if (state & NUD_PERMANENT) + { + updateMclagRemoteMacPort(ifindex, vid, macStr); + } + } + + if (nlmsg_type != RTM_DELNEIGH) + { + return; + } + } + else + { + /* If this is for vnet bridge vxlan interface, then return */ + if (ifname.find(VXLAN_BR_IF_NAME_PREFIX) != string::npos) + { + return; + } + + /* VxLan netdevice should be in - format */ + str_loc = ifname.rfind("-"); + if (str_loc == string::npos) + { + return; + } + + vlan_id = "Vlan" + ifname.substr(str_loc+1, std::string::npos); + vni = m_intf_info[ifindex].vni; + } + + + if (isVxlanIntf == false) + { + vlan = rtnl_neigh_get_vlan(neigh); + if (m_isEvpnNvoExist) + { + macRefreshStateDB(vlan, macStr); + } + return; + } + + vtep_addr = rtnl_neigh_get_dst(neigh); + if (vtep_addr == NULL) + { + return; + } + else + { + /* Currently we only support ipv4 tunnel endpoints */ + vtep.s_addr = *(uint32_t *)nl_addr_get_binary_addr(vtep_addr); + SWSS_LOG_INFO("Tunnel IP %s Int%d", inet_ntoa(vtep), *(uint32_t *)nl_addr_get_binary_addr(vtep_addr)); + } + + int state = rtnl_neigh_get_state(neigh); + if ((nlmsg_type == RTM_DELNEIGH) || (state == NUD_INCOMPLETE) || + (state == NUD_FAILED)) + { + delete_key = true; + } + + if (state & NUD_NOARP) + { + /* This is a static route */ + type = "static"; + } + else + { + type = "dynamic"; + } + + /* Handling IMET routes */ + if (MacAddress(macStr) == MacAddress("00:00:00:00:00:00")) + { + if (vtep.s_addr) + { + string vlan_str = ifname.substr(str_loc+1, string::npos); + + if (!delete_key) + { + imetAddRoute(vtep, vlan_str, vni); + } + else + { + imetDelRoute(vtep, vlan_str, vni); + } + } + return; + } + + key+= vlan_id; + key+= ":"; + key+= macStr; + + if (!delete_key) + { + macAddVxlan(key, vtep, type, vni, ifname); + } + else + { + macDelVxlan(key); + } + return; +} + +void FdbSync::onMsgLink(int nlmsg_type, struct nl_object *obj) +{ + struct rtnl_link *link; + char *ifname = NULL; + char *nil = "NULL"; + int ifindex; + unsigned int vni; + + link = (struct rtnl_link *)obj; + ifname = rtnl_link_get_name(link); + ifindex = rtnl_link_get_ifindex(link); + if (rtnl_link_is_vxlan(link) == 0) + { + return; + } + + if (rtnl_link_vxlan_get_id(link, &vni) != 0) + { + SWSS_LOG_INFO("Op:%d VxLAN dev:%s index:%d vni:%d. Not found", nlmsg_type, ifname? ifname: nil, ifindex, vni); + return; + } + SWSS_LOG_INFO("Op:%d VxLAN dev %s index:%d vni:%d", nlmsg_type, ifname? ifname: nil, ifindex, vni); + if (nlmsg_type == RTM_NEWLINK) + { + m_intf_info[ifindex].vni = vni; + m_intf_info[ifindex].ifname = ifname; + } + return; +} + +void FdbSync::onMsg(int nlmsg_type, struct nl_object *obj) +{ + if ((nlmsg_type != RTM_NEWLINK) && + (nlmsg_type != RTM_NEWNEIGH) && (nlmsg_type != RTM_DELNEIGH)) + { + SWSS_LOG_DEBUG("netlink: unhandled event: %d", nlmsg_type); + return; + } + if (nlmsg_type == RTM_NEWLINK) + { + onMsgLink(nlmsg_type, obj); + } + else + { + onMsgNbr(nlmsg_type, obj); + } +} + diff --git a/fdbsyncd/fdbsync.h b/fdbsyncd/fdbsync.h index ee6aa0845b..ab55b91a31 100644 --- a/fdbsyncd/fdbsync.h +++ b/fdbsyncd/fdbsync.h @@ -1,145 +1,161 @@ -#ifndef __FDBSYNC__ -#define __FDBSYNC__ - -#include -#include -#include "dbconnector.h" -#include "producerstatetable.h" -#include "subscriberstatetable.h" -#include "netmsg.h" -#include "warmRestartAssist.h" - -/* - * Default timer interval for fdbsyncd reconcillation - */ -#define DEFAULT_FDBSYNC_WARMSTART_TIMER 120 - -/* - * This is the MAX time in seconds, fdbsyncd will wait after warm-reboot - * for the interface entries to be recreated in kernel before attempting to - * write the FDB data to kernel - */ -#define INTF_RESTORE_MAX_WAIT_TIME 180 - -namespace swss { - -enum FDB_OP_TYPE { - FDB_OPER_ADD =1, - FDB_OPER_DEL = 2, -}; - -enum FDB_TYPE { - FDB_TYPE_STATIC = 1, - FDB_TYPE_DYNAMIC = 2, -}; - -struct m_fdb_info -{ - std::string mac; - std::string vid; /*Store as Vlan */ - std::string port_name; - short type; /*dynamic or static*/ - short op_type; /*add or del*/ -}; - -class FdbSync : public NetMsg -{ -public: - enum { MAX_ADDR_SIZE = 64 }; - - FdbSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *config_db); - ~FdbSync(); - - virtual void onMsg(int nlmsg_type, struct nl_object *obj); - - bool isIntfRestoreDone(); - - AppRestartAssist *getRestartAssist() - { - return m_AppRestartAssist; - } - - SubscriberStateTable *getFdbStateTable() - { - return &m_fdbStateTable; - } - - SubscriberStateTable *getCfgEvpnNvoTable() - { - return &m_cfgEvpnNvoTable; - } - - void processStateFdb(); - - void processCfgEvpnNvo(); - - bool m_reconcileDone = false; - - bool m_isEvpnNvoExist = false; - -private: - ProducerStateTable m_fdbTable; - ProducerStateTable m_imetTable; - SubscriberStateTable m_fdbStateTable; - AppRestartAssist *m_AppRestartAssist; - SubscriberStateTable m_cfgEvpnNvoTable; - - struct m_local_fdb_info - { - std::string port_name; - short type;/*dynamic or static*/ - }; - std::unordered_map m_fdb_mac; - - void macDelVxlanEntry(std::string auxkey, struct m_fdb_info *info); - - void macUpdateCache(struct m_fdb_info *info); - - bool macCheckSrcDB(struct m_fdb_info *info); - - void updateLocalMac(struct m_fdb_info *info); - - void updateAllLocalMac(); - - void macRefreshStateDB(int vlan, std::string kmac); - - bool checkImetExist(std::string key, uint32_t vni); - - bool checkDelImet(std::string key, uint32_t vni); - - struct m_mac_info - { - std::string vtep; - std::string type; - unsigned int vni; - std::string ifname; - }; - std::unordered_map m_mac; - - struct m_imet_info - { - unsigned int vni; - }; - std::unordered_map m_imet_route; - - struct intf - { - std::string ifname; - unsigned int vni; - }; - std::unordered_map m_intf_info; - - void addLocalMac(std::string key, std::string op); - void macAddVxlan(std::string key, struct in_addr vtep, std::string type, uint32_t vni, std::string intf_name); - void macDelVxlan(std::string auxkey); - void macDelVxlanDB(std::string key); - void imetAddRoute(struct in_addr vtep, std::string ifname, uint32_t vni); - void imetDelRoute(struct in_addr vtep, std::string ifname, uint32_t vni); - void onMsgNbr(int nlmsg_type, struct nl_object *obj); - void onMsgLink(int nlmsg_type, struct nl_object *obj); -}; - -} - -#endif - +#ifndef __FDBSYNC__ +#define __FDBSYNC__ + +#include +#include +#include "dbconnector.h" +#include "producerstatetable.h" +#include "subscriberstatetable.h" +#include "netmsg.h" +#include "warmRestartAssist.h" + +/* + * Default timer interval for fdbsyncd reconcillation + */ +#define DEFAULT_FDBSYNC_WARMSTART_TIMER 120 + +/* + * This is the MAX time in seconds, fdbsyncd will wait after warm-reboot + * for the interface entries to be recreated in kernel before attempting to + * write the FDB data to kernel + */ +#define INTF_RESTORE_MAX_WAIT_TIME 180 + +namespace swss { + +enum FDB_OP_TYPE { + FDB_OPER_ADD =1, + FDB_OPER_DEL = 2, +}; + +enum FDB_TYPE { + FDB_TYPE_STATIC = 1, + FDB_TYPE_DYNAMIC = 2, +}; + +struct m_fdb_info +{ + std::string mac; + std::string vid; /*Store as Vlan */ + std::string port_name; + short type; /*dynamic or static*/ + short op_type; /*add or del*/ +}; + +class FdbSync : public NetMsg +{ +public: + enum { MAX_ADDR_SIZE = 64 }; + + FdbSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *config_db); + ~FdbSync(); + + virtual void onMsg(int nlmsg_type, struct nl_object *obj); + + bool isIntfRestoreDone(); + + AppRestartAssist *getRestartAssist() + { + return m_AppRestartAssist; + } + + SubscriberStateTable *getFdbStateTable() + { + return &m_fdbStateTable; + } + + SubscriberStateTable *getMclagRemoteFdbStateTable() + { + return &m_mclagRemoteFdbStateTable; + } + + SubscriberStateTable *getCfgEvpnNvoTable() + { + return &m_cfgEvpnNvoTable; + } + + void processStateFdb(); + + void processStateMclagRemoteFdb(); + + void processCfgEvpnNvo(); + + bool m_reconcileDone = false; + + bool m_isEvpnNvoExist = false; + +private: + ProducerStateTable m_fdbTable; + ProducerStateTable m_imetTable; + SubscriberStateTable m_fdbStateTable; + SubscriberStateTable m_mclagRemoteFdbStateTable; + AppRestartAssist *m_AppRestartAssist; + SubscriberStateTable m_cfgEvpnNvoTable; + + struct m_local_fdb_info + { + std::string port_name; + short type;/*dynamic or static*/ + }; + std::unordered_map m_fdb_mac; + + std::unordered_map m_mclag_remote_fdb_mac; + + void macDelVxlanEntry(std::string auxkey, struct m_fdb_info *info); + + void macUpdateCache(struct m_fdb_info *info); + + bool macCheckSrcDB(struct m_fdb_info *info); + + void updateLocalMac(struct m_fdb_info *info); + + void updateAllLocalMac(); + + void macRefreshStateDB(int vlan, std::string kmac); + + void updateMclagRemoteMac(struct m_fdb_info *info); + + void updateMclagRemoteMacPort(int ifindex, int vlan, std::string mac); + + void macUpdateMclagRemoteCache(struct m_fdb_info *info); + + bool checkImetExist(std::string key, uint32_t vni); + + bool checkDelImet(std::string key, uint32_t vni); + + struct m_mac_info + { + std::string vtep; + std::string type; + unsigned int vni; + std::string ifname; + }; + std::unordered_map m_mac; + + struct m_imet_info + { + unsigned int vni; + }; + std::unordered_map m_imet_route; + + struct intf + { + std::string ifname; + unsigned int vni; + }; + std::unordered_map m_intf_info; + + void addLocalMac(std::string key, std::string op); + void macAddVxlan(std::string key, struct in_addr vtep, std::string type, uint32_t vni, std::string intf_name); + void macDelVxlan(std::string auxkey); + void macDelVxlanDB(std::string key); + void imetAddRoute(struct in_addr vtep, std::string ifname, uint32_t vni); + void imetDelRoute(struct in_addr vtep, std::string ifname, uint32_t vni); + void onMsgNbr(int nlmsg_type, struct nl_object *obj); + void onMsgLink(int nlmsg_type, struct nl_object *obj); +}; + +} + +#endif + diff --git a/fdbsyncd/fdbsyncd.cpp b/fdbsyncd/fdbsyncd.cpp index dac4bb85e5..a83b2693e1 100644 --- a/fdbsyncd/fdbsyncd.cpp +++ b/fdbsyncd/fdbsyncd.cpp @@ -1,156 +1,161 @@ -#include -#include -#include -#include -#include "logger.h" -#include "select.h" -#include "netdispatcher.h" -#include "netlink.h" -#include "fdbsyncd/fdbsync.h" -#include "warm_restart.h" - -using namespace std; -using namespace swss; - -int main(int argc, char **argv) -{ - Logger::linkToDbNative("fdbsyncd"); - - DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - RedisPipeline pipelineAppDB(&appDb); - DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector log_db(LOGLEVEL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector config_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - - FdbSync sync(&pipelineAppDB, &stateDb, &config_db); - - NetDispatcher::getInstance().registerMessageHandler(RTM_NEWNEIGH, &sync); - NetDispatcher::getInstance().registerMessageHandler(RTM_DELNEIGH, &sync); - NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, &sync); - - while (1) - { - try - { - NetLink netlink; - Selectable *temps; - int ret; - Select s; - SelectableTimer replayCheckTimer(timespec{0, 0}); - - using namespace std::chrono; - - /* - * If WarmStart is enabled, restore the VXLAN-FDB and VNI - * tables and start a reconcillation timer - */ - if (sync.getRestartAssist()->isWarmStartInProgress()) - { - sync.getRestartAssist()->readTablesToMap(); - - steady_clock::time_point starttime = steady_clock::now(); - while (!sync.isIntfRestoreDone()) - { - duration time_span = - duration_cast>(steady_clock::now() - starttime); - int pasttime = int(time_span.count()); - - if (pasttime > INTF_RESTORE_MAX_WAIT_TIME) - { - SWSS_LOG_INFO("timed-out before all interface data was replayed to kernel!!!"); - throw runtime_error("fdbsyncd: timedout on interface data replay"); - } - sleep(1); - } - replayCheckTimer.setInterval(timespec{1, 0}); - replayCheckTimer.start(); - s.addSelectable(&replayCheckTimer); - } - else - { - sync.getRestartAssist()->warmStartDisabled(); - sync.m_reconcileDone = true; - } - - netlink.registerGroup(RTNLGRP_LINK); - netlink.registerGroup(RTNLGRP_NEIGH); - SWSS_LOG_NOTICE("Listens to link and neigh messages..."); - netlink.dumpRequest(RTM_GETLINK); - s.addSelectable(&netlink); - ret = s.select(&temps, 1); - if (ret == Select::ERROR) - { - SWSS_LOG_ERROR("Error in RTM_GETLINK dump"); - } - - netlink.dumpRequest(RTM_GETNEIGH); - - s.addSelectable(sync.getFdbStateTable()); - s.addSelectable(sync.getCfgEvpnNvoTable()); - while (true) - { - s.select(&temps); - - if (temps == (Selectable *)sync.getFdbStateTable()) - { - sync.processStateFdb(); - } - else if (temps == (Selectable *)sync.getCfgEvpnNvoTable()) - { - sync.processCfgEvpnNvo(); - } - else if (temps == &replayCheckTimer) - { - if (sync.getFdbStateTable()->empty() && sync.getCfgEvpnNvoTable()->empty()) - { - sync.getRestartAssist()->appDataReplayed(); - SWSS_LOG_NOTICE("FDB Replay Complete"); - s.removeSelectable(&replayCheckTimer); - - /* Obtain warm-restart timer defined for routing application */ - uint32_t warmRestartIval = WarmStart::getWarmStartTimer("bgp","bgp"); - if (warmRestartIval) - { - sync.getRestartAssist()->setReconcileInterval(warmRestartIval); - } - //Else the interval is already set to default value - - //TODO: Optimise the reconcillation time using eoiu - issue#1657 - SWSS_LOG_NOTICE("Starting ReconcileTimer"); - sync.getRestartAssist()->startReconcileTimer(s); - } - else - { - replayCheckTimer.setInterval(timespec{1, 0}); - // re-start replay check timer - replayCheckTimer.start(); - } - } - else - { - /* - * If warmstart is in progress, we check the reconcile timer, - * if timer expired, we stop the timer and start the reconcile process - */ - if (sync.getRestartAssist()->isWarmStartInProgress()) - { - if (sync.getRestartAssist()->checkReconcileTimer(temps)) - { - sync.m_reconcileDone = true; - sync.getRestartAssist()->stopReconcileTimer(s); - sync.getRestartAssist()->reconcile(); - SWSS_LOG_NOTICE("VXLAN FDB VNI Reconcillation Complete"); - } - } - } - } - } - catch (const std::exception& e) - { - cout << "Exception \"" << e.what() << "\" had been thrown in daemon" << endl; - return 0; - } - } - - return 1; -} +#include +#include +#include +#include +#include "logger.h" +#include "select.h" +#include "netdispatcher.h" +#include "netlink.h" +#include "fdbsyncd/fdbsync.h" +#include "warm_restart.h" + +using namespace std; +using namespace swss; + +int main(int argc, char **argv) +{ + Logger::linkToDbNative("fdbsyncd"); + + DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + RedisPipeline pipelineAppDB(&appDb); + DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector log_db(LOGLEVEL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector config_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + + FdbSync sync(&pipelineAppDB, &stateDb, &config_db); + + NetDispatcher::getInstance().registerMessageHandler(RTM_NEWNEIGH, &sync); + NetDispatcher::getInstance().registerMessageHandler(RTM_DELNEIGH, &sync); + NetDispatcher::getInstance().registerMessageHandler(RTM_NEWLINK, &sync); + + while (1) + { + try + { + NetLink netlink; + Selectable *temps; + int ret; + Select s; + SelectableTimer replayCheckTimer(timespec{0, 0}); + + using namespace std::chrono; + + /* + * If WarmStart is enabled, restore the VXLAN-FDB and VNI + * tables and start a reconcillation timer + */ + if (sync.getRestartAssist()->isWarmStartInProgress()) + { + sync.getRestartAssist()->readTablesToMap(); + + steady_clock::time_point starttime = steady_clock::now(); + while (!sync.isIntfRestoreDone()) + { + duration time_span = + duration_cast>(steady_clock::now() - starttime); + int pasttime = int(time_span.count()); + + if (pasttime > INTF_RESTORE_MAX_WAIT_TIME) + { + SWSS_LOG_INFO("timed-out before all interface data was replayed to kernel!!!"); + throw runtime_error("fdbsyncd: timedout on interface data replay"); + } + sleep(1); + } + replayCheckTimer.setInterval(timespec{1, 0}); + replayCheckTimer.start(); + s.addSelectable(&replayCheckTimer); + } + else + { + sync.getRestartAssist()->warmStartDisabled(); + sync.m_reconcileDone = true; + } + + netlink.registerGroup(RTNLGRP_LINK); + netlink.registerGroup(RTNLGRP_NEIGH); + SWSS_LOG_NOTICE("Listens to link and neigh messages..."); + netlink.dumpRequest(RTM_GETLINK); + s.addSelectable(&netlink); + ret = s.select(&temps, 1); + if (ret == Select::ERROR) + { + SWSS_LOG_ERROR("Error in RTM_GETLINK dump"); + } + + netlink.dumpRequest(RTM_GETNEIGH); + + s.addSelectable(sync.getFdbStateTable()); + s.addSelectable(sync.getMclagRemoteFdbStateTable()); + s.addSelectable(sync.getCfgEvpnNvoTable()); + while (true) + { + s.select(&temps); + + if (temps == (Selectable *)sync.getFdbStateTable()) + { + sync.processStateFdb(); + } + else if (temps == (Selectable *)sync.getMclagRemoteFdbStateTable()) + { + sync.processStateMclagRemoteFdb(); + } + else if (temps == (Selectable *)sync.getCfgEvpnNvoTable()) + { + sync.processCfgEvpnNvo(); + } + else if (temps == &replayCheckTimer) + { + if (sync.getFdbStateTable()->empty() && sync.getCfgEvpnNvoTable()->empty()) + { + sync.getRestartAssist()->appDataReplayed(); + SWSS_LOG_NOTICE("FDB Replay Complete"); + s.removeSelectable(&replayCheckTimer); + + /* Obtain warm-restart timer defined for routing application */ + uint32_t warmRestartIval = WarmStart::getWarmStartTimer("bgp","bgp"); + if (warmRestartIval) + { + sync.getRestartAssist()->setReconcileInterval(warmRestartIval); + } + //Else the interval is already set to default value + + //TODO: Optimise the reconcillation time using eoiu - issue#1657 + SWSS_LOG_NOTICE("Starting ReconcileTimer"); + sync.getRestartAssist()->startReconcileTimer(s); + } + else + { + replayCheckTimer.setInterval(timespec{1, 0}); + // re-start replay check timer + replayCheckTimer.start(); + } + } + else + { + /* + * If warmstart is in progress, we check the reconcile timer, + * if timer expired, we stop the timer and start the reconcile process + */ + if (sync.getRestartAssist()->isWarmStartInProgress()) + { + if (sync.getRestartAssist()->checkReconcileTimer(temps)) + { + sync.m_reconcileDone = true; + sync.getRestartAssist()->stopReconcileTimer(s); + sync.getRestartAssist()->reconcile(); + SWSS_LOG_NOTICE("VXLAN FDB VNI Reconcillation Complete"); + } + } + } + } + } + catch (const std::exception& e) + { + cout << "Exception \"" << e.what() << "\" had been thrown in daemon" << endl; + return 0; + } + } + + return 1; +} diff --git a/gearsyncd/gearboxparser.cpp b/gearsyncd/gearboxparser.cpp index 863ee57dab..1ae8118266 100644 --- a/gearsyncd/gearboxparser.cpp +++ b/gearsyncd/gearboxparser.cpp @@ -143,6 +143,14 @@ bool GearboxParser::parse() val = phy["bus_id"]; attr = std::make_pair("bus_id", std::to_string(val.get())); attrs.push_back(attr); + if (phy.find("context_id") == phy.end()) + { + SWSS_LOG_ERROR("missing 'context_id' field in 'phys' item %d in gearbox configuration", iter); + return false; + } + val = phy["context_id"]; + attr = std::make_pair("context_id", std::to_string(val.get())); + attrs.push_back(attr); if (phy.find("hwinfo") == phy.end()) { SWSS_LOG_ERROR("missing 'hwinfo' field in 'phys' item %d in gearbox configuration", iter); diff --git a/lib/gearboxutils.cpp b/lib/gearboxutils.cpp index 989de4d22f..30c89ab494 100644 --- a/lib/gearboxutils.cpp +++ b/lib/gearboxutils.cpp @@ -185,6 +185,10 @@ std::map GearboxUtils::loadPhyMap(Table *gearboxTable) { phy.bus_id = std::stoi(val.second); } + else if (val.first == "context_id") + { + phy.context_id = std::stoi(val.second); + } } gearboxPhyMap[phy.phy_id] = phy; } diff --git a/lib/gearboxutils.h b/lib/gearboxutils.h index 0a70c62d77..b28b7b1080 100644 --- a/lib/gearboxutils.h +++ b/lib/gearboxutils.h @@ -44,6 +44,7 @@ typedef struct std::string hwinfo; uint32_t address; uint32_t bus_id; + uint32_t context_id; } gearbox_phy_t; typedef struct diff --git a/neighsyncd/neighsync.cpp b/neighsyncd/neighsync.cpp index 054f13a470..6b1abc235f 100644 --- a/neighsyncd/neighsync.cpp +++ b/neighsyncd/neighsync.cpp @@ -13,13 +13,17 @@ #include "neighsync.h" #include "warm_restart.h" +#include using namespace std; using namespace swss; -NeighSync::NeighSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb) : +NeighSync::NeighSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *cfgDb) : m_neighTable(pipelineAppDB, APP_NEIGH_TABLE_NAME), - m_stateNeighRestoreTable(stateDb, STATE_NEIGH_RESTORE_TABLE_NAME) + m_stateNeighRestoreTable(stateDb, STATE_NEIGH_RESTORE_TABLE_NAME), + m_cfgInterfaceTable(cfgDb, CFG_INTF_TABLE_NAME), + m_cfgLagInterfaceTable(cfgDb, CFG_LAG_INTF_TABLE_NAME), + m_cfgVlanInterfaceTable(cfgDb, CFG_VLAN_INTF_TABLE_NAME) { m_AppRestartAssist = new AppRestartAssist(pipelineAppDB, "neighsyncd", "swss", DEFAULT_NEIGHSYNC_WARMSTART_TIMER); if (m_AppRestartAssist) @@ -57,6 +61,7 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) struct rtnl_neigh *neigh = (struct rtnl_neigh *)obj; string key; string family; + string intfName; if ((nlmsg_type != RTM_NEWNEIGH) && (nlmsg_type != RTM_GETNEIGH) && (nlmsg_type != RTM_DELNEIGH)) @@ -70,12 +75,18 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) return; key+= LinkCache::getInstance().ifindexToName(rtnl_neigh_get_ifindex(neigh)); + intfName = key; key+= ":"; nl_addr2str(rtnl_neigh_get_dst(neigh), ipStr, MAX_ADDR_SIZE); - /* Ignore IPv6 link-local addresses as neighbors */ + /* Ignore IPv6 link-local addresses as neighbors, if ipv6 link local mode is disabled */ if (family == IPV6_NAME && IN6_IS_ADDR_LINKLOCAL(nl_addr_get_binary_addr(rtnl_neigh_get_dst(neigh)))) - return; + { + if (isLinkLocalEnabled(intfName) == false) + { + return; + } + } /* Ignore IPv6 multicast link-local addresses as neighbors */ if (family == IPV6_NAME && IN6_IS_ADDR_MC_LINKLOCAL(nl_addr_get_binary_addr(rtnl_neigh_get_dst(neigh)))) return; @@ -124,3 +135,52 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) m_neighTable.set(key, fvVector); } } + +/* To check the ipv6 link local is enabled on a given port */ +bool NeighSync::isLinkLocalEnabled(const string &port) +{ + vector values; + + if (!port.compare(0, strlen("Vlan"), "Vlan")) + { + if (!m_cfgVlanInterfaceTable.get(port, values)) + { + SWSS_LOG_INFO("IPv6 Link local is not enabled on %s", port.c_str()); + return false; + } + } + else if (!port.compare(0, strlen("PortChannel"), "PortChannel")) + { + if (!m_cfgLagInterfaceTable.get(port, values)) + { + SWSS_LOG_INFO("IPv6 Link local is not enabled on %s", port.c_str()); + return false; + } + } + else if (!port.compare(0, strlen("Ethernet"), "Ethernet")) + { + if (!m_cfgInterfaceTable.get(port, values)) + { + SWSS_LOG_INFO("IPv6 Link local is not enabled on %s", port.c_str()); + return false; + } + } + else + { + SWSS_LOG_INFO("IPv6 Link local is not supported for %s ", port.c_str()); + return false; + } + + auto it = std::find_if(values.begin(), values.end(), [](const FieldValueTuple& t){ return t.first == "ipv6_use_link_local_only";}); + if (it != values.end()) + { + if (it->second == "enable") + { + SWSS_LOG_INFO("IPv6 Link local is enabled on %s", port.c_str()); + return true; + } + } + + SWSS_LOG_INFO("IPv6 Link local is not enabled on %s", port.c_str()); + return false; +} diff --git a/neighsyncd/neighsync.h b/neighsyncd/neighsync.h index 387c849f30..49a17ee6b6 100644 --- a/neighsyncd/neighsync.h +++ b/neighsyncd/neighsync.h @@ -23,7 +23,7 @@ class NeighSync : public NetMsg public: enum { MAX_ADDR_SIZE = 64 }; - NeighSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb); + NeighSync(RedisPipeline *pipelineAppDB, DBConnector *stateDb, DBConnector *cfgDb); ~NeighSync(); virtual void onMsg(int nlmsg_type, struct nl_object *obj); @@ -39,6 +39,9 @@ class NeighSync : public NetMsg Table m_stateNeighRestoreTable; ProducerStateTable m_neighTable; AppRestartAssist *m_AppRestartAssist; + Table m_cfgVlanInterfaceTable, m_cfgLagInterfaceTable, m_cfgInterfaceTable; + + bool isLinkLocalEnabled(const std::string &port); }; } diff --git a/neighsyncd/neighsyncd.cpp b/neighsyncd/neighsyncd.cpp index 99e86b2ef9..a0882c28e2 100644 --- a/neighsyncd/neighsyncd.cpp +++ b/neighsyncd/neighsyncd.cpp @@ -18,8 +18,9 @@ int main(int argc, char **argv) DBConnector appDb("APPL_DB", 0); RedisPipeline pipelineAppDB(&appDb); DBConnector stateDb("STATE_DB", 0); + DBConnector cfgDb("CONFIG_DB", 0); - NeighSync sync(&pipelineAppDB, &stateDb); + NeighSync sync(&pipelineAppDB, &stateDb, &cfgDb); NetDispatcher::getInstance().registerMessageHandler(RTM_NEWNEIGH, &sync); NetDispatcher::getInstance().registerMessageHandler(RTM_DELNEIGH, &sync); diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 05abdfbcec..f4317134f6 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -64,6 +64,8 @@ orchagent_SOURCES = \ chassisorch.cpp \ debugcounterorch.cpp \ natorch.cpp \ + mlagorch.cpp \ + isolationgrouporch.cpp \ muxorch.cpp \ macsecorch.cpp \ lagid.cpp diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index 01b6a8e0bc..a2f5482a36 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -1225,13 +1225,6 @@ bool AclRuleMirror::create() SWSS_LOG_THROW("Failed to get mirror session state for session %s", m_sessionName.c_str()); } - // Increase session reference count regardless of state to deny - // attempt to remove mirror session with attached ACL rules. - if (!m_pMirrorOrch->increaseRefCount(m_sessionName)) - { - SWSS_LOG_THROW("Failed to increase mirror session reference count for session %s", m_sessionName.c_str()); - } - if (!state) { return true; @@ -1254,6 +1247,11 @@ bool AclRuleMirror::create() return false; } + if (!m_pMirrorOrch->increaseRefCount(m_sessionName)) + { + SWSS_LOG_THROW("Failed to increase mirror session reference count for session %s", m_sessionName.c_str()); + } + m_state = true; return true; @@ -2311,7 +2309,11 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr else { SWSS_LOG_ERROR("Failed to get ACL entry priority min/max values, rv:%d", status); - throw "AclOrch initialization failure"; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + throw "AclOrch initialization failure"; + } } queryAclActionCapability(); diff --git a/orchagent/copporch.cpp b/orchagent/copporch.cpp index 403fcb98d9..34d83dd274 100644 --- a/orchagent/copporch.cpp +++ b/orchagent/copporch.cpp @@ -179,7 +179,11 @@ void CoppOrch::initDefaultTrapGroup() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get default trap group, rv:%d", status); - throw "CoppOrch initialization failure"; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + throw "CoppOrch initialization failure"; + } } SWSS_LOG_INFO("Get default trap group"); diff --git a/orchagent/countercheckorch.cpp b/orchagent/countercheckorch.cpp index 54d6c1a51c..1c4735888f 100644 --- a/orchagent/countercheckorch.cpp +++ b/orchagent/countercheckorch.cpp @@ -164,7 +164,7 @@ PfcFrameCounters CounterCheckOrch::getPfcFrameCounters(sai_object_id_t portId) if (!m_countersTable->get(sai_serialize_object_id(portId), fieldValues)) { - return move(counters); + return counters; } for (const auto& fv : fieldValues) @@ -182,7 +182,7 @@ PfcFrameCounters CounterCheckOrch::getPfcFrameCounters(sai_object_id_t portId) } } - return move(counters); + return counters; } QueueMcCounters CounterCheckOrch::getQueueMcCounters( @@ -218,10 +218,9 @@ QueueMcCounters CounterCheckOrch::getQueueMcCounters( counters.push_back(pkts); } - return move(counters); + return counters; } - void CounterCheckOrch::addPort(const Port& port) { m_mcCountersMap.emplace(port.m_port_id, getQueueMcCounters(port)); diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index e0eb24239b..1e67a72087 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -488,7 +488,11 @@ void CrmOrch::getResAvailableCounters() break; } SWSS_LOG_ERROR("Failed to get switch attribute %u , rv:%d", attr.id, status); - break; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + break; + } } res.second.countersMap[CRM_COUNTERS_TABLE_KEY].availableCounter = attr.value.u32; @@ -517,7 +521,11 @@ void CrmOrch::getResAvailableCounters() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get switch attribute %u , rv:%d", attr.id, status); - break; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + break; + } } for (uint32_t i = 0; i < attr.value.aclresource.count; i++) @@ -557,6 +565,16 @@ void CrmOrch::getResAvailableCounters() sai_status_t status = sai_object_type_get_availability(gSwitchId, objType, 0, nullptr, &availCount); if (status != SAI_STATUS_SUCCESS) { + if ((status == SAI_STATUS_NOT_SUPPORTED) || + (status == SAI_STATUS_NOT_IMPLEMENTED) || + SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || + SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) + { + // mark unsupported resources + res.second.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; + SWSS_LOG_NOTICE("CRM Resource %s not supported", crmResTypeNameMap.at(res.first).c_str()); + break; + } SWSS_LOG_ERROR("Failed to get availability for object_type %u , rv:%d", objType, status); break; } @@ -577,6 +595,16 @@ void CrmOrch::getResAvailableCounters() sai_status_t status = sai_object_type_get_availability(gSwitchId, objType, 1, &attr, &availCount); if (status != SAI_STATUS_SUCCESS) { + if ((status == SAI_STATUS_NOT_SUPPORTED) || + (status == SAI_STATUS_NOT_IMPLEMENTED) || + SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || + SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) + { + // mark unsupported resources + res.second.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; + SWSS_LOG_NOTICE("CRM Resource %s not supported", crmResTypeNameMap.at(res.first).c_str()); + break; + } SWSS_LOG_ERROR("Failed to get availability for object_type %u , rv:%d", objType, status); break; } diff --git a/orchagent/debug_counter/drop_counter.cpp b/orchagent/debug_counter/drop_counter.cpp index db62574614..db80c894ab 100644 --- a/orchagent/debug_counter/drop_counter.cpp +++ b/orchagent/debug_counter/drop_counter.cpp @@ -342,6 +342,63 @@ unordered_set DropCounter::getSupportedDropReasons(sai_debug_counter_att return supported_drop_reasons; } +// Returns a set of supported counter types. +unordered_set DropCounter::getSupportedCounterTypes() +{ + sai_status_t status = SAI_STATUS_FAILURE; + + const auto& countersTypeLookup = getDebugCounterTypeLookup(); + unordered_set supportedCounterTypes; + + sai_s32_list_t enumValuesCapabilities; + vector saiCounterTypes; + + const auto* meta = sai_metadata_get_attr_metadata(SAI_OBJECT_TYPE_DEBUG_COUNTER, + SAI_DEBUG_COUNTER_ATTR_TYPE); + if (!meta) + { + SWSS_LOG_ERROR("SAI BUG: metadata null pointer returned by " + "sai_metadata_get_attr_metadata for SAI_DEBUG_COUNTER_ATTR_TYPE"); + return {}; + } + + if (!meta->isenum || !meta->enummetadata) + { + SWSS_LOG_ERROR("SAI BUG: SAI_DEBUG_COUNTER_ATTR_TYPE value type is not an enum"); + return {}; + } + + saiCounterTypes.assign(meta->enummetadata->valuescount, 0); + + enumValuesCapabilities.count = static_cast(saiCounterTypes.size()); + enumValuesCapabilities.list = saiCounterTypes.data(); + + status = sai_query_attribute_enum_values_capability(gSwitchId, + SAI_OBJECT_TYPE_DEBUG_COUNTER, + SAI_DEBUG_COUNTER_ATTR_TYPE, + &enumValuesCapabilities); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("This device does not support querying drop counters"); + return {}; + } + + for (uint32_t i = 0; i < enumValuesCapabilities.count; i++) + { + auto enumValue = static_cast(enumValuesCapabilities.list[i]); + for (const auto& it: countersTypeLookup) + { + if (it.second == enumValue) + { + supportedCounterTypes.emplace(it.first); + break; + } + } + } + + return supportedCounterTypes; +} + // serializeSupportedDropReasons takes a list of drop reasons and returns that // list as a string. // diff --git a/orchagent/debug_counter/drop_counter.h b/orchagent/debug_counter/drop_counter.h index bc548b34c7..7a151d139e 100644 --- a/orchagent/debug_counter/drop_counter.h +++ b/orchagent/debug_counter/drop_counter.h @@ -34,6 +34,7 @@ class DropCounter : public DebugCounter static std::unordered_set getSupportedDropReasons(sai_debug_counter_attr_t drop_reason_type); static std::string serializeSupportedDropReasons(std::unordered_set drop_reasons); + static std::unordered_set getSupportedCounterTypes(); static uint64_t getSupportedDebugCounterAmounts(sai_debug_counter_type_t counter_type); private: diff --git a/orchagent/debugcounterorch.cpp b/orchagent/debugcounterorch.cpp index ff2bee2e98..25d0b94589 100644 --- a/orchagent/debugcounterorch.cpp +++ b/orchagent/debugcounterorch.cpp @@ -183,6 +183,7 @@ void DebugCounterOrch::publishDropCounterCapabilities() { supported_ingress_drop_reasons = DropCounter::getSupportedDropReasons(SAI_DEBUG_COUNTER_ATTR_IN_DROP_REASON_LIST); supported_egress_drop_reasons = DropCounter::getSupportedDropReasons(SAI_DEBUG_COUNTER_ATTR_OUT_DROP_REASON_LIST); + supported_counter_types = DropCounter::getSupportedCounterTypes(); string ingress_drop_reason_str = DropCounter::serializeSupportedDropReasons(supported_ingress_drop_reasons); string egress_drop_reason_str = DropCounter::serializeSupportedDropReasons(supported_egress_drop_reasons); @@ -190,6 +191,12 @@ void DebugCounterOrch::publishDropCounterCapabilities() for (auto const &counter_type : DebugCounter::getDebugCounterTypeLookup()) { string drop_reasons; + + if (!supported_counter_types.count(counter_type.first)) + { + continue; + } + if (counter_type.first == PORT_INGRESS_DROPS || counter_type.first == SWITCH_INGRESS_DROPS) { drop_reasons = ingress_drop_reason_str; @@ -213,8 +220,6 @@ void DebugCounterOrch::publishDropCounterCapabilities() continue; } - supported_counter_types.emplace(counter_type.first); - vector fieldValues; fieldValues.push_back(FieldValueTuple("count", num_counters)); fieldValues.push_back(FieldValueTuple("reasons", drop_reasons)); diff --git a/orchagent/fabricportsorch.cpp b/orchagent/fabricportsorch.cpp index a4644dfffc..1adb84ec08 100644 --- a/orchagent/fabricportsorch.cpp +++ b/orchagent/fabricportsorch.cpp @@ -88,7 +88,11 @@ int FabricPortsOrch::getFabricPortList() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get fabric port number, rv:%d", status); - return FABRIC_PORT_ERROR; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + return FABRIC_PORT_ERROR; + } } m_fabricPortCount = attr.value.u32; SWSS_LOG_NOTICE("Get %d fabric ports", m_fabricPortCount); @@ -101,7 +105,11 @@ int FabricPortsOrch::getFabricPortList() status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - throw runtime_error("FabricPortsOrch get port list failure"); + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("FabricPortsOrch get port list failure"); + } } for (i = 0; i < m_fabricPortCount; i++) @@ -113,7 +121,11 @@ int FabricPortsOrch::getFabricPortList() status = sai_port_api->get_port_attribute(fabric_port_list[i], 1, &attr); if (status != SAI_STATUS_SUCCESS) { - throw runtime_error("FabricPortsOrch get port lane failure"); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("FabricPortsOrch get port lane failure"); + } } int lane = attr.value.u32list.list[0]; m_fabricLanePortMap[lane] = fabric_port_list[i]; @@ -198,7 +210,11 @@ void FabricPortsOrch::updateFabricPortState() { // Port may not be ready for query SWSS_LOG_ERROR("Failed to get fabric port (%d) status, rv:%d", lane, status); - return; + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + return; + } } if (m_portStatus.find(lane) != m_portStatus.end() && @@ -215,7 +231,11 @@ void FabricPortsOrch::updateFabricPortState() status = sai_port_api->get_port_attribute(port, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - throw runtime_error("FabricPortsOrch get remote id failure"); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("FabricPortsOrch get remote id failure"); + } } remote_peer = attr.value.u32; @@ -223,7 +243,11 @@ void FabricPortsOrch::updateFabricPortState() status = sai_port_api->get_port_attribute(port, 1, &attr); if (status != SAI_STATUS_SUCCESS) { - throw runtime_error("FabricPortsOrch get remote port index failure"); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("FabricPortsOrch get remote port index failure"); + } } remote_port = attr.value.u32; } diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index af46d958ec..b33ea76a43 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -11,6 +11,7 @@ #include "crmorch.h" #include "notifier.h" #include "sai_serialize.h" +#include "mlagorch.h" #include "vxlanorch.h" #include "directory.h" @@ -19,14 +20,17 @@ extern sai_fdb_api_t *sai_fdb_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; extern CrmOrch * gCrmOrch; +extern MlagOrch* gMlagOrch; extern Directory gDirectory; const int FdbOrch::fdborch_pri = 20; -FdbOrch::FdbOrch(DBConnector* applDbConnector, vector appFdbTables, TableConnector stateDbFdbConnector, PortsOrch *port) : +FdbOrch::FdbOrch(DBConnector* applDbConnector, vector appFdbTables, + TableConnector stateDbFdbConnector, TableConnector stateDbMclagFdbConnector, PortsOrch *port) : Orch(applDbConnector, appFdbTables), m_portsOrch(port), - m_fdbStateTable(stateDbFdbConnector.first, stateDbFdbConnector.second) + m_fdbStateTable(stateDbFdbConnector.first, stateDbFdbConnector.second), + m_mclagFdbStateTable(stateDbMclagFdbConnector.first, stateDbMclagFdbConnector.second) { for(auto it: appFdbTables) { @@ -61,6 +65,7 @@ bool FdbOrch::bake() return true; } + bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) { const FdbEntry& entry = update.entry; @@ -92,8 +97,11 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) */ if (port.m_bridge_port_id == it->second.bridge_port_id) { - SWSS_LOG_INFO("FdbOrch notification: mac %s is duplicate", entry.mac.to_string().c_str()); - return false; + if (it->second.origin != FDB_ORIGIN_MCLAG_ADVERTIZED) + { + SWSS_LOG_INFO("FdbOrch notification: mac %s is duplicate", entry.mac.to_string().c_str()); + return false; + } } mac_move = true; oldFdbData = it->second; @@ -112,6 +120,13 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) SWSS_LOG_INFO("m_entries size=%zu mac=%s port=0x%" PRIx64, m_entries.size(), entry.mac.to_string().c_str(), m_entries[entry].bridge_port_id); + if (mac_move && (oldFdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED)) + { + SWSS_LOG_NOTICE("fdbEvent: FdbOrch MCLAG remote to local move delete mac from state MCLAG remote fdb %s table:" + "bv_id 0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); + + m_mclagFdbStateTable.del(key); + } // Write to StateDb std::vector fvs; fvs.push_back(FieldValueTuple("port", portName)); @@ -140,7 +155,15 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) return false; } - if (oldFdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED) + if (oldFdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + SWSS_LOG_NOTICE("fdbEvent: FdbOrch MCLAG remote mac %s deleted, remove from state mclag remote fdb table:" + "bv_id 0x%" PRIx64, entry.mac.to_string().c_str(), entry.bv_id); + m_mclagFdbStateTable.del(key); + } + + if ((oldFdbData.origin == FDB_ORIGIN_LEARN) || + (oldFdbData.origin == FDB_ORIGIN_PROVISIONED)) { // Remove in StateDb for non advertised mac addresses m_fdbStateTable.del(key); @@ -205,10 +228,76 @@ void FdbOrch::update(sai_fdb_event_t type, auto existing_entry = m_entries.find(update.entry); if (existing_entry != m_entries.end()) { - SWSS_LOG_INFO("FdbOrch LEARN notification: mac %s is already in bv_id 0x%" + if (existing_entry->second.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + // If the bp is different MOVE the MAC entry. + if (existing_entry->second.bridge_port_id != bridge_port_id) + { + Port port; + SWSS_LOG_NOTICE("FdbOrch LEARN notification: mac %s is already in bv_id 0x%" PRIx64 "with different existing-bp 0x%" PRIx64 " new-bp:0x%" PRIx64, + update.entry.mac.to_string().c_str(), entry->bv_id, existing_entry->second.bridge_port_id, bridge_port_id); + if (!m_portsOrch->getPortByBridgePortId(existing_entry->second.bridge_port_id, port)) + { + SWSS_LOG_NOTICE("FdbOrch LEARN notification: Failed to get port by bridge port ID 0x%" PRIx64, existing_entry->second.bridge_port_id); + return; + } + else + { + port.m_fdb_count--; + m_portsOrch->setPort(port.m_alias, port); + vlan.m_fdb_count--; + m_portsOrch->setPort(vlan.m_alias, vlan); + } + // Continue to add (update/move) the MAC + } + else + { + SWSS_LOG_NOTICE("FdbOrch LEARN notification: mac %s is already in bv_id 0x%" PRIx64 "with same bp 0x%" PRIx64, + update.entry.mac.to_string().c_str(), entry->bv_id, existing_entry->second.bridge_port_id); + // Continue to move the MAC as local. + + // Existing MAC entry is on same VLAN, Port with Origin MCLAG(remote), its possible after the local learn MAC in + //the HW is updated to remote from FdbOrch, Update the MAC back to local in HW so that FdbOrch and HW is Sync and aging enabled. + sai_status_t status; + sai_fdb_entry_t fdb_entry; + fdb_entry.switch_id = gSwitchId; + memcpy(fdb_entry.mac_address, entry->mac_address, sizeof(sai_mac_t)); + fdb_entry.bv_id = entry->bv_id; + sai_attribute_t attr; + vector attrs; + + attr.id = SAI_FDB_ENTRY_ATTR_TYPE; + attr.value.s32 = SAI_FDB_ENTRY_TYPE_DYNAMIC; + attrs.push_back(attr); + + attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; + attr.value.oid = existing_entry->second.bridge_port_id; + attrs.push_back(attr); + + for(auto itr : attrs) + { + status = sai_fdb_api->set_fdb_entry_attribute(&fdb_entry, &itr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("macUpdate-Failed for MCLAG mac attr.id=0x%x for FDB %s in 0x%" PRIx64 "on %s, rv:%d", + itr.id, update.entry.mac.to_string().c_str(), entry->bv_id, update.port.m_alias.c_str(), status); + } + } + update.add = true; + update.type = "dynamic"; + storeFdbEntryState(update); + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + + return; + } + } + else + { + SWSS_LOG_INFO("FdbOrch LEARN notification: mac %s is already in bv_id 0x%" PRIx64 "existing-bp 0x%" PRIx64 "new-bp:0x%" PRIx64, update.entry.mac.to_string().c_str(), entry->bv_id, existing_entry->second.bridge_port_id, bridge_port_id); - break; + } + break; } update.add = true; @@ -269,7 +358,7 @@ void FdbOrch::update(sai_fdb_event_t type, fdbData.remote_ip = existing_entry->second.remote_ip; fdbData.esi = existing_entry->second.esi; fdbData.vni = existing_entry->second.vni; - saved_fdb_entries[update.port.m_alias].push_back( + saved_fdb_entries[update.port.m_alias].push_back( {existing_entry->first.mac, vlan.m_vlan_info.vlan_id, fdbData}); } else @@ -300,6 +389,47 @@ void FdbOrch::update(sai_fdb_event_t type, } } + // If MAC is MCLAG remote do not delete for age event, Add the MAC back.. + if (existing_entry->second.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + sai_status_t status; + sai_fdb_entry_t fdb_entry; + + fdb_entry.switch_id = gSwitchId; + memcpy(fdb_entry.mac_address, entry->mac_address, sizeof(sai_mac_t)); + fdb_entry.bv_id = entry->bv_id; + + sai_attribute_t attr; + vector attrs; + + attr.id = SAI_FDB_ENTRY_ATTR_TYPE; + attr.value.s32 = SAI_FDB_ENTRY_TYPE_STATIC; + attrs.push_back(attr); + + attr.id = SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE; + attr.value.booldata = true; + attrs.push_back(attr); + + attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; + attr.value.oid = existing_entry->second.bridge_port_id; + attrs.push_back(attr); + + SWSS_LOG_NOTICE("fdbEvent: MAC age event received, MAC is MCLAG origin, added back" + "to HW type %s FDB %s in %s on %s", + existing_entry->second.type.c_str(), + update.entry.mac.to_string().c_str(), vlan.m_alias.c_str(), + update.port.m_alias.c_str()); + + status = sai_fdb_api->create_fdb_entry(&fdb_entry, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create %s FDB %s in %s on %s, rv:%d", + existing_entry->second.type.c_str(), update.entry.mac.to_string().c_str(), + vlan.m_alias.c_str(), update.port.m_alias.c_str(), status); + } + return; + } + update.add = false; if (!update.port.m_alias.empty()) { @@ -401,10 +531,8 @@ void FdbOrch::update(sai_fdb_event_t type, storeFdbEntryState(update); - for (auto observer: m_observers) - { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); - } + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + } } else if (entry->bv_id == SAI_NULL_OBJECT_ID) @@ -424,11 +552,7 @@ void FdbOrch::update(sai_fdb_event_t type, update.add = false; storeFdbEntryState(update); - - for (auto observer: m_observers) - { - observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); - } + notify(SUBJECT_TYPE_FDB_CHANGE, &update); } itr = next_item; } @@ -503,7 +627,11 @@ bool FdbOrch::getPort(const MacAddress& mac, uint16_t vlan, Port& port) { SWSS_LOG_ERROR("Failed to get bridge port ID for FDB entry %s, rv:%d", mac.to_string().c_str(), status); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_FDB, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } if (!m_portsOrch->getPortByBridgePortId(attr.value.oid, port)) @@ -532,6 +660,10 @@ void FdbOrch::doTask(Consumer& consumer) origin = FDB_ORIGIN_VXLAN_ADVERTIZED; } + if (table_name == APP_MCLAG_FDB_TABLE_NAME) + { + origin = FDB_ORIGIN_MCLAG_ADVERTIZED; + } auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) @@ -629,18 +761,34 @@ void FdbOrch::doTask(Consumer& consumer) } /* FDB type is either dynamic or static */ - assert(type == "dynamic" || type == "static"); + assert(type == "dynamic" || type == "dynamic_local" || type == "static" ); + bool check_vlan_member = true; if(origin == FDB_ORIGIN_VXLAN_ADVERTIZED) { VxlanTunnelOrch* tunnel_orch = gDirectory.get(); - if(!remote_ip.length()) + if (tunnel_orch->dipTunnelsUsed()) { - it = consumer.m_toSync.erase(it); - continue; + if(!remote_ip.length()) + { + it = consumer.m_toSync.erase(it); + continue; + } + port = tunnel_orch->getTunnelPortName(remote_ip); + } + else + { + EvpnNvoOrch* evpn_nvo_orch = gDirectory.get(); + VxlanTunnel* sip_tunnel = evpn_nvo_orch->getEVPNVtep(); + check_vlan_member = false; + if (sip_tunnel == NULL) + { + it = consumer.m_toSync.erase(it); + continue; + } + port = tunnel_orch->getTunnelPortName(sip_tunnel->getSrcIP().to_string(), true); } - port = tunnel_orch->getTunnelPortName(remote_ip); } @@ -651,15 +799,46 @@ void FdbOrch::doTask(Consumer& consumer) fdbData.remote_ip = remote_ip; fdbData.esi = esi; fdbData.vni = vni; - if (addFdbEntry(entry, port, fdbData)) - it = consumer.m_toSync.erase(it); + if (addFdbEntry(entry, port, fdbData, check_vlan_member)) + { + if (origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); + if (type == "dynamic_local") + { + m_mclagFdbStateTable.del(key); + } + } + + if(origin == FDB_ORIGIN_VXLAN_ADVERTIZED) + { + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + + if(!remote_ip.length()) + { + it = consumer.m_toSync.erase(it); + continue; + } + port = tunnel_orch->getTunnelPortName(remote_ip); + } + } else it++; } else if (op == DEL_COMMAND) { if (removeFdbEntry(entry, origin)) + { + if (origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); + m_mclagFdbStateTable.del(key); + SWSS_LOG_NOTICE("fdbEvent: do Task Delete MCLAG FDB from state mclag remote fdb table: " + "Mac: %s Vlan: %d ",entry.mac.to_string().c_str(), vlan.m_vlan_info.vlan_id ); + } + it = consumer.m_toSync.erase(it); + } else it++; @@ -888,10 +1067,7 @@ void FdbOrch::notifyObserversFDBFlush(Port &port, sai_object_id_t& bvid) if (!flushUpdate.entries.empty()) { - for (auto observer: m_observers) - { - observer->update(SUBJECT_TYPE_FDB_FLUSH_CHANGE, &flushUpdate); - } + notify(SUBJECT_TYPE_FDB_FLUSH_CHANGE, &flushUpdate); } } @@ -959,15 +1135,15 @@ void FdbOrch::updateVlanMember(const VlanMemberUpdate& update) } bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, - FdbData fdbData) + FdbData fdbData, bool check_vlan_member) { Port vlan; Port port; SWSS_LOG_ENTER(); - SWSS_LOG_INFO("mac=%s bv_id=0x%" PRIx64 " port_name=%s type=%s origin=%d", + SWSS_LOG_INFO("mac=%s bv_id=0x%" PRIx64 " port_name=%s type=%s origin=%d remote_ip=%s", entry.mac.to_string().c_str(), entry.bv_id, port_name.c_str(), - fdbData.type.c_str(), fdbData.origin); + fdbData.type.c_str(), fdbData.origin, fdbData.remote_ip.c_str()); if (!m_portsOrch->getPort(entry.bv_id, vlan)) { @@ -985,7 +1161,7 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, } /* Retry until port is member of vlan*/ - if (vlan.m_members.find(port_name) == vlan.m_members.end()) + if (check_vlan_member && vlan.m_members.find(port_name) == vlan.m_members.end()) { SWSS_LOG_INFO("Saving a fdb entry until port %s becomes vlan %s member", port_name.c_str(), vlan.m_alias.c_str()); saved_fdb_entries[port_name].push_back({entry.mac, @@ -1001,14 +1177,17 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, Port oldPort; string oldType; + string oldRemoteIp; FdbOrigin oldOrigin = FDB_ORIGIN_INVALID ; bool macUpdate = false; + auto it = m_entries.find(entry); if (it != m_entries.end()) { /* get existing port and type */ oldType = it->second.type; oldOrigin = it->second.origin; + oldRemoteIp = it->second.remote_ip; if (!m_portsOrch->getPortByBridgePortId(it->second.bridge_port_id, oldPort)) { @@ -1016,12 +1195,13 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, return false; } - if ((oldOrigin == fdbData.origin) && (oldType == fdbData.type) && (port.m_bridge_port_id == it->second.bridge_port_id)) + if ((oldOrigin == fdbData.origin) && (oldType == fdbData.type) && (port.m_bridge_port_id == it->second.bridge_port_id) + && (oldRemoteIp == fdbData.remote_ip)) { /* Duplicate Mac */ - SWSS_LOG_INFO("FdbOrch: mac=%s %s port=%s type=%s origin=%d is duplicate", entry.mac.to_string().c_str(), + SWSS_LOG_INFO("FdbOrch: mac=%s %s port=%s type=%s origin=%d remote_ip=%s is duplicate", entry.mac.to_string().c_str(), vlan.m_alias.c_str(), port_name.c_str(), - fdbData.type.c_str(), fdbData.origin); + fdbData.type.c_str(), fdbData.origin, fdbData.remote_ip.c_str()); return true; } else if (fdbData.origin != oldOrigin) @@ -1061,6 +1241,20 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, it->second.remote_ip.c_str()); } } + else if ((oldOrigin == FDB_ORIGIN_LEARN) && (fdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED)) + { + if ((port.m_bridge_port_id == it->second.bridge_port_id) && (oldType == "dynamic") && (fdbData.type == "dynamic_local")) + { + SWSS_LOG_INFO("FdbOrch: mac=%s %s port=%s type=%s origin=%d old_origin=%d" + " old_type=%s local mac exists," + " received dynamic_local from iccpd, ignore update", + entry.mac.to_string().c_str(), vlan.m_alias.c_str(), port_name.c_str(), + fdbData.type.c_str(), fdbData.origin, oldOrigin, oldType.c_str()); + + return true; + } + } + } else /* (fdbData.origin == oldOrigin) */ { @@ -1081,15 +1275,21 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, attr.id = SAI_FDB_ENTRY_ATTR_TYPE; if (fdbData.origin == FDB_ORIGIN_VXLAN_ADVERTIZED) { - attr.value.s32 = SAI_FDB_ENTRY_TYPE_STATIC; + attr.value.s32 = SAI_FDB_ENTRY_TYPE_STATIC; + } + else if (fdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + { + attr.value.s32 = (fdbData.type == "dynamic_local") ? SAI_FDB_ENTRY_TYPE_DYNAMIC : SAI_FDB_ENTRY_TYPE_STATIC; } else { attr.value.s32 = (fdbData.type == "dynamic") ? SAI_FDB_ENTRY_TYPE_DYNAMIC : SAI_FDB_ENTRY_TYPE_STATIC; } + attrs.push_back(attr); - if ((fdbData.origin == FDB_ORIGIN_VXLAN_ADVERTIZED) && (fdbData.type == "dynamic")) + if ((fdbData.origin == FDB_ORIGIN_VXLAN_ADVERTIZED) || (fdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) + || (fdbData.type == "dynamic")) { attr.id = SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE; attr.value.booldata = true; @@ -1144,7 +1344,6 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, } } - if (macUpdate) { SWSS_LOG_INFO("MAC-Update FDB %s in %s on from-%s:to-%s from-%s:to-%s origin-%d-to-%d", @@ -1197,12 +1396,21 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, FdbData storeFdbData = fdbData; storeFdbData.bridge_port_id = port.m_bridge_port_id; + // overwrite the type and origin + if ((fdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) && (fdbData.type == "dynamic_local")) + { + //If the MAC is dynamic_local change the origin accordingly + //MAC is added/updated as dynamic to allow aging. + storeFdbData.origin = FDB_ORIGIN_LEARN; + storeFdbData.type = "dynamic"; + } m_entries[entry] = storeFdbData; string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); - if (fdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED) + if ((fdbData.origin != FDB_ORIGIN_MCLAG_ADVERTIZED) && + (fdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED)) { /* State-DB is updated only for Local Mac addresses */ // Write to StateDb @@ -1214,7 +1422,9 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, fvs.push_back(FieldValueTuple("type", fdbData.type)); m_fdbStateTable.set(key, fvs); } - else if (macUpdate && (oldOrigin != FDB_ORIGIN_VXLAN_ADVERTIZED)) + + else if (macUpdate && (oldOrigin != FDB_ORIGIN_MCLAG_ADVERTIZED) && + (oldOrigin != FDB_ORIGIN_VXLAN_ADVERTIZED)) { /* origin is FDB_ORIGIN_ADVERTIZED and it is mac-update * so delete from StateDb since we only keep local fdbs @@ -1223,6 +1433,26 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, m_fdbStateTable.del(key); } + if ((fdbData.origin == FDB_ORIGIN_MCLAG_ADVERTIZED) && (fdbData.type != "dynamic_local")) + { + std::vector fvs; + fvs.push_back(FieldValueTuple("port", port_name)); + fvs.push_back(FieldValueTuple("type", fdbData.type)); + m_mclagFdbStateTable.set(key, fvs); + + SWSS_LOG_NOTICE("fdbEvent: AddFdbEntry: Add MCLAG MAC with state mclag remote fdb table " + "Mac: %s Vlan: %d port:%s type:%s", entry.mac.to_string().c_str(), + vlan.m_vlan_info.vlan_id, port_name.c_str(), fdbData.type.c_str()); + } + else if (macUpdate && (oldOrigin == FDB_ORIGIN_MCLAG_ADVERTIZED) && + (fdbData.origin != FDB_ORIGIN_MCLAG_ADVERTIZED)) + { + SWSS_LOG_NOTICE("fdbEvent: AddFdbEntry: del MCLAG MAC from state MCLAG remote fdb table " + "Mac: %s Vlan: %d port:%s type:%s", entry.mac.to_string().c_str(), + vlan.m_vlan_info.vlan_id, port_name.c_str(), fdbData.type.c_str()); + m_mclagFdbStateTable.del(key); + } + if (!macUpdate) { gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); @@ -1273,20 +1503,32 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry, FdbOrigin origin) if (fdbData.origin != origin) { - /* When mac is moved from remote to local - * BGP will delete the mac from vxlan_fdb_table - * but we should not delete this mac here since now - * mac in orchagent represents locally learnt - */ - SWSS_LOG_INFO("FdbOrch RemoveFDBEntry: mac=%s fdb origin is different; found_origin:%d delete_origin:%d", - entry.mac.to_string().c_str(), fdbData.origin, origin); + if ((origin == FDB_ORIGIN_MCLAG_ADVERTIZED) && (fdbData.origin == FDB_ORIGIN_LEARN) && + (port.m_oper_status == SAI_PORT_OPER_STATUS_DOWN) && (gMlagOrch->isMlagInterface(port.m_alias))) + { + //check if the local MCLAG port is down, if yes then continue delete the local MAC + origin = FDB_ORIGIN_LEARN; + SWSS_LOG_INFO("FdbOrch RemoveFDBEntry: mac=%s fdb del origin is MCLAG; delete local mac as port %s is down", + entry.mac.to_string().c_str(), port.m_alias.c_str()); + } + else + { - /* We may still have the mac in saved-fdb probably due to unavailability - * of bridge-port. check whether the entry is in the saved fdb, - * if so delete it from there. */ - deleteFdbEntryFromSavedFDB(entry.mac, vlan.m_vlan_info.vlan_id, origin); + /* When mac is moved from remote to local + * BGP will delete the mac from vxlan_fdb_table + * but we should not delete this mac here since now + * mac in orchagent represents locally learnt + */ + SWSS_LOG_INFO("FdbOrch RemoveFDBEntry: mac=%s fdb origin is different; found_origin:%d delete_origin:%d", + entry.mac.to_string().c_str(), fdbData.origin, origin); - return true; + /* We may still have the mac in saved-fdb probably due to unavailability + * of bridge-port. check whether the entry is in the saved fdb, + * if so delete it from there. */ + deleteFdbEntryFromSavedFDB(entry.mac, vlan.m_vlan_info.vlan_id, origin); + + return true; + } } string key = "Vlan" + to_string(vlan.m_vlan_info.vlan_id) + ":" + entry.mac.to_string(); @@ -1319,7 +1561,7 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry, FdbOrigin origin) (void)m_entries.erase(entry); // Remove in StateDb - if (fdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED) + if ((fdbData.origin != FDB_ORIGIN_VXLAN_ADVERTIZED) && (fdbData.origin != FDB_ORIGIN_MCLAG_ADVERTIZED)) { m_fdbStateTable.del(key); } diff --git a/orchagent/fdborch.h b/orchagent/fdborch.h index 201493f574..ac37b035f2 100644 --- a/orchagent/fdborch.h +++ b/orchagent/fdborch.h @@ -10,7 +10,8 @@ enum FdbOrigin FDB_ORIGIN_INVALID = 0, FDB_ORIGIN_LEARN = 1, FDB_ORIGIN_PROVISIONED = 2, - FDB_ORIGIN_VXLAN_ADVERTIZED = 4 + FDB_ORIGIN_VXLAN_ADVERTIZED = 4, + FDB_ORIGIN_MCLAG_ADVERTIZED = 8 }; struct FdbEntry @@ -80,7 +81,8 @@ class FdbOrch: public Orch, public Subject, public Observer { public: - FdbOrch(DBConnector* applDbConnector, vector appFdbTables, TableConnector stateDbFdbConnector, PortsOrch *port); + FdbOrch(DBConnector* applDbConnector, vector appFdbTables, + TableConnector stateDbFdbConnector, TableConnector stateDbMclagFdbConnector, PortsOrch *port); ~FdbOrch() { @@ -105,6 +107,7 @@ class FdbOrch: public Orch, public Subject, public Observer fdb_entries_by_port_t saved_fdb_entries; vector m_appTables; Table m_fdbStateTable; + Table m_mclagFdbStateTable; NotificationConsumer* m_flushNotificationsConsumer; NotificationConsumer* m_fdbNotificationConsumer; @@ -114,7 +117,7 @@ class FdbOrch: public Orch, public Subject, public Observer void updateVlanMember(const VlanMemberUpdate&); void updatePortOperState(const PortOperStateUpdate&); - bool addFdbEntry(const FdbEntry&, const string&, FdbData fdbData); + bool addFdbEntry(const FdbEntry&, const string&, FdbData fdbData, bool check_vlan_member=true); void deleteFdbEntryFromSavedFDB(const MacAddress &mac, const unsigned short &vlanId, FdbOrigin origin, const string portName=""); bool storeFdbEntryState(const FdbUpdate& update); diff --git a/orchagent/fgnhgorch.cpp b/orchagent/fgnhgorch.cpp index de791678ac..4111665e09 100644 --- a/orchagent/fgnhgorch.cpp +++ b/orchagent/fgnhgorch.cpp @@ -294,11 +294,15 @@ bool FgNhgOrch::createFineGrainedNextHopGroup(FGNextHopGroupEntry &syncd_fg_rout { SWSS_LOG_ERROR("Failed to query next hop group %s SAI_NEXT_HOP_GROUP_ATTR_REAL_SIZE, rv:%d", nextHops.to_string().c_str(), status); - if (!removeFineGrainedNextHopGroup(&syncd_fg_route_entry)) + task_process_status handle_status = handleSaiGetStatus(SAI_API_NEXT_HOP_GROUP, status); + if (handle_status != task_process_status::task_success) { - SWSS_LOG_ERROR("Failed to clean-up after next hop group real_size query failure"); + if (!removeFineGrainedNextHopGroup(&syncd_fg_route_entry)) + { + SWSS_LOG_ERROR("Failed to clean-up after next hop group real_size query failure"); + } + return false; } - return false; } fgNhgEntry->real_bucket_size = nhg_attr.value.u32; } diff --git a/orchagent/isolationgrouporch.cpp b/orchagent/isolationgrouporch.cpp new file mode 100644 index 0000000000..0138a6ab95 --- /dev/null +++ b/orchagent/isolationgrouporch.cpp @@ -0,0 +1,749 @@ +/* + * Copyright 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. + * and/or its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "isolationgrouporch.h" +#include "converter.h" +#include "tokenize.h" +#include "portsorch.h" + +extern sai_object_id_t gSwitchId; +extern PortsOrch *gPortsOrch; +extern sai_isolation_group_api_t* sai_isolation_group_api; +extern sai_bridge_api_t *sai_bridge_api; +extern sai_port_api_t *sai_port_api; +extern IsoGrpOrch *gIsoGrpOrch; + +IsoGrpOrch::IsoGrpOrch(vector &connectors) : Orch(connectors) +{ + SWSS_LOG_ENTER(); + gPortsOrch->attach(this); +} + +IsoGrpOrch::~IsoGrpOrch() +{ + SWSS_LOG_ENTER(); +} + +shared_ptr +IsoGrpOrch::getIsolationGroup(string name) +{ + SWSS_LOG_ENTER(); + + shared_ptr ret = nullptr; + + auto grp = m_isolationGrps.find(name); + if (grp != m_isolationGrps.end()) + { + ret = grp->second; + } + + return ret; +} + +void +IsoGrpOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (!gPortsOrch->allPortsReady()) + { + return; + } + + string table_name = consumer.getTableName(); + if (table_name == APP_ISOLATION_GROUP_TABLE_NAME) + { + doIsoGrpTblTask(consumer); + } + else + { + SWSS_LOG_ERROR("Invalid table %s", table_name.c_str()); + } +} + +void +IsoGrpOrch::doIsoGrpTblTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + isolation_group_status_t status = ISO_GRP_STATUS_SUCCESS; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string op = kfvOp(t); + string key = kfvKey(t); + + size_t sep_loc = key.find(consumer.getConsumerTable()->getTableNameSeparator().c_str()); + string name = key.substr(0, sep_loc); + + SWSS_LOG_DEBUG("Op:%s IsoGrp:%s", op.c_str(), name.c_str()); + + if (op == SET_COMMAND) + { + isolation_group_type_t type = ISOLATION_GROUP_TYPE_INVALID; + string descr(""); + string bind_ports(""); + string mem_ports(""); + + for (auto itp : kfvFieldsValues(t)) + { + string attr_name = to_upper(fvField(itp)); + string attr_value = fvValue(itp); + + if (attr_name == ISOLATION_GRP_DESCRIPTION) + { + descr = attr_value; + } + else if (attr_name == ISOLATION_GRP_TYPE) + { + if (ISOLATION_GRP_TYPE_PORT == attr_value) + { + type = ISOLATION_GROUP_TYPE_PORT; + } + else if (ISOLATION_GRP_TYPE_BRIDGE_PORT == attr_value) + { + type = ISOLATION_GROUP_TYPE_BRIDGE_PORT; + } + else + SWSS_LOG_WARN("Attr:%s unknown type:%d", attr_name.c_str(), type); + } + else if (attr_name == ISOLATION_GRP_PORTS) + { + bind_ports = attr_value; + } + else if (attr_name == ISOLATION_GRP_MEMBERS) + { + mem_ports = attr_value; + } + else + SWSS_LOG_WARN("unknown Attr:%s ", attr_name.c_str()); + } + + status = addIsolationGroup(name, type, descr, bind_ports, mem_ports); + if (ISO_GRP_STATUS_SUCCESS == status) + { + auto grp = getIsolationGroup(name); + IsolationGroupUpdate update = {grp.get(), true}; + grp->notifyObservers(SUBJECT_TYPE_ISOLATION_GROUP_CHANGE, &update); + + grp->attach(this); + } + } + else + { + auto grp = getIsolationGroup(name); + if (grp) + { + grp->detach(this); + + /* Send a notification and see if observers want to detach */ + IsolationGroupUpdate update = {grp.get(), false}; + grp->notifyObservers(SUBJECT_TYPE_ISOLATION_GROUP_CHANGE, &update); + + // Finally delete it if it + status = delIsolationGroup(name); + } + } + + if (status != ISO_GRP_STATUS_RETRY) + { + it = consumer.m_toSync.erase(it); + } + else + { + it++; + } + } +} + +isolation_group_status_t +IsoGrpOrch::addIsolationGroup(string name, isolation_group_type_t type, string descr, string bindPorts, string memPorts) +{ + SWSS_LOG_ENTER(); + + isolation_group_status_t status = ISO_GRP_STATUS_SUCCESS; + + // Add Or Update + auto grp = getIsolationGroup(name); + if (!grp) + { + // Add Case + auto grp = make_shared(name, type, descr); + + status = grp->create(); + if (ISO_GRP_STATUS_SUCCESS != status) + { + return status; + } + grp->setMembers(memPorts); + grp->setBindPorts(bindPorts); + this->m_isolationGrps[name] = grp; + } + else if (grp->getType() == type) + { + grp->m_description = descr; + grp->setMembers(memPorts); + grp->setBindPorts(bindPorts); + } + else + { + SWSS_LOG_ERROR("Isolation group type update to %d not permitted", type); + status = ISO_GRP_STATUS_FAIL; + } + + return status; +} + +isolation_group_status_t +IsoGrpOrch::delIsolationGroup(string name) +{ + SWSS_LOG_ENTER(); + + auto grp = m_isolationGrps.find(name); + if (grp != m_isolationGrps.end()) + { + grp->second->destroy(); + m_isolationGrps.erase(name); + } + + return ISO_GRP_STATUS_SUCCESS; +} + + +void +IsoGrpOrch::update(SubjectType type, void *cntx) +{ + SWSS_LOG_ENTER(); + + if (type != SUBJECT_TYPE_BRIDGE_PORT_CHANGE) + { + return; + } + + for (auto kv : m_isolationGrps) + { + kv.second->update(type, cntx); + } +} + + +isolation_group_status_t +IsolationGroup::create() +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + + attr.id = SAI_ISOLATION_GROUP_ATTR_TYPE; + if (ISOLATION_GROUP_TYPE_BRIDGE_PORT == m_type) + { + attr.value.s32 = SAI_ISOLATION_GROUP_TYPE_BRIDGE_PORT; + } + else + { + attr.value.s32 = SAI_ISOLATION_GROUP_TYPE_PORT; + } + + sai_status_t status = sai_isolation_group_api->create_isolation_group(&m_oid, gSwitchId, 1, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Error %d creating isolation group %s", status, m_name.c_str()); + return ISO_GRP_STATUS_FAIL; + } + else + { + SWSS_LOG_NOTICE("Isolation group %s has oid 0x%" PRIx64 , m_name.c_str(), m_oid); + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::destroy() +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + + // Remove all bindings + attr.value.oid = SAI_NULL_OBJECT_ID; + for (auto p : m_bind_ports) + { + Port port; + gPortsOrch->getPort(p, port); + if (ISOLATION_GROUP_TYPE_BRIDGE_PORT == m_type) + { + attr.id = SAI_BRIDGE_PORT_ATTR_ISOLATION_GROUP; + if (SAI_STATUS_SUCCESS != sai_bridge_api->set_bridge_port_attribute(port.m_bridge_port_id, &attr)) + { + SWSS_LOG_ERROR("Unable to del SAI_BRIDGE_PORT_ATTR_ISOLATION_GROUP from %s", p.c_str()); + } + else + { + SWSS_LOG_NOTICE("SAI_BRIDGE_PORT_ATTR_ISOLATION_GROUP removed from %s", p.c_str()); + } + } + else if (ISOLATION_GROUP_TYPE_PORT == m_type) + { + attr.id = SAI_PORT_ATTR_ISOLATION_GROUP; + if (SAI_STATUS_SUCCESS != sai_port_api->set_port_attribute( + (port.m_type == Port::PHY ? port.m_port_id : port.m_lag_id), + &attr)) + { + SWSS_LOG_ERROR("Unable to del SAI_PORT_ATTR_ISOLATION_GROUP from %s", p.c_str()); + } + else + { + SWSS_LOG_NOTICE("SAI_PORT_ATTR_ISOLATION_GROUP removed from %s", p.c_str()); + } + } + } + m_bind_ports.clear(); + m_pending_bind_ports.clear(); + + // Remove all members + for (auto &kv : m_members) + { + if (SAI_STATUS_SUCCESS != sai_isolation_group_api->remove_isolation_group_member(kv.second)) + { + SWSS_LOG_ERROR("Unable to delete isolation group member 0x%" PRIx64 " from %s: 0x%" PRIx64 " for port %s", + kv.second, + m_name.c_str(), + m_oid, + kv.first.c_str()); + } + else + { + SWSS_LOG_NOTICE("Isolation group member 0x%" PRIx64 " deleted from %s: 0x%" PRIx64 " for port %s", + kv.second, + m_name.c_str(), + m_oid, + kv.first.c_str()); + } + } + m_members.clear(); + + sai_status_t status = sai_isolation_group_api->remove_isolation_group(m_oid); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to delete isolation group %s with oid 0x%" PRIx64 , m_name.c_str(), m_oid); + } + else + { + SWSS_LOG_NOTICE("Isolation group %s with oid 0x%" PRIx64 " deleted", m_name.c_str(), m_oid); + } + m_oid = SAI_NULL_OBJECT_ID; + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::addMember(Port &port) +{ + SWSS_LOG_ENTER(); + sai_object_id_t port_id = SAI_NULL_OBJECT_ID; + + if (m_type == ISOLATION_GROUP_TYPE_BRIDGE_PORT) + { + port_id = port.m_bridge_port_id; + } + else if (m_type == ISOLATION_GROUP_TYPE_PORT) + { + port_id = (port.m_type == Port::PHY ? port.m_port_id : port.m_lag_id); + } + + if (SAI_NULL_OBJECT_ID == port_id) + { + SWSS_LOG_NOTICE("Port %s not ready for for isolation group %s of type %d", + port.m_alias.c_str(), + m_name.c_str(), + m_type); + + m_pending_members.push_back(port.m_alias); + + return ISO_GRP_STATUS_SUCCESS; + } + + if (m_members.find(port.m_alias) != m_members.end()) + { + SWSS_LOG_DEBUG("Port %s: 0x%" PRIx64 "already a member of %s", port.m_alias.c_str(), port_id, m_name.c_str()); + } + else + { + sai_object_id_t mem_id = SAI_NULL_OBJECT_ID; + sai_attribute_t mem_attr[2]; + sai_status_t status = SAI_STATUS_SUCCESS; + + mem_attr[0].id = SAI_ISOLATION_GROUP_MEMBER_ATTR_ISOLATION_GROUP_ID; + mem_attr[0].value.oid = m_oid; + mem_attr[1].id = SAI_ISOLATION_GROUP_MEMBER_ATTR_ISOLATION_OBJECT; + mem_attr[1].value.oid = port_id; + + status = sai_isolation_group_api->create_isolation_group_member(&mem_id, gSwitchId, 2, mem_attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to add %s: 0x%" PRIx64 " as member of %s:0x%" PRIx64 , port.m_alias.c_str(), port_id, + m_name.c_str(), m_oid); + return ISO_GRP_STATUS_FAIL; + } + else + { + m_members[port.m_alias] = mem_id; + SWSS_LOG_NOTICE("Port %s: 0x%" PRIx64 " added as member of %s: 0x%" PRIx64 "with oid 0x%" PRIx64, + port.m_alias.c_str(), + port_id, + m_name.c_str(), + m_oid, + mem_id); + } + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::delMember(Port &port, bool do_fwd_ref) +{ + SWSS_LOG_ENTER(); + + if (m_members.find(port.m_alias) == m_members.end()) + { + auto node = find(m_pending_members.begin(), m_pending_members.end(), port.m_alias); + if (node != m_pending_members.end()) + { + m_pending_members.erase(node); + } + + return ISO_GRP_STATUS_SUCCESS; + } + + sai_object_id_t mem_id = m_members[port.m_alias]; + sai_status_t status = SAI_STATUS_SUCCESS; + + status = sai_isolation_group_api->remove_isolation_group_member(mem_id); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to delete isolation group member 0x%" PRIx64 " for port %s and iso group %s 0x%" PRIx64 , + mem_id, + port.m_alias.c_str(), + m_name.c_str(), + m_oid); + + return ISO_GRP_STATUS_FAIL; + } + else + { + SWSS_LOG_NOTICE("Deleted isolation group member 0x%" PRIx64 "for port %s and iso group %s 0x%" PRIx64 , + mem_id, + port.m_alias.c_str(), + m_name.c_str(), + m_oid); + + m_members.erase(port.m_alias); + } + + if (do_fwd_ref) + { + m_pending_members.push_back(port.m_alias); + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::setMembers(string ports) +{ + SWSS_LOG_ENTER(); + auto port_list = tokenize(ports, ','); + set portList(port_list.begin(), port_list.end()); + vector old_members = m_pending_members; + + for (auto mem : m_members) + { + old_members.emplace_back(mem.first); + } + + for (auto alias : portList) + { + if ((0 == alias.find("Ethernet")) || (0 == alias.find("PortChannel"))) + { + auto iter = find(old_members.begin(), old_members.end(), alias); + if (iter != old_members.end()) + { + SWSS_LOG_NOTICE("Port %s already part of %s. No change", alias.c_str(), m_name.c_str()); + old_members.erase(iter); + } + else + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_NOTICE("Port %s not found. Added it to m_pending_members", alias.c_str()); + m_pending_members.emplace_back(alias); + continue; + } + addMember(port); + } + } + else + { + SWSS_LOG_ERROR("Port %s not supported", alias.c_str()); + continue; + } + } + + // Remove all the ports which are no longer needed + for (auto alias : old_members) + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_ERROR("Port %s not found", alias.c_str()); + m_pending_members.erase(find(m_pending_members.begin(), m_pending_members.end(), port.m_alias)); + } + else + { + delMember(port); + } + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::bind(Port &port) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + sai_status_t status = SAI_STATUS_SUCCESS; + + if (find(m_bind_ports.begin(), m_bind_ports.end(), port.m_alias) != m_bind_ports.end()) + { + SWSS_LOG_NOTICE("isolation group %s of type %d already bound to Port %s", + m_name.c_str(), + m_type, + port.m_alias.c_str()); + + return ISO_GRP_STATUS_SUCCESS; + } + + attr.value.oid = m_oid; + if (m_type == ISOLATION_GROUP_TYPE_BRIDGE_PORT) + { + if (port.m_bridge_port_id != SAI_NULL_OBJECT_ID) + { + attr.id = SAI_BRIDGE_PORT_ATTR_ISOLATION_GROUP; + status = sai_bridge_api->set_bridge_port_attribute(port.m_bridge_port_id, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to set attribute %d value 0x%" PRIx64 "to %s", + attr.id, + attr.value.oid, + port.m_alias.c_str()); + } + else + { + m_bind_ports.push_back(port.m_alias); + } + } + else + { + m_pending_bind_ports.push_back(port.m_alias); + SWSS_LOG_NOTICE("Port %s saved in pending bind ports for isolation group %s of type %d", + port.m_alias.c_str(), + m_name.c_str(), + m_type); + } + } + else if ((m_type == ISOLATION_GROUP_TYPE_PORT) && (port.m_type == Port::PHY)) + { + if (port.m_port_id != SAI_NULL_OBJECT_ID) + { + attr.id = SAI_PORT_ATTR_ISOLATION_GROUP; + status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to set attribute %d value 0x%" PRIx64 "to %s", + attr.id, + attr.value.oid, + port.m_alias.c_str()); + } + else + { + m_bind_ports.push_back(port.m_alias); + } + } + else + { + m_pending_bind_ports.push_back(port.m_alias); + SWSS_LOG_NOTICE("Port %s saved in pending bind ports for isolation group %s of type %d", + port.m_alias.c_str(), + m_name.c_str(), + m_type); + } + } + else + { + SWSS_LOG_ERROR("Invalid attribute type %d ", m_type); + return ISO_GRP_STATUS_INVALID_PARAM; + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::unbind(Port &port, bool do_fwd_ref) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + sai_status_t status = SAI_STATUS_SUCCESS; + + if (find(m_bind_ports.begin(), m_bind_ports.end(), port.m_alias) == m_bind_ports.end()) + { + auto node = find(m_pending_bind_ports.begin(), m_pending_bind_ports.end(), port.m_alias); + if (node != m_pending_bind_ports.end()) + { + m_pending_bind_ports.erase(node); + } + + return ISO_GRP_STATUS_SUCCESS; + } + + attr.value.oid = SAI_NULL_OBJECT_ID; + if (m_type == ISOLATION_GROUP_TYPE_BRIDGE_PORT) + { + attr.id = SAI_BRIDGE_PORT_ATTR_ISOLATION_GROUP; + status = sai_bridge_api->set_bridge_port_attribute(port.m_bridge_port_id, &attr); + } + else if ((m_type == ISOLATION_GROUP_TYPE_PORT) && (port.m_type == Port::PHY)) + { + attr.id = SAI_PORT_ATTR_ISOLATION_GROUP; + status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + } + else + { + return ISO_GRP_STATUS_INVALID_PARAM; + } + + if (SAI_STATUS_SUCCESS != status) + { + SWSS_LOG_ERROR("Unable to set attribute %d value 0x%" PRIx64 "to %s", attr.id, attr.value.oid, port.m_alias.c_str()); + } + else + { + m_bind_ports.erase(find(m_bind_ports.begin(), m_bind_ports.end(), port.m_alias)); + } + + if (do_fwd_ref) + { + m_pending_bind_ports.push_back(port.m_alias); + } + + return ISO_GRP_STATUS_SUCCESS; +} + +isolation_group_status_t +IsolationGroup::setBindPorts(string ports) +{ + SWSS_LOG_ENTER(); + vector old_bindports = m_pending_bind_ports; + auto port_list = tokenize(ports, ','); + set portList(port_list.begin(), port_list.end()); + + old_bindports.insert(old_bindports.end(), m_bind_ports.begin(), m_bind_ports.end()); + for (auto alias : portList) + { + if ((0 == alias.find("Ethernet")) || (0 == alias.find("PortChannel"))) + { + auto iter = find(old_bindports.begin(), old_bindports.end(), alias); + if (iter != old_bindports.end()) + { + SWSS_LOG_NOTICE("%s is already bound to %s", m_name.c_str(), alias.c_str()); + old_bindports.erase(iter); + } + else + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_NOTICE("Port %s not found. Added it to m_pending_bind_ports", alias.c_str()); + m_pending_bind_ports.emplace_back(alias); + return ISO_GRP_STATUS_INVALID_PARAM; + } + bind(port); + } + } + else + { + return ISO_GRP_STATUS_INVALID_PARAM; + } + } + + // Remove all the ports which are no longer needed + for (auto alias : old_bindports) + { + Port port; + if (!gPortsOrch->getPort(alias, port)) + { + SWSS_LOG_ERROR("Port %s not found", alias.c_str()); + m_pending_bind_ports.erase(find(m_pending_bind_ports.begin(), m_pending_bind_ports.end(), port.m_alias)); + } + else + { + unbind(port); + } + } + + return ISO_GRP_STATUS_SUCCESS; +} + +void +IsolationGroup::update(SubjectType, void *cntx) +{ + PortUpdate *update = static_cast(cntx); + Port &port = update->port; + + if (update->add) + { + auto mem_node = find(m_pending_members.begin(), m_pending_members.end(), port.m_alias); + if (mem_node != m_pending_members.end()) + { + m_pending_members.erase(mem_node); + addMember(port); + } + + auto bind_node = find(m_pending_bind_ports.begin(), m_pending_bind_ports.end(), port.m_alias); + if (bind_node != m_pending_bind_ports.end()) + { + m_pending_bind_ports.erase(bind_node); + bind(port); + } + } + else + { + auto bind_node = find(m_bind_ports.begin(), m_bind_ports.end(), port.m_alias); + if (bind_node != m_bind_ports.end()) + { + unbind(port, true); + } + + auto mem_node = m_members.find(port.m_alias); + if (mem_node != m_members.end()) + { + delMember(port, true); + } + } +} diff --git a/orchagent/isolationgrouporch.h b/orchagent/isolationgrouporch.h new file mode 100644 index 0000000000..7bf8ef4c14 --- /dev/null +++ b/orchagent/isolationgrouporch.h @@ -0,0 +1,142 @@ +/* + * Copyright 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. + * and/or its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef __ISOLATIONGROUPORCH_H__ +#define __ISOLATIONGROUPORCH_H__ + +#include "orch.h" +#include "port.h" +#include "observer.h" + +#define ISOLATION_GRP_DESCRIPTION "DESCRIPTION" +#define ISOLATION_GRP_TYPE "TYPE" +#define ISOLATION_GRP_PORTS "PORTS" +#define ISOLATION_GRP_MEMBERS "MEMBERS" +#define ISOLATION_GRP_TYPE_PORT "port" +#define ISOLATION_GRP_TYPE_BRIDGE_PORT "bridge-port" + +typedef enum IsolationGroupType +{ + ISOLATION_GROUP_TYPE_INVALID, + ISOLATION_GROUP_TYPE_PORT, + ISOLATION_GROUP_TYPE_BRIDGE_PORT +} isolation_group_type_t; + +typedef enum IsolationGroupStatus +{ + ISO_GRP_STATUS_RETRY = -100, + ISO_GRP_STATUS_FAIL, + ISO_GRP_STATUS_INVALID_PARAM, + ISO_GRP_STATUS_SUCCESS = 0 +} isolation_group_status_t; + +class IsolationGroup: public Observer, public Subject +{ +public: + string m_description; + + IsolationGroup(string name, isolation_group_type_t type = ISOLATION_GROUP_TYPE_PORT, string description=""): + m_name(name), + m_description(description), + m_type(type), + m_oid(SAI_NULL_OBJECT_ID) + { + } + + // Create Isolation group in SAI + isolation_group_status_t create(); + + // Delete Isolation group in SAI + isolation_group_status_t destroy(); + + // Add Isolation group member + isolation_group_status_t addMember(Port &port); + + // Delete Isolation group member + isolation_group_status_t delMember(Port &port, bool do_fwd_ref=false); + + // Set Isolation group members to the input. May involve adding or deleting members + isolation_group_status_t setMembers(string ports); + + // Apply the Isolation group to all linked ports + isolation_group_status_t bind(Port &port); + + // Remove the Isolation group from all linked ports + isolation_group_status_t unbind(Port &port, bool do_fwd_ref=false); + + // Set Isolation group binding to the input. May involve bind + isolation_group_status_t setBindPorts(string ports); + + void update(SubjectType, void *); + + isolation_group_type_t + getType() + { + return m_type; + } + + void notifyObservers(SubjectType type, void *cntx) + { + this->notify(type, cntx); + } + +protected: + string m_name; + isolation_group_type_t m_type; + sai_object_id_t m_oid; + map m_members; // Members Name -> Member OID + vector m_bind_ports; // Ports in which this Iso Group is applied. + vector m_pending_members; + vector m_pending_bind_ports; +}; + +class IsoGrpOrch : public Orch, public Observer +{ +public: + IsoGrpOrch(vector &connectors); + + ~IsoGrpOrch(); + + shared_ptr + getIsolationGroup(string name); + + isolation_group_status_t + addIsolationGroup(string name, isolation_group_type_t type, string descr, string bindPorts, string memPorts); + + isolation_group_status_t + delIsolationGroup(string name); + + void update(SubjectType, void *); + +private: + void + doTask(Consumer &consumer); + + void + doIsoGrpTblTask(Consumer &consumer); + + map> m_isolationGrps; +}; + +struct IsolationGroupUpdate +{ + IsolationGroup *group; + bool add; +}; + + +#endif /* __ISOLATIONGROUPORCH_H__ */ diff --git a/orchagent/macsecorch.cpp b/orchagent/macsecorch.cpp index c5510a16fa..2247deba84 100644 --- a/orchagent/macsecorch.cpp +++ b/orchagent/macsecorch.cpp @@ -31,14 +31,9 @@ constexpr bool DEFAULT_ENABLE_ENCRYPT = true; constexpr bool DEFAULT_SCI_IN_SECTAG = false; constexpr sai_macsec_cipher_suite_t DEFAULT_CIPHER_SUITE = SAI_MACSEC_CIPHER_SUITE_GCM_AES_128; -static const std::vector macsec_egress_sa_attrs = +static const std::vector macsec_sa_attrs = { - "SAI_MACSEC_SA_ATTR_XPN", -}; - -static const std::vector macsec_ingress_sa_attrs = - { - "SAI_MACSEC_SA_ATTR_MINIMUM_XPN", + "SAI_MACSEC_SA_ATTR_CURRENT_XPN", }; template @@ -854,15 +849,20 @@ bool MACsecOrch::initMACsecObject(sai_object_id_t switch_id) attrs.clear(); attr.id = SAI_MACSEC_ATTR_SCI_IN_INGRESS_MACSEC_ACL; attrs.push_back(attr); - if (sai_macsec_api->get_macsec_attribute( - macsec_obj.first->second.m_ingress_id, - static_cast(attrs.size()), - attrs.data()) != SAI_STATUS_SUCCESS) + status = sai_macsec_api->get_macsec_attribute( + macsec_obj.first->second.m_ingress_id, + static_cast(attrs.size()), + attrs.data()); + if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_WARN( "Cannot get MACsec attribution SAI_MACSEC_ATTR_SCI_IN_INGRESS_MACSEC_ACL at the switch 0x%" PRIx64, switch_id); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_MACSEC, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } macsec_obj.first->second.m_sci_in_ingress_macsec_acl = attrs.front().value.booldata; @@ -1738,16 +1738,15 @@ task_process_status MACsecOrch::createMACsecSA( sc->m_sa_ids.erase(an); }); + installCounter(CounterType::MACSEC_SA_ATTR, port_sci_an, sc->m_sa_ids[an], macsec_sa_attrs); std::vector fvVector; fvVector.emplace_back("state", "ok"); if (direction == SAI_MACSEC_DIRECTION_EGRESS) { - installCounter(CounterType::MACSEC_SA_ATTR, port_sci_an, sc->m_sa_ids[an], macsec_egress_sa_attrs); m_state_macsec_egress_sa.set(swss::join('|', port_name, sci, an), fvVector); } else { - installCounter(CounterType::MACSEC_SA_ATTR, port_sci_an, sc->m_sa_ids[an], macsec_ingress_sa_attrs); m_state_macsec_ingress_sa.set(swss::join('|', port_name, sci, an), fvVector); } diff --git a/orchagent/main.cpp b/orchagent/main.cpp index 018af5999b..5c06e0a8a8 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -46,6 +46,7 @@ sai_object_id_t gUnderlayIfId; sai_object_id_t gSwitchId = SAI_NULL_OBJECT_ID; MacAddress gMacAddress; MacAddress gVxlanMacAddress; +bool gP2PTunnelSupported; extern size_t gMaxBulkSize; @@ -85,11 +86,11 @@ void usage() cout << " -b batch_size: set consumer table pop operation batch size (default 128)" << endl; cout << " -m MAC: set switch MAC address" << endl; cout << " -i INST_ID: set the ASIC instance_id in multi-asic platform" << endl; - cout << " -s: enable synchronous mode (deprecated, use -z)" << endl; - cout << " -z: redis communication mode (redis_async|redis_sync|zmq_sync), default: redis_async" << endl; + cout << " -s enable synchronous mode (deprecated, use -z)" << endl; + cout << " -z redis communication mode (redis_async|redis_sync|zmq_sync), default: redis_async" << endl; cout << " -f swss_rec_filename: swss record log filename(default 'swss.rec')" << endl; cout << " -j sairedis_rec_filename: sairedis record log filename(default sairedis.rec)" << endl; - cout << " -k max bulk size in bulk mode (default 1000)"; + cout << " -k max bulk size in bulk mode (default 1000)" << endl; } void sighup_handler(int signo) @@ -643,6 +644,32 @@ int main(int argc, char **argv) orchDaemon = make_shared(&appl_db, &config_db, &state_db, chassis_app_db.get()); } + uint32_t max_tunnel_modes = 2; + vector tunnel_peer_modes(max_tunnel_modes, 0); + sai_s32_list_t values; + values.count = max_tunnel_modes; + values.list = tunnel_peer_modes.data(); + + status = sai_query_attribute_enum_values_capability(gSwitchId, SAI_OBJECT_TYPE_TUNNEL, + SAI_TUNNEL_ATTR_PEER_MODE, &values); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Unable to get supported tunnel peer modes. Defaulting to P2P"); + gP2PTunnelSupported = true; + } + else + { + gP2PTunnelSupported = false; + for (uint32_t idx = 0; idx < values.count; idx++) + { + if (values.list[idx] == SAI_TUNNEL_PEER_MODE_P2P) + { + gP2PTunnelSupported = true; + break; + } + } + } + if (!orchDaemon->init()) { SWSS_LOG_ERROR("Failed to initialize orchestration daemon"); diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index c2c78c7601..37c2ef73df 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -759,7 +759,7 @@ bool MirrorOrch::setUnsetPortMirror(Port port, status = sai_port_api->set_port_attribute(p.m_port_id, &port_attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to configure %s session on port %s: %s, status %d, sessionId %x", + SWSS_LOG_ERROR("Failed to configure %s session on port %s: %s, status %d, sessionId %lx", ingress ? "RX" : "TX", port.m_alias.c_str(), p.m_alias.c_str(), status, sessionId); task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); @@ -775,7 +775,7 @@ bool MirrorOrch::setUnsetPortMirror(Port port, status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to configure %s session on port %s, status %d, sessionId %x", + SWSS_LOG_ERROR("Failed to configure %s session on port %s, status %d, sessionId %lx", ingress ? "RX" : "TX", port.m_alias.c_str(), status, sessionId); task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); if (handle_status != task_success) diff --git a/orchagent/mlagorch.cpp b/orchagent/mlagorch.cpp new file mode 100644 index 0000000000..81fd169fe4 --- /dev/null +++ b/orchagent/mlagorch.cpp @@ -0,0 +1,250 @@ +/* + * Copyright 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. + * and/or its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "portsorch.h" +#include "mlagorch.h" + +using namespace std; +using namespace swss; + +extern PortsOrch *gPortsOrch; +extern MlagOrch *gMlagOrch; + +MlagOrch::MlagOrch(DBConnector *db, vector &tableNames): + Orch(db, tableNames) +{ + SWSS_LOG_ENTER(); +} + +MlagOrch::~MlagOrch() +{ + SWSS_LOG_ENTER(); +} + +void MlagOrch::update(SubjectType type, void *cntx) +{ + SWSS_LOG_ENTER(); +} +//------------------------------------------------------------------ +//Private API section +//------------------------------------------------------------------ +void MlagOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + if (!gPortsOrch->allPortsReady()) + { + return; + } + string table_name = consumer.getTableName(); + if (table_name == CFG_MCLAG_TABLE_NAME) + { + doMlagDomainTask(consumer); + } + else if (table_name == CFG_MCLAG_INTF_TABLE_NAME) + { + doMlagInterfaceTask(consumer); + } + else + { + SWSS_LOG_ERROR("MLAG receives invalid table %s", table_name.c_str()); + } +} + +//Only interest in peer-link info from MLAG domain table +void MlagOrch::doMlagDomainTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + string peer_link; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "peer_link") + { + peer_link = fvValue(i); + break; + } + } + if (!peer_link.empty()) + { + if (addIslInterface(peer_link)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else + it = consumer.m_toSync.erase(it); + } + else if (op == DEL_COMMAND) + { + if (delIslInterface()) + it = consumer.m_toSync.erase(it); + else + it++; + } + else + { + SWSS_LOG_ERROR("MLAG receives unknown operation type %s", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +//MLAG interface table key format: MCLAG_INTF_TABLE|mclag|ifname +void MlagOrch::doMlagInterfaceTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + size_t delimiter_pos; + string mlag_if_name; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string op = kfvOp(t); + string key = kfvKey(t); + + delimiter_pos = key.find_first_of("|"); + mlag_if_name = key.substr(delimiter_pos+1); + + if (op == SET_COMMAND) + { + if (addMlagInterface(mlag_if_name)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else if (op == DEL_COMMAND) + { + if (delMlagInterface(mlag_if_name)) + it = consumer.m_toSync.erase(it); + else + it++; + } + else + { + SWSS_LOG_ERROR("MLAG receives unknown operation type %s", op.c_str()); + it = consumer.m_toSync.erase(it); + } + } +} + +bool MlagOrch::addIslInterface(string isl_name) +{ + Port isl_port; + MlagIslUpdate update; + + //No change + if ((m_isl_name == isl_name) || (isl_name.empty())) + return true; + + m_isl_name = isl_name; + + //Update observers + update.isl_name = isl_name; + update.is_add = true; + notify(SUBJECT_TYPE_MLAG_ISL_CHANGE, static_cast(&update)); + return true; +} + +bool MlagOrch::delIslInterface() +{ + MlagIslUpdate update; + + if (m_isl_name.empty()) + return true; + + update.isl_name = m_isl_name; + update.is_add = false; + + m_isl_name.clear(); + + //Notify observer + notify(SUBJECT_TYPE_MLAG_ISL_CHANGE, static_cast(&update)); + + return true; +} + +//Mlag interface can be added even before interface is configured +bool MlagOrch::addMlagInterface(string if_name) +{ + MlagIfUpdate update; + + //Duplicate add + if (m_mlagIntfs.find(if_name) != m_mlagIntfs.end()) + { + SWSS_LOG_ERROR("MLAG adds duplicate MLAG interface %s", if_name.c_str()); + } + else + { + + m_mlagIntfs.insert(if_name); + + //Notify observer + update.if_name = if_name; + update.is_add = true; + notify(SUBJECT_TYPE_MLAG_INTF_CHANGE, static_cast(&update)); + } + return true; + +} +bool MlagOrch::delMlagInterface(string if_name) +{ + MlagIfUpdate update; + + //Delete an unknown MLAG interface + if (m_mlagIntfs.find(if_name) == m_mlagIntfs.end()) + { + SWSS_LOG_ERROR("MLAG deletes unknown MLAG interface %s", if_name.c_str()); + } + else + { + m_mlagIntfs.erase(if_name); + + //Notify observers + update.if_name = if_name; + update.is_add = false; + notify(SUBJECT_TYPE_MLAG_INTF_CHANGE, static_cast(&update)); + } + return true; +} + +bool MlagOrch::isMlagInterface(string if_name) +{ + if (m_mlagIntfs.find(if_name) == m_mlagIntfs.end()) + return false; + else + return true; +} + +bool MlagOrch::isIslInterface(string if_name) +{ + if (m_isl_name == if_name) + return true; + else + return false; +} diff --git a/orchagent/mlagorch.h b/orchagent/mlagorch.h new file mode 100644 index 0000000000..27f4e9219b --- /dev/null +++ b/orchagent/mlagorch.h @@ -0,0 +1,66 @@ +/* + * Copyright 2019 Broadcom. The term "Broadcom" refers to Broadcom Inc. + * and/or its subsidiaries. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SWSS_MLAGORCH_H +#define SWSS_MLAGORCH_H + +#include +#include +#include "orch.h" +#include "port.h" + +struct MlagIfUpdate +{ + string if_name; + bool is_add; +}; + +struct MlagIslUpdate +{ + string isl_name; + bool is_add; +}; + +class MlagOrch: public Orch, public Observer, public Subject +{ +public: + MlagOrch(DBConnector *db, vector &tableNames); + ~MlagOrch(); + void update(SubjectType type, void *cntx); + bool isMlagInterface(string if_name); + bool isIslInterface(string if_name); + + const std::set& + getMlagIntfs() const + { + return m_mlagIntfs; + } + +private: + std::string m_isl_name; + std::set m_mlagIntfs; + + void doTask(Consumer &consumer); + void doMlagDomainTask(Consumer &consumer); + void doMlagInterfaceTask(Consumer &consumer); + bool addIslInterface(string isl_name); + bool delIslInterface(); + bool addMlagInterface(string if_name); + bool delMlagInterface(string if_name); +}; + +#endif /* SWSS_MLAGORCH_H */ diff --git a/orchagent/muxorch.cpp b/orchagent/muxorch.cpp index 503ff3cd16..6cfc6bc4d5 100644 --- a/orchagent/muxorch.cpp +++ b/orchagent/muxorch.cpp @@ -1300,7 +1300,17 @@ void MuxCableOrch::updateMuxMetricState(string portName, string muxState, bool s char buf[256]; std::strftime(buf, 256, "%Y-%b-%d %H:%M:%S.", &now_tm); - string time = string(buf) + to_string(micros); + /* + * Prepend '0's for 6 point precision + */ + const int precision = 6; + auto ms = to_string(micros); + if (ms.length() < precision) + { + ms.insert(ms.begin(), precision - ms.length(), '0'); + } + + string time = string(buf) + ms; mux_metric_table_.hset(portName, msg, time); } diff --git a/orchagent/natorch.cpp b/orchagent/natorch.cpp index 283110efd1..d7f124a28e 100644 --- a/orchagent/natorch.cpp +++ b/orchagent/natorch.cpp @@ -2667,7 +2667,6 @@ void NatOrch::doNatTableTask(Consumer& consumer) string key = kfvKey(t); string op = kfvOp(t); vector keys = tokenize(key, ':'); - IpAddress global_address; /* Example : APPL_DB * NAT_TABLE:65.55.45.1 * translated_ip: 10.0.0.1 @@ -2808,7 +2807,6 @@ void NatOrch::doTwiceNatTableTask(Consumer& consumer) string key = kfvKey(t); string op = kfvOp(t); vector keys = tokenize(key, ':'); - IpAddress global_address; /* Example : APPL_DB * NAT_TWICE_TABLE:91.91.91.91:65.55.45.1 * translated_src_ip: 14.14.14.14 @@ -3020,7 +3018,6 @@ void NatOrch::doDnatPoolTableTask(Consumer& consumer) string key = kfvKey(t); string op = kfvOp(t); vector keys = tokenize(key, ':'); - IpAddress global_address; /* Example : APPL_DB * NAT_DNAT_POOL_TABLE:65.55.45.1 * NULL: NULL diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index acd8d8718b..e3f9a4175e 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -191,7 +191,7 @@ bool NeighOrch::addNextHop(const NextHopKey &nh) } NextHopKey nexthop(nh); - if (m_intfsOrch->isRemoteSystemPortIntf(nexthop.alias)) + if (m_intfsOrch->isRemoteSystemPortIntf(nh.alias)) { //For remote system ports kernel nexthops are always on inband. Change the key Port inbp; @@ -202,7 +202,7 @@ bool NeighOrch::addNextHop(const NextHopKey &nh) } assert(!hasNextHop(nexthop)); - sai_object_id_t rif_id = m_intfsOrch->getRouterIntfsId(nexthop.alias); + sai_object_id_t rif_id = m_intfsOrch->getRouterIntfsId(nh.alias); vector next_hop_attrs; @@ -671,6 +671,19 @@ void NeighOrch::doTask(Consumer &consumer) IpAddress ip_address(key.substr(found+1)); + /* Verify Ipv4 LinkLocal and skip neighbor entry added for RFC5549 */ + if ((ip_address.getAddrScope() == IpAddress::LINK_SCOPE) && (ip_address.isV4())) + { + /* Check if this prefix is not a configured ip, if so allow */ + IpPrefix ipll_prefix(ip_address.getV4Addr(), 16); + if (!m_intfsOrch->isPrefixSubnet (ipll_prefix, alias)) + { + SWSS_LOG_NOTICE("Skip IPv4LL neighbor %s, Intf:%s op: %s ", ip_address.to_string().c_str(), alias.c_str(), op.c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + } + NeighborEntry neighbor_entry = { ip_address, alias }; if (op == SET_COMMAND) @@ -1547,7 +1560,11 @@ void NeighOrch::voqSyncAddNeigh(string &alias, IpAddress &ip_address, const MacA if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get neighbor attribute for %s on %s, rv:%d", ip_address.to_string().c_str(), alias.c_str(), status); - return; + task_process_status handle_status = handleSaiGetStatus(SAI_API_NEIGHBOR, status); + if (handle_status != task_process_status::task_success) + { + return; + } } if (!attr.value.u32) diff --git a/orchagent/observer.h b/orchagent/observer.h index 76f00f1bfd..03668c1198 100644 --- a/orchagent/observer.h +++ b/orchagent/observer.h @@ -16,8 +16,14 @@ enum SubjectType SUBJECT_TYPE_MIRROR_SESSION_CHANGE, SUBJECT_TYPE_INT_SESSION_CHANGE, SUBJECT_TYPE_PORT_CHANGE, + SUBJECT_TYPE_BRIDGE_PORT_CHANGE, SUBJECT_TYPE_PORT_OPER_STATE_CHANGE, - SUBJECT_TYPE_FDB_FLUSH_CHANGE, + SUBJECT_TYPE_ISOLATION_GROUP_CHANGE, + SUBJECT_TYPE_ISOLATION_GROUP_MEMBER_CHANGE, + SUBJECT_TYPE_ISOLATION_GROUP_BINDING_CHANGE, + SUBJECT_TYPE_MLAG_INTF_CHANGE, + SUBJECT_TYPE_MLAG_ISL_CHANGE, + SUBJECT_TYPE_FDB_FLUSH_CHANGE }; class Observer diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index 464e6aa688..b4ef246662 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -536,7 +536,7 @@ void Orch::logfileReopen() * empty file here. */ - gRecordOfs.open(gRecordFile); + gRecordOfs.open(gRecordFile, std::ofstream::out | std::ofstream::app); if (!gRecordOfs.is_open()) { @@ -699,15 +699,38 @@ task_process_status Orch::handleSaiCreateStatus(sai_api_t api, sai_status_t stat * in each orch. * 3. Take the type of sai api into consideration. */ - switch (status) + switch (api) { - case SAI_STATUS_SUCCESS: - SWSS_LOG_WARN("SAI_STATUS_SUCCESS is not expected in handleSaiCreateStatus"); - return task_success; + case SAI_API_FDB: + switch (status) + { + case SAI_STATUS_SUCCESS: + SWSS_LOG_WARN("SAI_STATUS_SUCCESS is not expected in handleSaiCreateStatus"); + return task_success; + case SAI_STATUS_ITEM_ALREADY_EXISTS: + /* + * In FDB creation, there are scenarios where the hardware learns an FDB entry before orchagent. + * In such cases, the FDB SAI creation would report the status of SAI_STATUS_ITEM_ALREADY_EXISTS, + * and orchagent should ignore the error and treat it as entry was explicitly created. + */ + return task_success; + default: + SWSS_LOG_ERROR("Encountered failure in create operation, exiting orchagent, SAI API: %s, status: %s", + sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); + exit(EXIT_FAILURE); + } + break; default: - SWSS_LOG_ERROR("Encountered failure in create operation, exiting orchagent, SAI API: %s, status: %s", - sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); - exit(EXIT_FAILURE); + switch (status) + { + case SAI_STATUS_SUCCESS: + SWSS_LOG_WARN("SAI_STATUS_SUCCESS is not expected in handleSaiCreateStatus"); + return task_success; + default: + SWSS_LOG_ERROR("Encountered failure in create operation, exiting orchagent, SAI API: %s, status: %s", + sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); + exit(EXIT_FAILURE); + } } return task_need_retry; } @@ -765,6 +788,35 @@ task_process_status Orch::handleSaiRemoveStatus(sai_api_t api, sai_status_t stat return task_need_retry; } +task_process_status Orch::handleSaiGetStatus(sai_api_t api, sai_status_t status, void *context) +{ + /* + * This function aims to provide coarse handling of failures in sairedis get + * operation (i.e., notify users by throwing excepions when failures happen). + * Return value: task_success - Handled the status successfully. No need to retry this SAI operation. + * task_need_retry - Cannot handle the status. Need to retry the SAI operation. + * task_failed - Failed to handle the status but another attempt is unlikely to resolve the failure. + * TODO: 1. Add general handling logic for specific statuses + * 2. Develop fine-grain failure handling mechanisms and replace this coarse handling + * in each orch. + * 3. Take the type of sai api into consideration. + */ + switch (status) + { + case SAI_STATUS_SUCCESS: + SWSS_LOG_WARN("SAI_STATUS_SUCCESS is not expected in handleSaiGetStatus"); + return task_success; + case SAI_STATUS_NOT_IMPLEMENTED: + SWSS_LOG_ERROR("Encountered failure in get operation due to the function is not implemented, exiting orchagent, SAI API: %s", + sai_serialize_api(api).c_str()); + throw std::logic_error("SAI get function not implemented"); + default: + SWSS_LOG_ERROR("Encountered failure in get operation, SAI API: %s, status: %s", + sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); + } + return task_failed; +} + bool Orch::parseHandleSaiStatusFailure(task_process_status status) { /* diff --git a/orchagent/orch.h b/orchagent/orch.h index b61cdb53e2..766d02c766 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -240,6 +240,7 @@ class Orch virtual task_process_status handleSaiCreateStatus(sai_api_t api, sai_status_t status, void *context = nullptr); virtual task_process_status handleSaiSetStatus(sai_api_t api, sai_status_t status, void *context = nullptr); virtual task_process_status handleSaiRemoveStatus(sai_api_t api, sai_status_t status, void *context = nullptr); + virtual task_process_status handleSaiGetStatus(sai_api_t api, sai_status_t status, void *context = nullptr); bool parseHandleSaiStatusFailure(task_process_status status); private: void removeMeFromObjsReferencedByMe(type_map &type_maps, const std::string &table, const std::string &obj_name, const std::string &field, const std::string &old_referenced_obj_name); diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 673c5f71ec..72a2b2ce50 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -20,6 +20,7 @@ using namespace swss; extern sai_switch_api_t* sai_switch_api; extern sai_object_id_t gSwitchId; extern bool gSaiRedisLogRotate; +extern bool gP2PTunnelSupported; extern void syncd_apply_view(); /* @@ -39,6 +40,8 @@ BufferOrch *gBufferOrch; SwitchOrch *gSwitchOrch; Directory gDirectory; NatOrch *gNatOrch; +MlagOrch *gMlagOrch; +IsoGrpOrch *gIsoGrpOrch; MACsecOrch *gMacsecOrch; bool gIsNatSupported = false; @@ -104,13 +107,15 @@ bool OrchDaemon::init() vector app_fdb_tables = { { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, - { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri} + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_MCLAG_FDB_TABLE_NAME, FdbOrch::fdborch_pri} }; gCrmOrch = new CrmOrch(m_configDb, CFG_CRM_TABLE_NAME); gPortsOrch = new PortsOrch(m_applDb, m_stateDb, ports_tables, m_chassisAppDb); TableConnector stateDbFdb(m_stateDb, STATE_FDB_TABLE_NAME); - gFdbOrch = new FdbOrch(m_applDb, app_fdb_tables, stateDbFdb, gPortsOrch); + TableConnector stateMclagDbFdb(m_stateDb, STATE_MCLAG_REMOTE_FDB_TABLE_NAME); + gFdbOrch = new FdbOrch(m_applDb, app_fdb_tables, stateDbFdb, stateMclagDbFdb, gPortsOrch); vector vnet_tables = { APP_VNET_RT_TABLE_NAME, @@ -163,19 +168,18 @@ bool OrchDaemon::init() CoppOrch *copp_orch = new CoppOrch(m_applDb, APP_COPP_TABLE_NAME); TunnelDecapOrch *tunnel_decap_orch = new TunnelDecapOrch(m_applDb, APP_TUNNEL_DECAP_TABLE_NAME); - VxlanTunnelOrch *vxlan_tunnel_orch = new VxlanTunnelOrch(m_stateDb, m_applDb, APP_VXLAN_TUNNEL_TABLE_NAME); - gDirectory.set(vxlan_tunnel_orch); VxlanTunnelMapOrch *vxlan_tunnel_map_orch = new VxlanTunnelMapOrch(m_applDb, APP_VXLAN_TUNNEL_MAP_TABLE_NAME); gDirectory.set(vxlan_tunnel_map_orch); VxlanVrfMapOrch *vxlan_vrf_orch = new VxlanVrfMapOrch(m_applDb, APP_VXLAN_VRF_TABLE_NAME); gDirectory.set(vxlan_vrf_orch); - EvpnRemoteVniOrch* evpn_remote_vni_orch = new EvpnRemoteVniOrch(m_applDb, APP_VXLAN_REMOTE_VNI_TABLE_NAME); - gDirectory.set(evpn_remote_vni_orch); EvpnNvoOrch* evpn_nvo_orch = new EvpnNvoOrch(m_applDb, APP_VXLAN_EVPN_NVO_TABLE_NAME); gDirectory.set(evpn_nvo_orch); + VxlanTunnelOrch *vxlan_tunnel_orch = new VxlanTunnelOrch(m_stateDb, m_applDb, + APP_VXLAN_TUNNEL_TABLE_NAME, gP2PTunnelSupported); + gDirectory.set(vxlan_tunnel_orch); vector qos_tables = { CFG_TC_TO_QUEUE_MAP_TABLE_NAME, @@ -326,6 +330,19 @@ bool OrchDaemon::init() } gAclOrch = new AclOrch(acl_table_connectors, gSwitchOrch, gPortsOrch, gMirrorOrch, gNeighOrch, gRouteOrch, dtel_orch); + vector mlag_tables = { + { CFG_MCLAG_TABLE_NAME }, + { CFG_MCLAG_INTF_TABLE_NAME } + }; + gMlagOrch = new MlagOrch(m_configDb, mlag_tables); + + TableConnector appDbIsoGrpTbl(m_applDb, APP_ISOLATION_GROUP_TABLE_NAME); + vector iso_grp_tbl_ctrs = { + appDbIsoGrpTbl + }; + + gIsoGrpOrch = new IsoGrpOrch(iso_grp_tbl_ctrs); + m_orchList.push_back(gFdbOrch); m_orchList.push_back(gMirrorOrch); m_orchList.push_back(gAclOrch); @@ -334,12 +351,27 @@ bool OrchDaemon::init() m_orchList.push_back(vxlan_tunnel_orch); m_orchList.push_back(evpn_nvo_orch); m_orchList.push_back(vxlan_tunnel_map_orch); - m_orchList.push_back(evpn_remote_vni_orch); + + if (gP2PTunnelSupported) + { + EvpnRemoteVnip2pOrch* evpn_remote_vni_orch = new EvpnRemoteVnip2pOrch(m_applDb, APP_VXLAN_REMOTE_VNI_TABLE_NAME); + gDirectory.set(evpn_remote_vni_orch); + m_orchList.push_back(evpn_remote_vni_orch); + } + else + { + EvpnRemoteVnip2mpOrch* evpn_remote_vni_orch = new EvpnRemoteVnip2mpOrch(m_applDb, APP_VXLAN_REMOTE_VNI_TABLE_NAME); + gDirectory.set(evpn_remote_vni_orch); + m_orchList.push_back(evpn_remote_vni_orch); + } + m_orchList.push_back(vxlan_vrf_orch); m_orchList.push_back(cfg_vnet_rt_orch); m_orchList.push_back(vnet_orch); m_orchList.push_back(vnet_rt_orch); m_orchList.push_back(gNatOrch); + m_orchList.push_back(gMlagOrch); + m_orchList.push_back(gIsoGrpOrch); m_orchList.push_back(gFgNhgOrch); m_orchList.push_back(mux_orch); m_orchList.push_back(mux_cb_orch); diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 1829414265..bdcc053ed3 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -32,6 +32,8 @@ #include "debugcounterorch.h" #include "directory.h" #include "natorch.h" +#include "isolationgrouporch.h" +#include "mlagorch.h" #include "muxorch.h" #include "macsecorch.h" diff --git a/orchagent/pfcactionhandler.cpp b/orchagent/pfcactionhandler.cpp index 34c513e5d6..cf43f57d08 100644 --- a/orchagent/pfcactionhandler.cpp +++ b/orchagent/pfcactionhandler.cpp @@ -121,7 +121,7 @@ PfcWdActionHandler::PfcWdQueueStats PfcWdActionHandler::getQueueStats(shared_ptr if (!countersTable->get(queueIdStr, fieldValues)) { - return move(stats); + return stats; } for (const auto& fv : fieldValues) @@ -175,7 +175,7 @@ PfcWdActionHandler::PfcWdQueueStats PfcWdActionHandler::getQueueStats(shared_ptr } } - return move(stats); + return stats; } void PfcWdActionHandler::initWdCounters(shared_ptr countersTable, const string &queueIdStr) diff --git a/orchagent/port.h b/orchagent/port.h index 57c9f6e2d9..31c3392fcd 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -38,11 +38,17 @@ struct VlanMemberEntry typedef std::map vlan_members_t; +typedef std::map endpoint_ip_l2mc_group_member_map_t; + struct VlanInfo { sai_object_id_t vlan_oid = 0; sai_vlan_id_t vlan_id = 0; sai_object_id_t host_intf_id = SAI_NULL_OBJECT_ID; + sai_vlan_flood_control_type_t uuc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + sai_vlan_flood_control_type_t bc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + sai_object_id_t l2mc_group_id = SAI_NULL_OBJECT_ID; + endpoint_ip_l2mc_group_member_map_t l2mc_members; }; struct SystemPortInfo diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index a699ddded1..9b26cecbde 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -42,6 +42,7 @@ extern sai_acl_api_t* sai_acl_api; extern sai_queue_api_t *sai_queue_api; extern sai_object_id_t gSwitchId; extern sai_fdb_api_t *sai_fdb_api; +extern sai_l2mc_group_api_t *sai_l2mc_group_api; extern IntfsOrch *gIntfsOrch; extern NeighOrch *gNeighOrch; extern CrmOrch *gCrmOrch; @@ -266,6 +267,12 @@ static char* hostif_vlan_tag[] = { [SAI_HOSTIF_VLAN_TAG_KEEP] = "SAI_HOSTIF_VLAN_TAG_KEEP", [SAI_HOSTIF_VLAN_TAG_ORIGINAL] = "SAI_HOSTIF_VLAN_TAG_ORIGINAL" }; + +static bool isValidPortTypeForLagMember(const Port& port) +{ + return (port.m_type == Port::Type::PHY || port.m_type == Port::Type::SYSTEM); +} + /* * Initialize PortsOrch * 0) If Gearbox is enabled, then initialize the external PHYs as defined in @@ -380,7 +387,11 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector tmp_lane_set; @@ -448,6 +471,45 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector supported_flood_control_types(max_flood_control_types, 0); + sai_s32_list_t values; + values.count = max_flood_control_types; + values.list = supported_flood_control_types.data(); + + if (sai_query_attribute_enum_values_capability(gSwitchId, SAI_OBJECT_TYPE_VLAN, + SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_CONTROL_TYPE, + &values) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("This device does not support unknown unicast flood control types"); + } + else + { + for (uint32_t idx = 0; idx < values.count; idx++) + { + uuc_sup_flood_control_type.insert(static_cast(values.list[idx])); + } + } + + + supported_flood_control_types.assign(max_flood_control_types, 0); + values.count = max_flood_control_types; + values.list = supported_flood_control_types.data(); + + if (sai_query_attribute_enum_values_capability(gSwitchId, SAI_OBJECT_TYPE_VLAN, + SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE, + &values) != SAI_STATUS_SUCCESS) + { + SWSS_LOG_NOTICE("This device does not support broadcast flood control types"); + } + else + { + for (uint32_t idx = 0; idx < values.count; idx++) + { + bc_sup_flood_control_type.insert(static_cast(values.list[idx])); + } + } + /* Get default 1Q bridge and default VLAN */ vector attrs; attr.id = SAI_SWITCH_ATTR_DEFAULT_1Q_BRIDGE_ID; @@ -459,7 +521,11 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vectorget_port_attribute(id, 1, &attr); if (status == SAI_STATUS_SUCCESS) + { speed = attr.value.u32; + } + else + { + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + return false; + } + } - return status == SAI_STATUS_SUCCESS; + return true; } bool PortsOrch::setPortAdvSpeeds(sai_object_id_t port_id, std::vector& speed_list) @@ -1994,7 +2104,11 @@ bool PortsOrch::getQueueTypeAndIndex(sai_object_id_t queue_id, string &type, uin if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get queue type and index for queue %" PRIu64 " rv:%d", queue_id, status); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_QUEUE, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } switch (attr[0].value.s32) @@ -2193,7 +2307,7 @@ sai_status_t PortsOrch::removePort(sai_object_id_t port_id) Port port; - /* + /* * Make sure to bring down admin state. * SET would have replaced with DEL */ @@ -2235,11 +2349,6 @@ bool PortsOrch::initPort(const string &alias, const string &role, const int inde { SWSS_LOG_ENTER(); - if (role == "Rec" || role == "Inb") - { - return doProcessRecircPort(alias, role, lane_set, SET_COMMAND); - } - /* Determine if the lane combination exists in switch */ if (m_portListLaneMap.find(lane_set) != m_portListLaneMap.end()) { @@ -2294,6 +2403,11 @@ bool PortsOrch::initPort(const string &alias, const string &role, const int inde m_portList[alias].m_init = true; + if (role == "Rec" || role == "Inb") + { + m_recircPortRole[alias] = role; + } + SWSS_LOG_NOTICE("Initialized port %s", alias.c_str()); } else @@ -2575,26 +2689,23 @@ void PortsOrch::doPortTask(Consumer &consumer) { an_str = fvValue(i); } - /* Set advertised speeds */ - if (fvField(i) == "adv_speeds") + else if (fvField(i) == "adv_speeds") { adv_speeds_str = fvValue(i); } - /* Set interface type */ - if (fvField(i) == "interface_type") + else if (fvField(i) == "interface_type") { interface_type_str = fvValue(i); } - /* Set advertised interface type */ - if (fvField(i) == "adv_interface_types") + else if (fvField(i) == "adv_interface_types") { adv_interface_types_str = fvValue(i); } /* Set port serdes Pre-emphasis */ - if (fvField(i) == "preemphasis") + else if (fvField(i) == "preemphasis") { getPortSerdesVal(fvValue(i), attr_val); serdes_attr.insert(serdes_attr_pair(SAI_PORT_SERDES_ATTR_PREEMPHASIS, attr_val)); @@ -2756,16 +2867,6 @@ void PortsOrch::doPortTask(Consumer &consumer) } else { - /* Skip configuring recirc port for now because the current SAI implementation of some vendors - * have limiited support for recirc port. This check can be removed once SAI implementation - * is enhanced/changed in the future. - */ - if (m_recircPortRole.find(alias) != m_recircPortRole.end()) - { - it = consumer.m_toSync.erase(it); - continue; - } - if (!an_str.empty()) { if (autoneg_mode_map.find(an_str) == autoneg_mode_map.end()) @@ -2775,7 +2876,7 @@ void PortsOrch::doPortTask(Consumer &consumer) it = consumer.m_toSync.erase(it); continue; } - + an = autoneg_mode_map[an_str]; if (an != p.m_autoneg) { @@ -2841,7 +2942,7 @@ void PortsOrch::doPortTask(Consumer &consumer) it++; continue; } - + SWSS_LOG_NOTICE("Set port %s speed from %u to %u", alias.c_str(), p.m_speed, speed); p.m_speed = speed; m_portList[alias] = p; @@ -2882,7 +2983,7 @@ void PortsOrch::doPortTask(Consumer &consumer) auto ori_adv_speeds = swss::join(',', p.m_adv_speeds.begin(), p.m_adv_speeds.end()); if (!setPortAdvSpeeds(p.m_port_id, adv_speeds)) { - + SWSS_LOG_ERROR("Failed to set port %s advertised speed from %s to %s", alias.c_str(), ori_adv_speeds.c_str(), adv_speeds_str.c_str()); @@ -3676,6 +3777,15 @@ void PortsOrch::doLagMemberTask(Consumer &consumer) continue; } + /* Fail if a port type is not a valid type for being a LAG member port. + * Erase invalid entry, no need to retry in this case. */ + if (!isValidPortTypeForLagMember(port)) + { + SWSS_LOG_ERROR("LAG member port has to be of type PHY or SYSTEM"); + it = consumer.m_toSync.erase(it); + continue; + } + if (table_name == CHASSIS_APP_LAG_MEMBER_TABLE_NAME) { int32_t lag_switch_id = lag.m_system_lag_info.switch_id; @@ -3709,8 +3819,12 @@ void PortsOrch::doLagMemberTask(Consumer &consumer) if (lag.m_members.find(port_alias) == lag.m_members.end()) { - /* Assert the port doesn't belong to any LAG already */ - assert(!port.m_lag_id && !port.m_lag_member_id); + if (port.m_lag_member_id != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Port %s is already a LAG member", port.m_alias.c_str()); + it++; + continue; + } if (!addLagMember(lag, port, (status == "enabled"))) { @@ -3860,7 +3974,11 @@ void PortsOrch::initializeQueues(Port &port) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get number of queues for port %s rv:%d", port.m_alias.c_str(), status); - throw runtime_error("PortsOrch initialization failure."); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("PortsOrch initialization failure."); + } } SWSS_LOG_INFO("Get %d queues for port %s", attr.value.u32, port.m_alias.c_str()); @@ -3880,7 +3998,11 @@ void PortsOrch::initializeQueues(Port &port) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get queue list for port %s rv:%d", port.m_alias.c_str(), status); - throw runtime_error("PortsOrch initialization failure."); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("PortsOrch initialization failure."); + } } SWSS_LOG_INFO("Get queues for port %s", port.m_alias.c_str()); @@ -3896,7 +4018,11 @@ void PortsOrch::initializePriorityGroups(Port &port) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get number of priority groups for port %s rv:%d", port.m_alias.c_str(), status); - throw runtime_error("PortsOrch initialization failure."); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("PortsOrch initialization failure."); + } } SWSS_LOG_INFO("Get %d priority groups for port %s", attr.value.u32, port.m_alias.c_str()); @@ -3917,7 +4043,11 @@ void PortsOrch::initializePriorityGroups(Port &port) if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Fail to get priority group list for port %s rv:%d", port.m_alias.c_str(), status); - throw runtime_error("PortsOrch initialization failure."); + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + throw runtime_error("PortsOrch initialization failure."); + } } SWSS_LOG_INFO("Get priority groups for port %s", port.m_alias.c_str()); } @@ -4173,6 +4303,9 @@ bool PortsOrch::addBridgePort(Port &port) m_portList[port.m_alias] = port; SWSS_LOG_NOTICE("Add bridge port %s to default 1Q bridge", port.m_alias.c_str()); + PortUpdate update = { port, true }; + notify(SUBJECT_TYPE_BRIDGE_PORT_CHANGE, static_cast(&update)); + return true; } @@ -4226,6 +4359,10 @@ bool PortsOrch::removeBridgePort(Port &port) } port.m_bridge_port_id = SAI_NULL_OBJECT_ID; + /* Remove bridge port */ + PortUpdate update = { port, false }; + notify(SUBJECT_TYPE_BRIDGE_PORT_CHANGE, static_cast(&update)); + SWSS_LOG_NOTICE("Remove bridge port %s from default 1Q bridge", port.m_alias.c_str()); m_portList[port.m_alias] = port; @@ -4278,8 +4415,10 @@ bool PortsOrch::addVlan(string vlan_alias) sai_vlan_id_t vlan_id = (uint16_t)stoi(vlan_alias.substr(4)); sai_attribute_t attr; + attr.id = SAI_VLAN_ATTR_VLAN_ID; attr.value.u16 = vlan_id; + sai_status_t status = sai_vlan_api->create_vlan(&vlan_oid, gSwitchId, 1, &attr); if (status != SAI_STATUS_SUCCESS) @@ -4297,6 +4436,8 @@ bool PortsOrch::addVlan(string vlan_alias) Port vlan(vlan_alias, Port::VLAN); vlan.m_vlan_info.vlan_oid = vlan_oid; vlan.m_vlan_info.vlan_id = vlan_id; + vlan.m_vlan_info.uuc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + vlan.m_vlan_info.bc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; vlan.m_members = set(); m_portList[vlan_alias] = vlan; m_port_ref_count[vlan_alias] = 0; @@ -4377,7 +4518,128 @@ bool PortsOrch::getVlanByVlanId(sai_vlan_id_t vlan_id, Port &vlan) return false; } -bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode) +bool PortsOrch::addVlanFloodGroups(Port &vlan, Port &port, string end_point_ip) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t l2mc_group_id = SAI_NULL_OBJECT_ID; + sai_status_t status; + sai_attribute_t attr; + + if (vlan.m_vlan_info.uuc_flood_type != SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_CONTROL_TYPE; + attr.value.s32 = SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set l2mc flood type combined " + " to vlan %hu for unknown unicast flooding", vlan.m_vlan_info.vlan_id); + return false; + } + vlan.m_vlan_info.uuc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED; + } + + if (vlan.m_vlan_info.bc_flood_type != SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE; + attr.value.s32 = SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set l2mc flood type combined " + " to vlan %hu for broadcast flooding", vlan.m_vlan_info.vlan_id); + return false; + } + vlan.m_vlan_info.bc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED; + } + + if (vlan.m_vlan_info.l2mc_group_id == SAI_NULL_OBJECT_ID) + { + status = sai_l2mc_group_api->create_l2mc_group(&l2mc_group_id, gSwitchId, 0, NULL); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create l2mc flood group"); + return false; + } + + if (vlan.m_vlan_info.uuc_flood_type == SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_GROUP; + attr.value.oid = l2mc_group_id; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set l2mc group %" PRIx64 + " to vlan %hu for unknown unicast flooding", + l2mc_group_id, vlan.m_vlan_info.vlan_id); + return false; + } + } + if (vlan.m_vlan_info.bc_flood_type == SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_BROADCAST_FLOOD_GROUP; + attr.value.oid = l2mc_group_id; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set l2mc group %" PRIx64 + " to vlan %hu for broadcast flooding", + l2mc_group_id, vlan.m_vlan_info.vlan_id); + return false; + } + } + vlan.m_vlan_info.l2mc_group_id = l2mc_group_id; + m_portList[vlan.m_alias] = vlan; + } + + vector attrs; + attr.id = SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_GROUP_ID; + attr.value.oid = vlan.m_vlan_info.l2mc_group_id; + attrs.push_back(attr); + + attr.id = SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_OUTPUT_ID; + attr.value.oid = port.m_bridge_port_id; + attrs.push_back(attr); + + attr.id = SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_ENDPOINT_IP; + IpAddress remote = IpAddress(end_point_ip); + sai_ip_address_t ipaddr; + if (remote.isV4()) + { + ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + ipaddr.addr.ip4 = remote.getV4Addr(); + } + else + { + ipaddr.addr_family = SAI_IP_ADDR_FAMILY_IPV6; + memcpy(ipaddr.addr.ip6, remote.getV6Addr(), sizeof(ipaddr.addr.ip6)); + } + attr.value.ipaddr = ipaddr; + attrs.push_back(attr); + + sai_object_id_t l2mc_group_member = SAI_NULL_OBJECT_ID; + status = sai_l2mc_group_api->create_l2mc_group_member(&l2mc_group_member, gSwitchId, + static_cast(attrs.size()), + attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create l2mc group member for adding tunnel %s to vlan %hu", + end_point_ip.c_str(), vlan.m_vlan_info.vlan_id); + return false; + } + vlan.m_vlan_info.l2mc_members[end_point_ip] = l2mc_group_member; + m_portList[vlan.m_alias] = vlan; + increaseBridgePortRefCount(port); + return true; +} + +bool PortsOrch::createVlanMember(Port &vlan, Port &port, sai_vlan_tagging_mode_t &sai_tagging_mode, + sai_object_id_t &vlan_member_id) { SWSS_LOG_ENTER(); @@ -4392,19 +4654,10 @@ bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode) attr.value.oid = port.m_bridge_port_id; attrs.push_back(attr); - sai_vlan_tagging_mode_t sai_tagging_mode = SAI_VLAN_TAGGING_MODE_TAGGED; attr.id = SAI_VLAN_MEMBER_ATTR_VLAN_TAGGING_MODE; - if (tagging_mode == "untagged") - sai_tagging_mode = SAI_VLAN_TAGGING_MODE_UNTAGGED; - else if (tagging_mode == "tagged") - sai_tagging_mode = SAI_VLAN_TAGGING_MODE_TAGGED; - else if (tagging_mode == "priority_tagged") - sai_tagging_mode = SAI_VLAN_TAGGING_MODE_PRIORITY_TAGGED; - else assert(false); attr.value.s32 = sai_tagging_mode; attrs.push_back(attr); - sai_object_id_t vlan_member_id; sai_status_t status = sai_vlan_api->create_vlan_member(&vlan_member_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { @@ -4418,6 +4671,40 @@ bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode) } SWSS_LOG_NOTICE("Add member %s to VLAN %s vid:%hu pid%" PRIx64, port.m_alias.c_str(), vlan.m_alias.c_str(), vlan.m_vlan_info.vlan_id, port.m_port_id); + return true; + +} + +bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode, string end_point_ip) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t vlan_member_id; + if (!end_point_ip.empty()) + { + if ((uuc_sup_flood_control_type.find(SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + == uuc_sup_flood_control_type.end()) || + (bc_sup_flood_control_type.find(SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + == bc_sup_flood_control_type.end())) + { + SWSS_LOG_ERROR("Flood group with end point ip is not supported"); + return false; + } + return addVlanFloodGroups(vlan, port, end_point_ip); + } + + sai_vlan_tagging_mode_t sai_tagging_mode = SAI_VLAN_TAGGING_MODE_TAGGED; + if (tagging_mode == "untagged") + sai_tagging_mode = SAI_VLAN_TAGGING_MODE_UNTAGGED; + else if (tagging_mode == "tagged") + sai_tagging_mode = SAI_VLAN_TAGGING_MODE_TAGGED; + else if (tagging_mode == "priority_tagged") + sai_tagging_mode = SAI_VLAN_TAGGING_MODE_PRIORITY_TAGGED; + else assert(false); + if(!createVlanMember(vlan, port, sai_tagging_mode, vlan_member_id)) + { + return false; + } /* Use untagged VLAN as pvid of the member port */ if (sai_tagging_mode == SAI_VLAN_TAGGING_MODE_UNTAGGED) @@ -4441,10 +4728,102 @@ bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode) return true; } -bool PortsOrch::removeVlanMember(Port &vlan, Port &port) +bool PortsOrch::removeVlanEndPointIp(Port &vlan, Port &port, string end_point_ip) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + if(vlan.m_vlan_info.l2mc_members.find(end_point_ip) == vlan.m_vlan_info.l2mc_members.end()) + { + SWSS_LOG_NOTICE("End point ip %s is not part of vlan %hu", + end_point_ip.c_str(), vlan.m_vlan_info.vlan_id); + return true; + } + + status = sai_l2mc_group_api->remove_l2mc_group_member(vlan.m_vlan_info.l2mc_members[end_point_ip]); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove end point ip %s from vlan %hu", + end_point_ip.c_str(), vlan.m_vlan_info.vlan_id); + return false; + } + decreaseBridgePortRefCount(port); + vlan.m_vlan_info.l2mc_members.erase(end_point_ip); + sai_object_id_t l2mc_group_id = SAI_NULL_OBJECT_ID; + sai_attribute_t attr; + + if (vlan.m_vlan_info.l2mc_members.empty()) + { + if (vlan.m_vlan_info.uuc_flood_type == SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_GROUP; + attr.value.oid = SAI_NULL_OBJECT_ID; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set null l2mc group " + " to vlan %hu for unknown unicast flooding", + vlan.m_vlan_info.vlan_id); + return false; + } + attr.id = SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_CONTROL_TYPE; + attr.value.s32 = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set flood control type all" + " to vlan %hu for unknown unicast flooding", + vlan.m_vlan_info.vlan_id); + return false; + } + vlan.m_vlan_info.uuc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + } + if (vlan.m_vlan_info.bc_flood_type == SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED) + { + attr.id = SAI_VLAN_ATTR_BROADCAST_FLOOD_GROUP; + attr.value.oid = SAI_NULL_OBJECT_ID; + + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set null l2mc group " + " to vlan %hu for broadcast flooding", + vlan.m_vlan_info.vlan_id); + return false; + } + attr.id = SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE; + attr.value.s32 = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + status = sai_vlan_api->set_vlan_attribute(vlan.m_vlan_info.vlan_oid, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set flood control type all" + " to vlan %hu for broadcast flooding", + vlan.m_vlan_info.vlan_id); + return false; + } + vlan.m_vlan_info.bc_flood_type = SAI_VLAN_FLOOD_CONTROL_TYPE_ALL; + } + status = sai_l2mc_group_api->remove_l2mc_group(vlan.m_vlan_info.l2mc_group_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove l2mc group %" PRIx64, l2mc_group_id); + return false; + } + vlan.m_vlan_info.l2mc_group_id = SAI_NULL_OBJECT_ID; + } + return true; +} + +bool PortsOrch::removeVlanMember(Port &vlan, Port &port, string end_point_ip) { SWSS_LOG_ENTER(); + if (!end_point_ip.empty()) + { + return removeVlanEndPointIp(vlan, port, end_point_ip); + } sai_object_id_t vlan_member_id; sai_vlan_tagging_mode_t sai_tagging_mode; auto vlan_member = port.m_vlan_members.find(vlan.m_vlan_info.vlan_id); @@ -4488,8 +4867,16 @@ bool PortsOrch::removeVlanMember(Port &vlan, Port &port) return true; } -bool PortsOrch::isVlanMember(Port &vlan, Port &port) +bool PortsOrch::isVlanMember(Port &vlan, Port &port, string end_point_ip) { + if (!end_point_ip.empty()) + { + if (vlan.m_vlan_info.l2mc_members.find(end_point_ip) != vlan.m_vlan_info.l2mc_members.end()) + { + return true; + } + return false; + } if (vlan.m_members.find(port.m_alias) == vlan.m_members.end()) return false; @@ -5413,7 +5800,11 @@ bool PortsOrch::setPortSerdesAttribute(sai_object_id_t port_id, { SWSS_LOG_ERROR("Failed to get port attr serdes id %d to port pid:0x%" PRIx64, port_attr.id, port_id); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } if (port_attr.value.oid != SAI_NULL_OBJECT_ID) @@ -5510,7 +5901,7 @@ void PortsOrch::getPortSerdesVal(const std::string& val_str, } } -bool PortsOrch::getPortAdvSpeedsVal(const std::string &val_str, +bool PortsOrch::getPortAdvSpeedsVal(const std::string &val_str, std::vector &speed_values) { SWSS_LOG_ENTER(); @@ -5524,7 +5915,7 @@ bool PortsOrch::getPortAdvSpeedsVal(const std::string &val_str, std::string speed_str; std::istringstream iss(val_str); - try + try { while (std::getline(iss, speed_str, ',')) { @@ -5541,31 +5932,31 @@ bool PortsOrch::getPortAdvSpeedsVal(const std::string &val_str, return true; } -bool PortsOrch::getPortInterfaceTypeVal(const std::string &s, +bool PortsOrch::getPortInterfaceTypeVal(const std::string &s, sai_port_interface_type_t &interface_type) { SWSS_LOG_ENTER(); auto iter = interface_type_map_for_an.find(s); - if (iter != interface_type_map_for_an.end()) + if (iter != interface_type_map_for_an.end()) { interface_type = interface_type_map_for_an[s]; return true; } - else + else { const std::string &validInterfaceTypes = getValidInterfaceTypes(); - SWSS_LOG_ERROR("Failed to parse interface_type value %s, valid interface type includes: %s", + SWSS_LOG_ERROR("Failed to parse interface_type value %s, valid interface type includes: %s", s.c_str(), validInterfaceTypes.c_str()); return false; } } -bool PortsOrch::getPortAdvInterfaceTypesVal(const std::string &val_str, +bool PortsOrch::getPortAdvInterfaceTypesVal(const std::string &val_str, std::vector &type_values) { SWSS_LOG_ENTER(); - if (val_str == "all") + if (val_str == "all") { return true; } @@ -5580,7 +5971,7 @@ bool PortsOrch::getPortAdvInterfaceTypesVal(const std::string &val_str, valid = getPortInterfaceTypeVal(type_str, interface_type); if (!valid) { const std::string &validInterfaceTypes = getValidInterfaceTypes(); - SWSS_LOG_ERROR("Failed to parse adv_interface_types value %s, valid interface type includes: %s", + SWSS_LOG_ERROR("Failed to parse adv_interface_types value %s, valid interface type includes: %s", val_str.c_str(), validInterfaceTypes.c_str()); return false; } @@ -5907,7 +6298,11 @@ bool PortsOrch::getSystemPorts() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get system port list, rv:%d", status); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SWITCH, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } uint32_t spcnt = attr.value.objlist.count; @@ -5919,7 +6314,11 @@ bool PortsOrch::getSystemPorts() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get system port config info spid:%" PRIx64, system_port_list[i]); - return false; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SYSTEM_PORT, status); + if (handle_status != task_process_status::task_success) + { + return false; + } } SWSS_LOG_NOTICE("SystemPort(0x%" PRIx64 ") - port_id:%u, switch_id:%u, core:%u, core_port:%u, speed:%u, voqs:%u", @@ -5955,80 +6354,6 @@ bool PortsOrch::getRecircPort(Port &port, string role) return false; } -bool PortsOrch::doProcessRecircPort(string alias, string role, set lane_set, string op) -{ - SWSS_LOG_ENTER(); - - if (op == SET_COMMAND) - { - if (m_recircPortRole.find(alias) != m_recircPortRole.end()) - { - SWSS_LOG_DEBUG("Recirc port %s already added", alias.c_str()); - return true; - } - - /* Find pid of recirc port */ - sai_object_id_t port_id = SAI_NULL_OBJECT_ID; - if (m_portListLaneMap.find(lane_set) != m_portListLaneMap.end()) - { - port_id = m_portListLaneMap[lane_set]; - } - - if (port_id == SAI_NULL_OBJECT_ID) - { - SWSS_LOG_ERROR("Failed to find port id for recirc port %s", alias.c_str()); - return false; - } - - Port p(alias, Port::PHY); - p.m_port_id = port_id; - p.m_init = true; - m_recircPortRole[alias] = role; - setPort(alias, p); - - string lane_str = ""; - for (auto lane : lane_set) - { - lane_str += to_string(lane) + " "; - } - SWSS_LOG_NOTICE("Added recirc port %s, pid:%" PRIx64 " lanes:%s", - alias.c_str(), port_id, lane_str.c_str()); - - /* Create host intf for recirc port */ - if(addHostIntfs(p, p.m_alias, p.m_hif_id)) - { - SWSS_LOG_NOTICE("Created host intf for recycle port %s", p.m_alias.c_str()); - } - else - { - SWSS_LOG_ERROR("Failed to Create host intf for recirc port %s", p.m_alias.c_str()); - } - - if(setHostIntfsOperStatus(p, true)) - { - SWSS_LOG_NOTICE("Set host intf oper status UP for recirc port %s", p.m_alias.c_str()); - } - else - { - SWSS_LOG_ERROR("Failed to set host intf oper status for recirc port %s", p.m_alias.c_str()); - } - - PortUpdate update = { p, true }; - notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); - return true; - } - else if (op == DEL_COMMAND) - { - SWSS_LOG_ERROR("Delete recirc port is not supported."); - return false; - } - else - { - SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); - return false; - } -} - bool PortsOrch::addSystemPorts() { vector keys; @@ -6102,7 +6427,11 @@ bool PortsOrch::addSystemPorts() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get system port config info spid:%" PRIx64, system_port_oid); - continue; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SYSTEM_PORT, status); + if (handle_status != task_process_status::task_success) + { + continue; + } } //Create or update system port and add to the port list. @@ -6121,7 +6450,11 @@ bool PortsOrch::addSystemPorts() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to get local port oid of local system port spid:%" PRIx64, system_port_oid); - continue; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SYSTEM_PORT, status); + if (handle_status != task_process_status::task_success) + { + continue; + } } //System port for local port. Update the system port info in the existing physical port diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index c29b59837f..2a699193fb 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -142,9 +142,15 @@ class PortsOrch : public Orch, public Subject bool removeTunnel(Port tunnel); bool addBridgePort(Port &port); bool removeBridgePort(Port &port); - bool addVlanMember(Port &vlan, Port &port, string& tagging_mode); - bool removeVlanMember(Port &vlan, Port &port); - bool isVlanMember(Port &vlan, Port &port); + bool addVlanMember(Port &vlan, Port &port, string& tagging_mode, string end_point_ip = ""); + bool removeVlanMember(Port &vlan, Port &port, string end_point_ip = ""); + bool isVlanMember(Port &vlan, Port &port, string end_point_ip = ""); + bool addVlanFloodGroups(Port &vlan, Port &port, string end_point_ip); + bool createVlanMember(Port &vlan, Port &port, sai_vlan_tagging_mode_t &sai_tagging_mode, sai_object_id_t &vlan_member_id); + bool removeVlanEndPointIp(Port &vlan, Port &port, string end_point_ip); + void increaseBridgePortRefCount(Port &port); + void decreaseBridgePortRefCount(Port &port); + bool getBridgePortReferenceCount(Port &port); string m_inbandPortName = ""; bool isInbandPort(const string &alias); @@ -219,6 +225,10 @@ class PortsOrch : public Orch, public Subject unordered_map m_portOidToIndex; map m_port_ref_count; unordered_set m_pendingPortSet; + const uint32_t max_flood_control_types = 4; + set uuc_sup_flood_control_type; + set bc_sup_flood_control_type; + map m_bridge_port_ref_count; NotificationConsumer* m_portStatusNotificationConsumer; @@ -324,7 +334,6 @@ class PortsOrch : public Orch, public Subject bool initGearboxPort(Port &port); map m_recircPortRole; - bool doProcessRecircPort(string alias, string role, set laneSet, string op); //map key is tuple of map, sai_object_id_t> m_systemPortOidMap; diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp index 31e61b5433..c2e15aa763 100644 --- a/orchagent/qosorch.cpp +++ b/orchagent/qosorch.cpp @@ -933,7 +933,11 @@ sai_object_id_t QosOrch::getSchedulerGroup(const Port &port, const sai_object_id if (SAI_STATUS_SUCCESS != sai_status) { SWSS_LOG_ERROR("Failed to get number of scheduler groups for port:%s", port.m_alias.c_str()); - return SAI_NULL_OBJECT_ID; + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, sai_status); + if (handle_status != task_process_status::task_success) + { + return SAI_NULL_OBJECT_ID; + } } /* Get total groups list on the port */ @@ -947,7 +951,11 @@ sai_object_id_t QosOrch::getSchedulerGroup(const Port &port, const sai_object_id if (SAI_STATUS_SUCCESS != sai_status) { SWSS_LOG_ERROR("Failed to get scheduler group list for port:%s", port.m_alias.c_str()); - return SAI_NULL_OBJECT_ID; + task_process_status handle_status = handleSaiGetStatus(SAI_API_PORT, sai_status); + if (handle_status != task_process_status::task_success) + { + return SAI_NULL_OBJECT_ID; + } } m_scheduler_group_port_info[port.m_port_id] = { @@ -969,7 +977,11 @@ sai_object_id_t QosOrch::getSchedulerGroup(const Port &port, const sai_object_id if (SAI_STATUS_SUCCESS != sai_status) { SWSS_LOG_ERROR("Failed to get child count for scheduler group:0x%" PRIx64 " of port:%s", group_id, port.m_alias.c_str()); - return SAI_NULL_OBJECT_ID; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SCHEDULER_GROUP, sai_status); + if (handle_status != task_process_status::task_success) + { + return SAI_NULL_OBJECT_ID; + } } uint32_t child_count = attr.value.u32; @@ -988,7 +1000,11 @@ sai_object_id_t QosOrch::getSchedulerGroup(const Port &port, const sai_object_id if (SAI_STATUS_SUCCESS != sai_status) { SWSS_LOG_ERROR("Failed to get child list for scheduler group:0x%" PRIx64 " of port:%s", group_id, port.m_alias.c_str()); - return SAI_NULL_OBJECT_ID; + task_process_status handle_status = handleSaiGetStatus(SAI_API_SCHEDULER_GROUP, sai_status); + if (handle_status != task_process_status::task_success) + { + return SAI_NULL_OBJECT_ID; + } } m_scheduler_group_port_info[port.m_port_id].child_groups[ii] = std::move(child_groups); diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 0f688a64d0..ae4998fde7 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -127,15 +127,15 @@ RouteOrch::RouteOrch(DBConnector *db, vector &tableNames, IpPrefix linklocal_prefix = getLinkLocalEui64Addr(); addLinkLocalRouteToMe(gVirtualRouterId, linklocal_prefix); + SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu", linklocal_prefix.to_string().c_str()); /* Add fe80::/10 subnet route to forward all link-local packets * destined to us, to CPU */ IpPrefix default_link_local_prefix("fe80::/10"); addLinkLocalRouteToMe(gVirtualRouterId, default_link_local_prefix); + SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu", default_link_local_prefix.to_string().c_str()); - /* TODO: Add the link-local fe80::/10 route to cpu in every VRF created from - * vrforch::addOperation. */ } std::string RouteOrch::getLinkLocalEui64Addr(void) @@ -205,6 +205,27 @@ void RouteOrch::addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu", linklocal_prefix.to_string().c_str()); } +void RouteOrch::delLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix) +{ + sai_route_entry_t unicast_route_entry; + unicast_route_entry.switch_id = gSwitchId; + unicast_route_entry.vr_id = vrf_id; + copy(unicast_route_entry.destination, linklocal_prefix); + subnet(unicast_route_entry.destination, unicast_route_entry.destination); + + sai_status_t status = sai_route_api->remove_route_entry(&unicast_route_entry); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to delete link local ipv6 route %s to cpu, rv:%d", + linklocal_prefix.getIp().to_string().c_str(), status); + return; + } + + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + + SWSS_LOG_NOTICE("Deleted link local ipv6 route %s to cpu", linklocal_prefix.to_string().c_str()); +} + bool RouteOrch::hasNextHopGroup(const NextHopGroupKey& nexthops) const { return m_syncdNextHopGroups.find(nexthops) != m_syncdNextHopGroups.end(); @@ -2048,7 +2069,7 @@ bool RouteOrch::createRemoteVtep(sai_object_id_t vrf_id, const NextHopKey &nextH auto vtep_ptr = evpn_orch->getEVPNVtep(); if (vtep_ptr) { - ip_refcnt = vtep_ptr->getDipTunnelIPRefCnt(nextHop.ip_address.to_string()); + ip_refcnt = vtep_ptr->getRemoteEndPointIPRefCnt(nextHop.ip_address.to_string()); } SWSS_LOG_INFO("Routeorch Add Remote VTEP %s, VNI %d, VR_ID %" PRIx64 ", IP ref_cnt %d", nextHop.ip_address.to_string().c_str(), nextHop.vni, vrf_id, ip_refcnt); @@ -2068,7 +2089,7 @@ bool RouteOrch::deleteRemoteVtep(sai_object_id_t vrf_id, const NextHopKey &nextH auto vtep_ptr = evpn_orch->getEVPNVtep(); if (vtep_ptr) { - ip_refcnt = vtep_ptr->getDipTunnelIPRefCnt(nextHop.ip_address.to_string()); + ip_refcnt = vtep_ptr->getRemoteEndPointIPRefCnt(nextHop.ip_address.to_string()); } SWSS_LOG_INFO("Routeorch Del Remote VTEP %s, VNI %d, VR_ID %" PRIx64 ", IP ref_cnt %d", diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index aca68624f6..20e79699d5 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -152,6 +152,10 @@ class RouteOrch : public Orch, public Subject bool createFineGrainedNextHopGroup(sai_object_id_t &next_hop_group_id, vector &nhg_attrs); bool removeFineGrainedNextHopGroup(sai_object_id_t &next_hop_group_id); + void addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix); + void delLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix); + std::string getLinkLocalEui64Addr(void); + private: SwitchOrch *m_switchOrch; NeighOrch *m_neighOrch; @@ -188,9 +192,6 @@ class RouteOrch : public Orch, public Subject bool addLabelRoutePost(const LabelRouteBulkContext& ctx, const NextHopGroupKey &nextHops); bool removeLabelRoutePost(const LabelRouteBulkContext& ctx); - std::string getLinkLocalEui64Addr(void); - void addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal_prefix); - void doTask(Consumer& consumer); void doLabelTask(Consumer& consumer); }; diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index dac7b1e92f..2779fb85ff 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -62,8 +62,10 @@ sai_dtel_api_t* sai_dtel_api; sai_samplepacket_api_t* sai_samplepacket_api; sai_debug_counter_api_t* sai_debug_counter_api; sai_nat_api_t* sai_nat_api; +sai_isolation_group_api_t* sai_isolation_group_api; sai_system_port_api_t* sai_system_port_api; sai_macsec_api_t* sai_macsec_api; +sai_l2mc_group_api_t* sai_l2mc_group_api; extern sai_object_id_t gSwitchId; extern bool gSairedisRecord; @@ -183,8 +185,10 @@ void initSaiApi() sai_api_query(SAI_API_SAMPLEPACKET, (void **)&sai_samplepacket_api); sai_api_query(SAI_API_DEBUG_COUNTER, (void **)&sai_debug_counter_api); sai_api_query(SAI_API_NAT, (void **)&sai_nat_api); + sai_api_query(SAI_API_ISOLATION_GROUP, (void **)&sai_isolation_group_api); sai_api_query(SAI_API_SYSTEM_PORT, (void **)&sai_system_port_api); sai_api_query(SAI_API_MACSEC, (void **)&sai_macsec_api); + sai_api_query(SAI_API_L2MC_GROUP, (void **)&sai_l2mc_group_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -216,6 +220,7 @@ void initSaiApi() sai_log_set((sai_api_t)SAI_API_NAT, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_SYSTEM_PORT, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_MACSEC, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_L2MC_GROUP, SAI_LOG_LEVEL_NOTICE); } void initSaiRedis(const string &record_location, const std::string &record_filename) @@ -418,7 +423,7 @@ sai_status_t initSaiPhyApi(swss::gearbox_phy_t *phy) /* Must be last Attribute */ attr.id = SAI_REDIS_SWITCH_ATTR_CONTEXT; - attr.value.u64 = phy->phy_id; + attr.value.u64 = phy->context_id; attrs.push_back(attr); status = sai_switch_api->create_switch(&phyOid, (uint32_t)attrs.size(), attrs.data()); diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index 6e25454f13..61d4c8229e 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -15,6 +15,7 @@ #include "swssnet.h" #include "warm_restart.h" #include "tokenize.h" +#include "converter.h" /* Global variables */ extern sai_object_id_t gSwitchId; @@ -906,7 +907,10 @@ bool VxlanTunnel::createTunnelHw(uint8_t mapper_list, tunnel_map_use_t map_src, void VxlanTunnel::deletePendingSIPTunnel() { - if ((getDipTunnelCnt() == 0) && del_tnl_hw_pending) + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + bool dip_tunnels_used = tunnel_orch->dipTunnelsUsed(); + + if ((!dip_tunnels_used || getDipTunnelCnt() == 0) && del_tnl_hw_pending) { uint8_t mapper_list=0; @@ -994,7 +998,7 @@ int VxlanTunnel::getDipTunnelIMRRefCnt(const std::string remote_vtep) } } -int VxlanTunnel::getDipTunnelIPRefCnt(const std::string remote_vtep) +int VxlanTunnel::getRemoteEndPointIPRefCnt(const std::string remote_vtep) { tunnel_refcnt_t tnl_refcnts; @@ -1058,6 +1062,43 @@ void VxlanTunnel::updateDipTunnelRefCnt(bool inc, tunnel_refcnt_t& tnl_refcnts, } } +void VxlanTunnel::updateRemoteEndPointIpRef(const std::string remote_vtep, bool inc) +{ + tunnel_refcnt_t tnl_refcnts; + + auto it = tnl_users_.find(remote_vtep); + if (inc) + { + if (it == tnl_users_.end()) + { + memset(&tnl_refcnts, 0, sizeof(tunnel_refcnt_t)); + tnl_refcnts.ip_refcnt++; + tnl_users_[remote_vtep] = tnl_refcnts; + } + else + { + it->second.ip_refcnt++; + } + SWSS_LOG_DEBUG("Incrementing remote end point %s reference to %d", remote_vtep.c_str(), + it->second.ip_refcnt); + } + else + { + if (it == tnl_users_.end()) + { + SWSS_LOG_ERROR("Cannot decrement ref. End point not referenced %s", remote_vtep.c_str()); + } + it->second.ip_refcnt--; + + SWSS_LOG_DEBUG("Decrementing remote end point %s reference to %d", remote_vtep.c_str(), + it->second.ip_refcnt); + if (it->second.ip_refcnt == 0) + { + tnl_users_.erase(remote_vtep); + } + } +} + bool VxlanTunnel::createDynamicDIPTunnel(const std::string dip, tunnel_user_t usr) { uint8_t mapper_list = 0; @@ -1439,6 +1480,12 @@ bool VxlanTunnelOrch::addTunnelUser(const std::string remote_vtep, uint32_t vni return false; } + if (!dipTunnelsUsed() && usr == TUNNEL_USER_IP) + { + vtep_ptr->updateRemoteEndPointIpRef(remote_vtep, true); + return true; + } + vtep_ptr->createDynamicDIPTunnel(remote_vtep, usr); getTunnelNameFromDIP(remote_vtep, tunnel_name); @@ -1479,12 +1526,29 @@ bool VxlanTunnelOrch::delTunnelUser(const std::string remote_vtep, uint32_t vni Port tunnelPort; gPortsOrch->getPort(port_tunnel_name,tunnelPort); + bool ret; + + if (!dipTunnelsUsed()) + { + vtep_ptr->updateRemoteEndPointIpRef(remote_vtep, false); + if (vtep_ptr->del_tnl_hw_pending && !vtep_ptr->isTunnelReferenced()) + { + ret = gPortsOrch->removeBridgePort(tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Remove Bridge port failed for source vtep = %s fdbcount = %d", + port_tunnel_name.c_str(), tunnelPort.m_fdb_count); + return true; + } + gPortsOrch->removeTunnel(tunnelPort); + vtep_ptr->deletePendingSIPTunnel(); + } + return true; + } if ((vtep_ptr->getDipTunnelRefCnt(remote_vtep) == 1) && tunnelPort.m_fdb_count == 0) { - bool ret; - ret = gPortsOrch->removeBridgePort(tunnelPort); if (!ret) { @@ -1520,6 +1584,22 @@ void VxlanTunnelOrch::deleteTunnelPort(Port &tunnelPort) return; } + if (isSrcVtepTunnel(tunnelPort)) + { + if (vtep_ptr->del_tnl_hw_pending && !vtep_ptr->isTunnelReferenced()) + { + ret = gPortsOrch->removeBridgePort(tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Remove Bridge port failed for source vtep = %s fdbcount = %d", + tunnelPort.m_alias.c_str(), tunnelPort.m_fdb_count); + return; + } + gPortsOrch->removeTunnel(tunnelPort); + vtep_ptr->deletePendingSIPTunnel(); + } + return; + } getTunnelDIPFromPort(tunnelPort, remote_vtep); //If there are IMR/IP routes to the remote VTEP then ignore this call @@ -1527,7 +1607,7 @@ void VxlanTunnelOrch::deleteTunnelPort(Port &tunnelPort) if (refcnt > 0) { SWSS_LOG_INFO("Tunnel bridge port not removed. remote = %s refcnt = %d", - remote_vtep.c_str(), refcnt); + remote_vtep.c_str(), refcnt); return; } @@ -1536,7 +1616,7 @@ void VxlanTunnelOrch::deleteTunnelPort(Port &tunnelPort) if (!ret) { SWSS_LOG_ERROR("Remove Bridge port failed for remote = %s fdbcount = %d", - remote_vtep.c_str(), tunnelPort.m_fdb_count); + remote_vtep.c_str(), tunnelPort.m_fdb_count); return; } gPortsOrch->removeTunnel(tunnelPort); @@ -1544,23 +1624,39 @@ void VxlanTunnelOrch::deleteTunnelPort(Port &tunnelPort) // Remove DIP Tunnel HW vtep_ptr->deleteDynamicDIPTunnel(remote_vtep, TUNNEL_USER_IMR, false); SWSS_LOG_NOTICE("diprefcnt for remote %s = %d", - remote_vtep.c_str(), vtep_ptr->getDipTunnelRefCnt(remote_vtep)); - + remote_vtep.c_str(), vtep_ptr->getDipTunnelRefCnt(remote_vtep)); // Remove SIP Tunnel HW which might be pending on delete vtep_ptr->deletePendingSIPTunnel(); return ; } -std::string VxlanTunnelOrch::getTunnelPortName(const std::string& remote_vtep) +std::string VxlanTunnelOrch::getTunnelPortName(const std::string& vtep, bool local) { - std::string tunnelPortName = "Port_EVPN_" + remote_vtep; + + std::string tunnelPortName; + if (local) + { + tunnelPortName = LOCAL_TUNNEL_PORT_PREFIX + vtep; + } + else + { + tunnelPortName = EVPN_TUNNEL_PORT_PREFIX + vtep; + } return tunnelPortName; } +bool VxlanTunnelOrch::isSrcVtepTunnel(Port& tunnelPort) +{ + string tunnel_port_name = tunnelPort.m_alias; + string prefix = LOCAL_TUNNEL_PORT_PREFIX; + return (tunnel_port_name.compare(0, prefix.length(), prefix) == 0); +} + + void VxlanTunnelOrch::getTunnelNameFromDIP(const string& dip, string& tunnel_name) { - tunnel_name = "EVPN_" + dip; + tunnel_name = EVPN_TUNNEL_NAME_PREFIX + dip; return; } @@ -1577,7 +1673,7 @@ void VxlanTunnelOrch::getTunnelNameFromPort(string& tunnel_portname, string& tun void VxlanTunnelOrch:: getTunnelDIPFromPort(Port& tunnelPort, string& remote_vtep) { remote_vtep = tunnelPort.m_alias; - remote_vtep.erase(0,sizeof("Port_EVPN_")-1); + remote_vtep.erase(0,sizeof(EVPN_TUNNEL_PORT_PREFIX)-1); } @@ -1645,9 +1741,9 @@ void VxlanTunnelOrch::addRemoveStateTableEntry(string tunnel_name, } } -bool VxlanTunnelOrch::getTunnelPort(const std::string& remote_vtep,Port& tunnelPort) +bool VxlanTunnelOrch::getTunnelPort(const std::string& vtep,Port& tunnelPort, bool local) { - auto port_tunnel_name = getTunnelPortName(remote_vtep); + auto port_tunnel_name = getTunnelPortName(vtep, local); bool ret = gPortsOrch->getPort(port_tunnel_name,tunnelPort); @@ -1657,6 +1753,49 @@ bool VxlanTunnelOrch::getTunnelPort(const std::string& remote_vtep,Port& tunnelP return ret; } +bool VxlanTunnel::isTunnelReferenced() +{ + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + auto src_vtep = getSrcIP().to_string(); + auto port_tunnel_name = tunnel_orch->getTunnelPortName(src_vtep, true); + bool ret; + Port tunnelPort; + bool dip_tunnels_used = tunnel_orch->dipTunnelsUsed(); + + ret = gPortsOrch->getPort(port_tunnel_name, tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Get port failed for source vtep %s", port_tunnel_name.c_str()); + return false; + } + + if (tunnelPort.m_fdb_count != 0) + { + return true; + } + + if (dip_tunnels_used) + { + return (getDipTunnelCnt() != 0); + } + else + { + /* Bridge port will have reference since on IMET routes reception L2MC group member + would be created with end point IP and the P2MP tunnel bridge port */ + + if (gPortsOrch->getBridgePortReferenceCount(tunnelPort) != 0) + { + return true; + } + /* If there are routes pointing to the tunnel */ + if (!tnl_users_.empty()) + { + return true; + } + } + return false; +} + //------------------- VXLAN_TUNNEL_MAP Table --------------------------// bool VxlanTunnelMapOrch::addOperation(const Request& request) @@ -1683,7 +1822,7 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request) } auto vni_id = static_cast(request.getAttrUint("vni")); - if (vni_id >= 1<<24) + if (vni_id >= MAX_VNI_ID) { SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); return true; @@ -1717,6 +1856,15 @@ bool VxlanTunnelMapOrch::addOperation(const Request& request) TUNNELMAP_SET_VLAN(mapper_list); TUNNELMAP_SET_VRF(mapper_list); tunnel_obj->createTunnelHw(mapper_list,TUNNEL_MAP_USE_DEDICATED_ENCAP_DECAP); + Port tunPort; + auto src_vtep = tunnel_obj->getSrcIP().to_string(); + if (!tunnel_orch->getTunnelPort(src_vtep, tunPort, true)) + { + auto port_tunnel_name = tunnel_orch->getTunnelPortName(src_vtep, true); + gPortsOrch->addTunnel(port_tunnel_name, tunnel_obj->getTunnelId(), false); + gPortsOrch->getPort(port_tunnel_name,tunPort); + gPortsOrch->addBridgePort(tunPort); + } } const auto tunnel_map_id = tunnel_obj->getDecapMapId(TUNNEL_MAP_T_VLAN); @@ -1756,7 +1904,7 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) const auto& tunnel_name = request.getKeyString(0); const auto& tunnel_map_entry_name = request.getKeyString(1); const auto& full_tunnel_map_entry_name = request.getFullKey(); - + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); if (!isTunnelMapExists(full_tunnel_map_entry_name)) { @@ -1786,7 +1934,6 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) vxlan_tunnel_map_table_.erase(full_tunnel_map_entry_name); - VxlanTunnelOrch* tunnel_orch = gDirectory.get(); if (!tunnel_orch->isTunnelExists(tunnel_name)) { SWSS_LOG_WARN("Vxlan tunnel '%s' doesn't exist", tunnel_name.c_str()); @@ -1803,10 +1950,31 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) if (tunnel_obj->vlan_vrf_vni_count == 0) { + Port tunnelPort; + auto src_vtep = tunnel_obj->getSrcIP().to_string(); + auto port_tunnel_name = tunnel_orch->getTunnelPortName(src_vtep, true); + bool ret; + + ret = gPortsOrch->getPort(port_tunnel_name, tunnelPort); // If there are Dynamic DIP Tunnels referring to this SIP Tunnel // then mark it as pending for delete. - if (tunnel_obj->getDipTunnelCnt() == 0) + if (!tunnel_obj->isTunnelReferenced()) { + if (!ret) + { + SWSS_LOG_ERROR("Get port failed for source vtep %s", port_tunnel_name.c_str()); + return true; + } + ret = gPortsOrch->removeBridgePort(tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Remove Bridge port failed for source vtep = %s fdbcount = %d", + port_tunnel_name.c_str(), tunnelPort.m_fdb_count); + return true; + } + + gPortsOrch->removeTunnel(tunnelPort); + uint8_t mapper_list=0; TUNNELMAP_SET_VLAN(mapper_list); TUNNELMAP_SET_VRF(mapper_list); @@ -1815,8 +1983,16 @@ bool VxlanTunnelMapOrch::delOperation(const Request& request) else { tunnel_obj->del_tnl_hw_pending = true; - SWSS_LOG_WARN("Postponing the SIP Tunnel HW deletion DIP Tunnel count = %d", - tunnel_obj->getDipTunnelCnt()); + if (tunnel_orch->dipTunnelsUsed()) + { + SWSS_LOG_WARN("Postponing the SIP Tunnel HW deletion DIP Tunnel count = %d", + tunnel_obj->getDipTunnelCnt()); + } + else + { + SWSS_LOG_WARN("Postponing the SIP Tunnel HW deletion Remote reference count = %d", + gPortsOrch->getBridgePortReferenceCount(tunnelPort)); + } } } @@ -1855,7 +2031,7 @@ bool VxlanVrfMapOrch::addOperation(const Request& request) } auto vni_id = static_cast(request.getAttrUint("vni")); - if (vni_id >= 1<<24) + if (vni_id >= MAX_VNI_ID) { SWSS_LOG_ERROR("Vxlan vni id is too big: %d", vni_id); return true; @@ -1977,7 +2153,7 @@ bool VxlanVrfMapOrch::delOperation(const Request& request) //------------------- EVPN_REMOTE_VNI Table --------------------------// -bool EvpnRemoteVniOrch::addOperation(const Request& request) +bool EvpnRemoteVnip2pOrch::addOperation(const Request& request) { SWSS_LOG_ENTER(); @@ -1989,7 +2165,7 @@ bool EvpnRemoteVniOrch::addOperation(const Request& request) sai_vlan_id_t vlan_id = (sai_vlan_id_t) stoi(vlan_name.substr(4)); auto vni_id = static_cast(request.getAttrUint("vni")); - if (vni_id >= 1<<24) + if (vni_id >= MAX_VNI_ID) { SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); return true; @@ -2044,7 +2220,7 @@ bool EvpnRemoteVniOrch::addOperation(const Request& request) return true; } -bool EvpnRemoteVniOrch::delOperation(const Request& request) +bool EvpnRemoteVnip2pOrch::delOperation(const Request& request) { bool ret; @@ -2058,7 +2234,7 @@ bool EvpnRemoteVniOrch::delOperation(const Request& request) sai_vlan_id_t vlan_id = (sai_vlan_id_t)stoi(vlan_name.substr(4)); auto vni_id = static_cast(request.getAttrUint("vni")); - if (vni_id >= 1<<24) + if (vni_id >= MAX_VNI_ID) { SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); return true; @@ -2120,6 +2296,149 @@ bool EvpnRemoteVniOrch::delOperation(const Request& request) return ret; } +bool EvpnRemoteVnip2mpOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + EvpnNvoOrch* evpn_orch = gDirectory.get(); + // Extract end point ip + auto end_point_ip = request.getKeyString(1); + + // Extract VLAN and VNI + auto vlan_name = request.getKeyString(0); + sai_vlan_id_t vlan_id = to_uint(vlan_name.substr(4), MIN_VLAN_ID, MAX_VLAN_ID); + + auto vni_id = static_cast(request.getAttrUint("vni")); + if (vni_id >= MAX_VNI_ID) + { + SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); + return true; + } + + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + Port tunnelPort, vlanPort; + auto vtep_ptr = evpn_orch->getEVPNVtep(); + if (!vtep_ptr) + { + SWSS_LOG_WARN("Remote VNI add: Source VTEP not found. remote=%s vid=%d", + end_point_ip.c_str(),vlan_id); + return true; + } + + if (!gPortsOrch->getVlanByVlanId(vlan_id, vlanPort)) + { + SWSS_LOG_WARN("Vxlan tunnel map vlan id doesn't exist: %d", vlan_id); + return false; + } + + auto src_vtep = vtep_ptr->getSrcIP().to_string(); + if (tunnel_orch->getTunnelPort(src_vtep,tunnelPort, true)) + { + SWSS_LOG_INFO("Vxlan tunnelPort exists: %s", src_vtep.c_str()); + + if (gPortsOrch->isVlanMember(vlanPort, tunnelPort, end_point_ip)) + { + SWSS_LOG_WARN("Remote end point %s already member of vid %d", + end_point_ip.c_str(),vlan_id); + vtep_ptr->increment_spurious_imr_add(end_point_ip); + return true; + } + } + else + { + SWSS_LOG_WARN("Vxlan tunnelPort doesn't exist: %s", src_vtep.c_str()); + return false; + } + + // SAI Call to add tunnel to the VLAN flood domain + + string tagging_mode = "untagged"; + gPortsOrch->addVlanMember(vlanPort, tunnelPort, tagging_mode, end_point_ip); + + SWSS_LOG_INFO("end_point_ip=%s vni=%d vlanid=%d ", + end_point_ip.c_str(), vni_id, vlan_id); + + return true; +} + +bool EvpnRemoteVnip2mpOrch::delOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + // Extract end point ip + auto end_point_ip = request.getKeyString(1); + + // Extract VLAN and VNI + auto vlan_name = request.getKeyString(0); + sai_vlan_id_t vlan_id = to_uint(vlan_name.substr(4), MIN_VLAN_ID, MAX_VLAN_ID); + + auto vni_id = static_cast(request.getAttrUint("vni")); + if (vni_id >= MAX_VNI_ID) + { + SWSS_LOG_ERROR("Vxlan tunnel map vni id is too big: %d", vni_id); + return true; + } + + // SAI Call to add tunnel to the VLAN flood domain + + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + Port vlanPort, tunnelPort; + EvpnNvoOrch* evpn_orch = gDirectory.get(); + + auto vtep_ptr = evpn_orch->getEVPNVtep(); + if (!vtep_ptr) + { + SWSS_LOG_WARN("Remote VNI add: VTEP not found. remote=%s vid=%d", + end_point_ip.c_str(), vlan_id); + return true; + } + + if (!gPortsOrch->getVlanByVlanId(vlan_id, vlanPort)) + { + SWSS_LOG_WARN("Vxlan tunnel map vlan id doesn't exist: %d", vlan_id); + return true; + } + + auto src_vtep = vtep_ptr->getSrcIP().to_string(); + if (!tunnel_orch->getTunnelPort(src_vtep,tunnelPort,true)) + { + SWSS_LOG_WARN("RemoteVniDel getTunnelPort Fails: %s", src_vtep.c_str()); + return true; + } + + + if (!gPortsOrch->isVlanMember(vlanPort, tunnelPort, end_point_ip)) + { + SWSS_LOG_WARN("marking it as spurious tunnelPort %s not a member of vid %d", + end_point_ip.c_str(), vlan_id); + vtep_ptr->increment_spurious_imr_del(end_point_ip); + return true; + } + + if (!gPortsOrch->removeVlanMember(vlanPort, tunnelPort, end_point_ip)) + { + SWSS_LOG_WARN("RemoteVniDel remove vlan member fails: vlan:%hu ip %s", + vlan_id, end_point_ip.c_str()); + return false; + } + + if (vtep_ptr->del_tnl_hw_pending && + !vtep_ptr->isTunnelReferenced()) + { + bool ret = gPortsOrch->removeBridgePort(tunnelPort); + if (!ret) + { + SWSS_LOG_ERROR("Remove Bridge port failed for source vtep = %s fdbcount = %d", + src_vtep.c_str(), tunnelPort.m_fdb_count); + return true; + } + gPortsOrch->removeTunnel(tunnelPort); + vtep_ptr->deletePendingSIPTunnel(); + } + + return true; +} + //------------------- EVPN_NVO Table --------------------------// bool EvpnNvoOrch::addOperation(const Request& request) diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index edc65d97fe..9d6b531eec 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -35,6 +35,15 @@ typedef enum #define IS_TUNNELMAP_SET_VRF(x) ((x)& (1< VTEPTable; class VxlanTunnelOrch : public Orch2 { public: - VxlanTunnelOrch(DBConnector *statedb, DBConnector *db, const std::string& tableName) : + VxlanTunnelOrch(DBConnector *statedb, DBConnector *db, const std::string& tableName, bool dipTunnelsSupported) : Orch2(db, tableName, request_), - m_stateVxlanTable(statedb, STATE_VXLAN_TUNNEL_TABLE_NAME) + m_stateVxlanTable(statedb, STATE_VXLAN_TUNNEL_TABLE_NAME), dip_tunnels_used(dipTunnelsSupported) {} @@ -296,7 +311,7 @@ class VxlanTunnelOrch : public Orch2 bool removeNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAddress macAddress, uint32_t vni=0); - bool getTunnelPort(const std::string& remote_vtep,Port& tunnelPort); + bool getTunnelPort(const std::string& vtep,Port& tunnelPort, bool local=false); bool addTunnelUser(string remote_vtep, uint32_t vni_id, uint32_t vlan, tunnel_user_t usr, @@ -310,7 +325,8 @@ class VxlanTunnelOrch : public Orch2 void addRemoveStateTableEntry(const string, IpAddress&, IpAddress&, tunnel_creation_src_t, bool); - std::string getTunnelPortName(const std::string& remote_vtep); + std::string getTunnelPortName(const std::string& vtep, bool local=false); + bool isSrcVtepTunnel(Port& tunnelPort); void getTunnelNameFromDIP(const string& dip, string& tunnel_name); void getTunnelNameFromPort(string& tunnel_portname, string& tunnel_name); void getTunnelDIPFromPort(Port& tunnelPort, string& remote_vtep); @@ -338,6 +354,10 @@ class VxlanTunnelOrch : public Orch2 vxlan_vni_vlan_map_table_.erase(vni); } + bool dipTunnelsUsed(void) + { + return dip_tunnels_used; + } private: @@ -349,6 +369,7 @@ class VxlanTunnelOrch : public Orch2 VxlanVniVlanMapTable vxlan_vni_vlan_map_table_; VTEPTable vtep_table_; Table m_stateVxlanTable; + bool dip_tunnels_used; }; const request_description_t vxlan_tunnel_map_request_description = { @@ -445,10 +466,23 @@ class EvpnRemoteVniRequest : public Request EvpnRemoteVniRequest() : Request(evpn_remote_vni_request_description, ':') { } }; -class EvpnRemoteVniOrch : public Orch2 +class EvpnRemoteVnip2pOrch : public Orch2 +{ +public: + EvpnRemoteVnip2pOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } + + +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + EvpnRemoteVniRequest request_; +}; + +class EvpnRemoteVnip2mpOrch : public Orch2 { public: - EvpnRemoteVniOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } + EvpnRemoteVnip2mpOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) { } private: diff --git a/portsyncd/portsyncd.cpp b/portsyncd/portsyncd.cpp index 151ac16657..beaa008449 100644 --- a/portsyncd/portsyncd.cpp +++ b/portsyncd/portsyncd.cpp @@ -88,10 +88,8 @@ int main(int argc, char **argv) if (!handlePortConfigFromConfigDB(p, cfgDb, warm)) { - // if port config is missing in ConfigDB - // program will exit with failure - SWSS_LOG_THROW("ConfigDB does not have port information, exiting..."); - return EXIT_FAILURE; + SWSS_LOG_NOTICE("ConfigDB does not have port information, " + "however ports can be added later on, continuing..."); } LinkSync sync(&appl_db, &state_db); @@ -235,6 +233,10 @@ bool handlePortConfigFromConfigDB(ProducerStateTable &p, DBConnector &cfgDb, boo void handlePortConfig(ProducerStateTable &p, map &port_cfg_map) { + string autoneg; + vector attrs; + vector autoneg_attrs; + vector force_attrs; auto it = port_cfg_map.begin(); while (it != port_cfg_map.end()) @@ -250,7 +252,54 @@ void handlePortConfig(ProducerStateTable &p, map /* No support for port delete yet */ if (op == SET_COMMAND) { - p.set(key, values); + + for (auto i : values) + { + auto field = fvField(i); + if (field == "adv_speeds") + { + autoneg_attrs.push_back(i); + } + else if (field == "adv_interface_types") + { + autoneg_attrs.push_back(i); + } + else if (field == "speed") + { + force_attrs.push_back(i); + } + else if (field == "interface_type") + { + force_attrs.push_back(i); + } + else if (field == "autoneg") + { + autoneg = fvValue(i); + attrs.push_back(i); + } + else + { + attrs.push_back(i); + } + } + if (autoneg == "on") // autoneg is on, only put adv_speeds and adv_interface_types to APPL_DB + { + attrs.insert(attrs.end(), autoneg_attrs.begin(), autoneg_attrs.end()); + } + else if (autoneg == "off") // autoneg is off, only put speed and interface_type to APPL_DB + { + attrs.insert(attrs.end(), force_attrs.begin(), force_attrs.end()); + } + else // autoneg is not configured, put all attributes to APPL_DB + { + attrs.insert(attrs.end(), autoneg_attrs.begin(), autoneg_attrs.end()); + attrs.insert(attrs.end(), force_attrs.begin(), force_attrs.end()); + } + p.set(key, attrs); + attrs.clear(); + autoneg_attrs.clear(); + force_attrs.clear(); + autoneg.clear(); } it = port_cfg_map.erase(it); diff --git a/tests/evpn_tunnel.py b/tests/evpn_tunnel.py new file mode 100644 index 0000000000..48ed6c0904 --- /dev/null +++ b/tests/evpn_tunnel.py @@ -0,0 +1,1121 @@ +from swsscommon import swsscommon +import time +import json +import random +from pytest import * +from pprint import pprint + + +class VxlanEvpnHelper(object): + def create_entry(self, tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + time.sleep(1) + + def create_entry_tbl(self, db, table, key, pairs): + tbl = swsscommon.Table(db, table) + self.create_entry(tbl, key, pairs) + + def delete_entry_tbl(self, db, table, key): + tbl = swsscommon.Table(db, table) + tbl._del(key) + time.sleep(1) + + def create_entry_pst(self, db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + self.create_entry(tbl, key, pairs) + + def delete_entry_pst(self, db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + + def how_many_entries_exist(self, db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + + def get_exist_entries(self, dvs, table): + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(db, table) + return set(tbl.getKeys()) + + def get_created_entry(self, db, table, existed_entries): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) == 1, "Wrong number of created entries." + return new_entries[0] + + def get_created_entries(self, db, table, existed_entries, count): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) == count, "Wrong number of created entries." + new_entries.sort() + return new_entries + + def get_deleted_entries(self, db, table, existed_entries, count): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + old_entries = list(existed_entries - entries) + assert len(old_entries) == count, "Wrong number of deleted entries." + old_entries.sort() + return old_entries + + def check_object(self, db, table, key, expected_attributes): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert key in keys, "The desired key is not presented" + + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + assert len(fvs) >= len(expected_attributes), "Incorrect attributes" + + attr_keys = {entry[0] for entry in fvs} + + for name, value in fvs: + if name in expected_attributes: + assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ + (value, name, expected_attributes[name]) + + def get_key_with_attr(self, db, table, expected_attributes ): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + retkey = list() + + for key in keys: + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + assert len(fvs) >= len(expected_attributes), "Incorrect attributes" + + attr_keys = {entry[0] for entry in fvs} + + num_match = 0 + for name, value in fvs: + if name in expected_attributes: + if expected_attributes[name] == value: + num_match += 1 + if num_match == len(expected_attributes): + retkey.append(key) + + return retkey + + def check_deleted_object(self, db, table, key): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert key not in keys, "The desired key is not removed" + +class VxlanTunnel(object): + + ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" + ASIC_TUNNEL_MAP = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP" + ASIC_TUNNEL_MAP_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY" + ASIC_TUNNEL_TERM_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" + ASIC_BRIDGE_PORT = "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT" + ASIC_VRF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER" + ASIC_RIF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE" + ASIC_ROUTE_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" + ASIC_NEXT_HOP = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP" + ASIC_NEXT_HOP_GRP = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP" + ASIC_NEXT_HOP_GRP_MEMBERS = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER" + + tunnel_map_ids = set() + tunnel_map_entry_ids = set() + tunnel_map_vrf_entry_ids = set() + tunnel_ids = set() + tunnel_term_ids = set() + bridgeport_ids = set() + tunnel_map_map = {} + tunnel = {} + tunnel_appdb = {} + tunnel_term = {} + map_entry_map = {} + dip_tunnel_map = {} + dip_tun_state_map = {} + diptunterm_map = {} + bridgeport_map = {} + vlan_id_map = {} + vlan_member_map = {} + l2mcgroup_member_map = {} + l2mcgroup_map = {} + vr_map = {} + vnet_vr_ids = set() + nh_ids = {} + nh_grp_id = set() + nh_grp_member_id = set() + route_id = {} + helper = None + switch_mac = None + + + def __init__(self): + self.helper = VxlanEvpnHelper() + + def create_evpn_nvo(self, dvs, nvoname, tnl_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("source_vtep", tnl_name), + ] + + # create the VXLAN tunnel Term entry in Config DB + self.helper.create_entry_tbl( + conf_db, + "VXLAN_EVPN_NVO", nvoname, + attrs, + ) + + def remove_evpn_nvo(self, dvs, nvoname): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + self.helper.delete_entry_tbl(conf_db,"VXLAN_EVPN_NVO", nvoname) + + def create_vxlan_tunnel(self, dvs, name, src_ip, dst_ip = '0.0.0.0', skip_dst_ip=True): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("src_ip", src_ip), + ] + + if not skip_dst_ip: + attrs.append(("dst_ip", dst_ip)) + + # create the VXLAN tunnel Term entry in Config DB + self.helper.create_entry_tbl( + conf_db, + "VXLAN_TUNNEL", name, + attrs, + ) + + def create_vxlan_tunnel_map(self, dvs, tnl_name, map_name, vni_id, vlan_id): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", vni_id), + ("vlan", vlan_id), + ] + + # create the VXLAN tunnel Term entry in Config DB + self.helper.create_entry_tbl( + conf_db, + "VXLAN_TUNNEL_MAP", "%s|%s" % (tnl_name, map_name), + attrs, + ) + + def create_evpn_remote_vni(self, dvs, vlan_id, remote_vtep, vnid): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + self.helper.create_entry_pst( + app_db, + "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remote_vtep), + [ + ("vni", vnid), + ], + ) + time.sleep(2) + + def remove_vxlan_tunnel(self, dvs, tnl_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + # create the VXLAN tunnel Term entry in Config DB + self.helper.delete_entry_tbl( + conf_db, + "VXLAN_TUNNEL", tnl_name + ) + + def remove_vxlan_tunnel_map(self, dvs, tnl_name, map_name,vni_id, vlan_id): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", vni_id), + ("vlan", vlan_id), + ] + + # create the VXLAN tunnel Term entry in Config DB + self.helper.delete_entry_tbl( + conf_db, + "VXLAN_TUNNEL_MAP", "%s|%s" % (tnl_name, map_name) + ) + + def remove_evpn_remote_vni(self, dvs, vlan_id, remote_vtep ): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + self.helper.delete_entry_pst( + app_db, + "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remote_vtep), + ) + time.sleep(2) + + def create_vxlan_vrf_tunnel_map(self, dvs, vrfname, vni_id): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", vni_id), + ] + + # create the VXLAN VRF tunnel Term entry in Config DB + self.helper.create_entry_tbl( + conf_db, + "VRF", vrfname, + attrs, + ) + + def remove_vxlan_vrf_tunnel_map(self, dvs, vrfname): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attrs = [ + ("vni", "0"), + ] + + # remove the VXLAN VRF tunnel Term entry in Config DB + self.helper.create_entry_tbl( + conf_db, + "VRF", vrfname, + attrs, + ) + + def create_vlan1(self, dvs, vlan_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + vlan_id = vlan_name[4:] + + # create vlan + self.helper.create_entry_tbl( + conf_db, + "VLAN", vlan_name, + [ + ("vlanid", vlan_id), + ], + ) + + def create_vrf_route(self, dvs, prefix, vrf_name, endpoint, ifname, mac="", vni=0): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + attrs = [ + ("nexthop", endpoint), + ("ifname", ifname), + ] + + if vni: + attrs.append(('vni_label', vni)) + + if mac: + attrs.append(('router_mac', mac)) + + self.helper.create_entry_pst( + app_db, + "ROUTE_TABLE", "%s:%s" % (vrf_name, prefix), + attrs, + ) + + time.sleep(2) + + def delete_vrf_route(self, dvs, prefix, vrf_name): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + self.helper.delete_entry_pst(app_db, "ROUTE_TABLE", "%s:%s" % (vrf_name, prefix)) + + time.sleep(2) + + def create_vrf_route_ecmp(self, dvs, prefix, vrf_name, ecmp_nexthop_attributes): + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + self.helper.create_entry_pst( + app_db, + "ROUTE_TABLE", "%s:%s" % (vrf_name, prefix), + ecmp_nexthop_attributes, + ) + + time.sleep(2) + + def create_vlan(self, dvs, vlan_name, vlan_ids): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + vlan_id = vlan_name[4:] + + # create vlan + self.helper.create_entry_tbl( + conf_db, + "VLAN", vlan_name, + [ + ("vlanid", vlan_id), + ], + ) + + time.sleep(1) + + vlan_oid = self.helper.get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN", vlan_ids) + + self.helper.check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN", vlan_oid, + { + "SAI_VLAN_ATTR_VLAN_ID": vlan_id, + } + ) + + return vlan_oid + + def remove_vlan(self, dvs, vlan): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(conf_db, "VLAN") + tbl._del("Vlan" + vlan) + time.sleep(1) + + def create_vlan_member(self, dvs, vlan, interface, tagging_mode="untagged"): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(conf_db, "VLAN_MEMBER") + fvs = swsscommon.FieldValuePairs([("tagging_mode", tagging_mode)]) + tbl.set("Vlan" + vlan + "|" + interface, fvs) + time.sleep(1) + + def remove_vlan_member(self, dvs, vlan, interface): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(conf_db, "VLAN_MEMBER") + tbl._del("Vlan" + vlan + "|" + interface) + time.sleep(1) + + + def create_vlan_interface(self, dvs, vlan_name, ifname, vrf_name, ipaddr): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + # create a vlan member in config db + self.helper.create_entry_tbl( + conf_db, + "VLAN_MEMBER", "%s|%s" % (vlan_name, ifname), + [ + ("tagging_mode", "untagged"), + ], + ) + + time.sleep(1) + + # create vlan interface in config db + self.helper.create_entry_tbl( + conf_db, + "VLAN_INTERFACE", vlan_name, + [ + ("vrf_name", vrf_name), + ], + ) + + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + self.helper.create_entry_pst( + app_db, + "INTF_TABLE", vlan_name, + [ + ("vrf_name", vrf_name), + ], + ) + time.sleep(2) + + self.helper.create_entry_tbl( + conf_db, + "VLAN_INTERFACE", "%s|%s" % (vlan_name, ipaddr), + [ + ("family", "IPv4"), + ], + ) + + time.sleep(2) + + def delete_vlan_interface(self, dvs, ifname, ipaddr): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + self.helper.delete_entry_tbl(conf_db, "VLAN_INTERFACE", "%s|%s" % (ifname, ipaddr)) + time.sleep(2) + + self.helper.delete_entry_tbl(conf_db, "VLAN_INTERFACE", ifname) + time.sleep(2) + + def get_switch_mac(self, dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH') + + entries = tbl.getKeys() + mac = None + for entry in entries: + status, fvs = tbl.get(entry) + assert status, "Got an error when get a key" + for key, value in fvs: + if key == 'SAI_SWITCH_ATTR_SRC_MAC_ADDRESS': + mac = value + break + else: + assert False, 'Don\'t found switch mac' + + return mac + + def fetch_exist_entries(self, dvs): + self.tunnel_ids = self.helper.get_exist_entries(dvs, self.ASIC_TUNNEL_TABLE) + self.tunnel_map_ids = self.helper.get_exist_entries(dvs, self.ASIC_TUNNEL_MAP) + self.tunnel_map_entry_ids = self.helper.get_exist_entries(dvs, self.ASIC_TUNNEL_MAP_ENTRY) + self.tunnel_term_ids = self.helper.get_exist_entries(dvs, self.ASIC_TUNNEL_TERM_ENTRY) + self.bridgeport_ids = self.helper.get_exist_entries(dvs, self.ASIC_BRIDGE_PORT) + self.vnet_vr_ids = self.helper.get_exist_entries(dvs, self.ASIC_VRF_TABLE) + self.rifs = self.helper.get_exist_entries(dvs, self.ASIC_RIF_TABLE) + self.routes = self.helper.get_exist_entries(dvs, self.ASIC_ROUTE_ENTRY) + self.nhops = self.helper.get_exist_entries(dvs, self.ASIC_NEXT_HOP) + self.nhop_grp = self.helper.get_exist_entries(dvs, self.ASIC_NEXT_HOP_GRP) + self.nhop_grp_members = self.helper.get_exist_entries(dvs, self.ASIC_NEXT_HOP_GRP_MEMBERS) + + if self.switch_mac is None: + self.switch_mac = self.get_switch_mac(dvs) + + def check_vxlan_tunnel_map_entry_delete(self, dvs, tunnel_name, vidlist, vnilist): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) + + for x in range(len(vidlist)): + status, fvs = tbl.get(self.map_entry_map[tunnel_name + vidlist[x]]) + assert status == False, "SIP Tunnel Map entry not deleted" + iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] + (exitcode, out) = dvs.runcmd(iplinkcmd) + assert exitcode != 0, "Kernel device not deleted" + + def check_vxlan_tunnel_map_entry(self, dvs, tunnel_name, vidlist, vnidlist): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) + + expected_attributes_1 = { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': self.tunnel_map_map[tunnel_name][0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], + } + + for x in range(len(vidlist)): + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] + ret = self.helper.get_key_with_attr(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, expected_attributes_1) + assert len(ret) > 0, "SIP TunnelMap entry not created" + assert len(ret) == 1, "More than 1 SIP TunnMapEntry created" + self.map_entry_map[tunnel_name + vidlist[x]] = ret[0] + iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] + (exitcode, out) = dvs.runcmd(iplinkcmd) + assert exitcode == 0, "Kernel device not created" + + def check_vxlan_sip_tunnel_delete(self, dvs, tunnel_name, sip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(app_db, "VXLAN_TUNNEL_TABLE") + status, fvs = tbl.get(self.tunnel_appdb[tunnel_name]) + assert status == False, "SIP Tunnel entry not deleted from APP_DB" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) + status, fvs = tbl.get(self.tunnel_term[tunnel_name]) + assert status == False, "SIP Tunnel Term entry not deleted from ASIC_DB" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) + status, fvs = tbl.get(self.tunnel[tunnel_name]) + assert status == False, "SIP Tunnel entry not deleted from ASIC_DB" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP) + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][0]) + assert status == False, "SIP Tunnel mapper0 not deleted from ASIC_DB" + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][1]) + assert status == False, "SIP Tunnel mapper1 not deleted from ASIC_DB" + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][2]) + assert status == False, "SIP Tunnel mapper2 not deleted from ASIC_DB" + status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][3]) + assert status == False, "SIP Tunnel mapper3 not deleted from ASIC_DB" + + tbl = swsscommon.Table(asic_db, self.ASIC_BRIDGE_PORT) + status, fvs = tbl.get(self.bridgeport_map[sip]) + assert status == False, "Tunnel bridgeport entry not deleted" + + def check_vxlan_sip_tunnel(self, dvs, tunnel_name, src_ip, vidlist, vnidlist, dst_ip = '0.0.0.0', skip_dst_ip = 'True'): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + tunnel_map_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) + tunnel_id = self.helper.get_created_entry(asic_db, self.ASIC_TUNNEL_TABLE, self.tunnel_ids) + tunnel_term_id = self.helper.get_created_entry(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, self.tunnel_term_ids) + tunnel_map_entry_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) + + # check that the vxlan tunnel termination are there + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP) == (len(self.tunnel_map_ids) + 4), "The TUNNEL_MAP wasn't created" + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created" + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TABLE) == (len(self.tunnel_ids) + 1), "The TUNNEL wasn't created" + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) == (len(self.tunnel_term_ids) + 1), "The TUNNEL_TERM_TABLE_ENTRY wasm't created" + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[2], + { + 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', + } + ) + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[3], + { + 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', + } + ) + + decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] + encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] + #'SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE': loopback_id, + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_TABLE, tunnel_id, + { + 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', + 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, + 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, + 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2MP', + 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, + } + ) + + expected_attributes = { + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE': 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP', + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP': src_ip, + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', + 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID': tunnel_id, + } + + if not skip_dst_ip: + expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP'] = dst_ip + expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE'] = 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P' + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, tunnel_term_id, expected_attributes) + + expected_attributes_1 = { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], + } + + for x in range(len(vidlist)): + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] + expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[x], expected_attributes_1) + + expected_siptnl_attributes = { + 'src_ip': src_ip, + } + + if not skip_dst_ip: + expected_siptnl_attributes['dst_ip'] = dst_ip + + ret = self.helper.get_key_with_attr(app_db, "VXLAN_TUNNEL_TABLE", expected_siptnl_attributes) + assert len(ret) > 0, "SIP Tunnel entry not created in APPDB" + assert len(ret) == 1, "More than 1 Tunn statetable entry created" + self.tunnel_appdb[tunnel_name] = ret[0] + + expected_bridgeport_attributes = { + 'SAI_BRIDGE_PORT_ATTR_TYPE': 'SAI_BRIDGE_PORT_TYPE_TUNNEL', + 'SAI_BRIDGE_PORT_ATTR_TUNNEL_ID': tunnel_id, + 'SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE': 'SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE', + 'SAI_BRIDGE_PORT_ATTR_ADMIN_STATE': 'true', + } + ret = self.helper.get_key_with_attr(asic_db, self.ASIC_BRIDGE_PORT, expected_bridgeport_attributes) + assert len(ret) > 0, "Bridgeport entry not created" + assert len(ret) == 1, "More than 1 bridgeport entry created" + self.bridgeport_map[src_ip] = ret[0] + self.tunnel_map_ids.update(tunnel_map_id) + self.tunnel_ids.add(tunnel_id) + self.tunnel_term_ids.add(tunnel_term_id) + self.tunnel_map_map[tunnel_name] = tunnel_map_id + self.tunnel[tunnel_name] = tunnel_id + self.tunnel_term[tunnel_name] = tunnel_term_id + + def check_vxlan_dip_tunnel_delete(self, dvs, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(state_db, 'VXLAN_TUNNEL_TABLE') + status, fvs = tbl.get(self.dip_tun_state_map[dip]) + assert status == False, "State Table entry not deleted" + + tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) + status, fvs = tbl.get(self.dip_tunnel_map[dip]) + assert status == False, "Tunnel entry not deleted" + + tbl = swsscommon.Table(asic_db, self.ASIC_BRIDGE_PORT) + status, fvs = tbl.get(self.bridgeport_map[dip]) + assert status == False, "Tunnel bridgeport entry not deleted" + + def check_vxlan_dip_tunnel(self, dvs, vtep_name, src_ip, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + + expected_state_attributes = { + 'src_ip': src_ip, + 'dst_ip': dip, + 'tnl_src': 'EVPN', + } + + ret = self.helper.get_key_with_attr(state_db, 'VXLAN_TUNNEL_TABLE', expected_state_attributes) + assert len(ret) > 0, "Tunnel Statetable entry not created" + assert len(ret) == 1, "More than 1 Tunn statetable entry created" + self.dip_tun_state_map[dip] = ret[0] + + + tunnel_map_id = self.tunnel_map_map[vtep_name] + + decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] + encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] + + print(decapstr) + print(encapstr) + + expected_tun_attributes = { + 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', + 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2P', + 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, + 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, + 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, + 'SAI_TUNNEL_ATTR_ENCAP_DST_IP': dip, + } + + ret = self.helper.get_key_with_attr(asic_db, self.ASIC_TUNNEL_TABLE, expected_tun_attributes) + assert len(ret) > 0, "Tunnel entry not created" + assert len(ret) == 1, "More than 1 tunnel entry created" + + self.dip_tunnel_map[dip] = ret[0] + tunnel_id = ret[0] + + expected_bridgeport_attributes = { + 'SAI_BRIDGE_PORT_ATTR_TYPE': 'SAI_BRIDGE_PORT_TYPE_TUNNEL', + 'SAI_BRIDGE_PORT_ATTR_TUNNEL_ID': tunnel_id, + 'SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE': 'SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE', + 'SAI_BRIDGE_PORT_ATTR_ADMIN_STATE': 'true', + } + + ret = self.helper.get_key_with_attr(asic_db, self.ASIC_BRIDGE_PORT, expected_bridgeport_attributes) + assert len(ret) > 0, "Bridgeport entry not created" + assert len(ret) == 1, "More than 1 bridgeport entry created" + + self.bridgeport_map[dip] = ret[0] + + def check_vlan_extension_delete(self, dvs, vlan_name, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER') + status, fvs = tbl.get(self.vlan_member_map[dip+vlan_name]) + assert status == False, "VLAN Member entry not deleted" + + def check_vlan_extension_delete_p2mp(self, dvs, vlan_name, sip, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_L2MC_GROUP_MEMBER') + status, fvs = tbl.get(self.l2mcgroup_member_map[dip+vlan_name]) + assert status == False, "L2MC Group Member entry not deleted" + + def check_vlan_extension(self, dvs, vlan_name, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + expected_vlan_attributes = { + 'SAI_VLAN_ATTR_VLAN_ID': vlan_name, + } + ret = self.helper.get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN', expected_vlan_attributes) + assert len(ret) > 0, "VLAN entry not created" + assert len(ret) == 1, "More than 1 VLAN entry created" + + self.vlan_id_map[vlan_name] = ret[0] + + expected_vlan_member_attributes = { + 'SAI_VLAN_MEMBER_ATTR_VLAN_ID': self.vlan_id_map[vlan_name], + 'SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID': self.bridgeport_map[dip], + } + ret = self.helper.get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER', expected_vlan_member_attributes) + assert len(ret) > 0, "VLAN Member not created" + assert len(ret) == 1, "More than 1 VLAN member created" + self.vlan_member_map[dip+vlan_name] = ret[0] + + def check_vlan_extension_p2mp(self, dvs, vlan_name, sip, dip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN') + expected_vlan_attributes = { + 'SAI_VLAN_ATTR_VLAN_ID': vlan_name, + } + ret = self.helper.get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN', expected_vlan_attributes) + assert len(ret) > 0, "VLAN entry not created" + assert len(ret) == 1, "More than 1 VLAN entry created" + + self.vlan_id_map[vlan_name] = ret[0] + status, fvs = tbl.get(self.vlan_id_map[vlan_name]) + + print(fvs) + + uuc_flood_type = None + bc_flood_type = None + uuc_flood_group = None + bc_flood_group = None + + for attr,value in fvs: + if attr == 'SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_CONTROL_TYPE': + uuc_flood_type = value + elif attr == 'SAI_VLAN_ATTR_BROADCAST_FLOOD_CONTROL_TYPE': + bc_flood_type = value + elif attr == 'SAI_VLAN_ATTR_UNKNOWN_UNICAST_FLOOD_GROUP': + uuc_flood_group = value + elif attr == 'SAI_VLAN_ATTR_BROADCAST_FLOOD_GROUP': + bc_flood_group = value + + assert uuc_flood_type == 'SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED', "Unknown unicast flood control type is not combined" + assert bc_flood_type == 'SAI_VLAN_FLOOD_CONTROL_TYPE_COMBINED', "Broadcast flood control type is not combined" + assert uuc_flood_group == bc_flood_group, "Unexpected two flood groups in VLAN" + + + expected_l2mc_group_member_attributes = { + 'SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_GROUP_ID' : uuc_flood_group, + 'SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_ENDPOINT_IP': dip, + 'SAI_L2MC_GROUP_MEMBER_ATTR_L2MC_OUTPUT_ID': self.bridgeport_map[sip], + } + + ret = self.helper.get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_L2MC_GROUP_MEMBER', expected_l2mc_group_member_attributes) + assert len(ret) > 0, "L2MC group Member not created" + assert len(ret) == 1, "More than 1 L2MC group member created" + self.l2mcgroup_member_map[dip+vlan_name] = ret[0] + + def check_vxlan_tunnel_entry(self, dvs, tunnel_name, vnet_name, vni_id): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + time.sleep(2) + + if (self.tunnel_map_map.get(tunnel_name) is None): + tunnel_map_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 2) + else: + tunnel_map_id = self.tunnel_map_map[tunnel_name] + + tunnel_map_entry_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 2) + + # check that the vxlan tunnel termination are there + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 2), "The TUNNEL_MAP_ENTRY is created too early" + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[1], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY': self.vr_map[vnet_name].get('ing'), + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE': vni_id, + } + ) + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE': self.vr_map[vnet_name].get('egr'), + } + ) + + self.tunnel_map_entry_ids.update(tunnel_map_entry_id) + + def check_vxlan_tunnel_vrf_map_entry(self, dvs, tunnel_name, vrf_name, vni_id): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + if (self.tunnel_map_map.get(tunnel_name) is None): + tunnel_map_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) + else: + tunnel_map_id = self.tunnel_map_map[tunnel_name] + + tunnel_map_entry_id = self.helper.get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) + + # check that the vxlan tunnel termination are there + assert self.helper.how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created too early" + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[3], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY': self.vr_map[vrf_name].get('ing'), + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE': vni_id, + } + ) + + self.tunnel_map_vrf_entry_ids.update(tunnel_map_entry_id[1]) + + self.helper.check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[2], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[2], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE': self.vr_map[vrf_name].get('egr'), + } + ) + + self.tunnel_map_vrf_entry_ids.update(tunnel_map_entry_id[2]) + self.tunnel_map_entry_ids.update(tunnel_map_entry_id) + + def check_vxlan_tunnel_vrf_map_entry_remove(self, dvs, tunnel_name, vrf_name, vni_id): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tunnel_map_entry_id = self.helper.get_deleted_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 2) + self.helper.check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0]) + self.helper.check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1]) + for vrf_map_id in self.tunnel_map_vrf_entry_ids: + self.helper.check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, vrf_map_id) + + def check_router_interface(self, dvs, name, vlan_oid, route_count): + # Check RIF in ingress VRF + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + expected_attr = { + "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": self.vr_map[name].get('ing'), + "SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS": self.switch_mac, + } + + if vlan_oid: + expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_VLAN'}) + expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_VLAN_ID': vlan_oid}) + else: + expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_PORT'}) + + new_rif = self.helper.get_created_entry(asic_db, self.ASIC_RIF_TABLE, self.rifs) + self.helper.check_object(asic_db, self.ASIC_RIF_TABLE, new_rif, expected_attr) + + #IP2ME route will be created with every router interface + new_route = self.helper.get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, route_count) + + self.rifs.add(new_rif) + self.routes.update(new_route) + + def check_del_router_interface(self, dvs, name): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + old_rif = self.helper.get_deleted_entries(asic_db, self.ASIC_RIF_TABLE, self.rifs, 1) + self.helper.check_deleted_object(asic_db, self.ASIC_RIF_TABLE, old_rif[0]) + + self.rifs.remove(old_rif[0]) + + def vrf_route_ids(self, dvs, vrf_name): + vr_set = set() + + vr_set.add(self.vr_map[vrf_name].get('ing')) + return vr_set + + def check_vrf_routes(self, dvs, prefix, vrf_name, endpoint, tunnel, mac="", vni=0, no_update=0): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + vr_ids = self.vrf_route_ids(dvs, vrf_name) + count = len(vr_ids) + + # Check routes in ingress VRF + expected_attr = { + "SAI_NEXT_HOP_ATTR_TYPE": "SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP", + "SAI_NEXT_HOP_ATTR_IP": endpoint, + "SAI_NEXT_HOP_ATTR_TUNNEL_ID": self.tunnel[tunnel], + } + + if vni: + expected_attr.update({'SAI_NEXT_HOP_ATTR_TUNNEL_VNI': vni}) + + if mac: + expected_attr.update({'SAI_NEXT_HOP_ATTR_TUNNEL_MAC': mac}) + + if endpoint in self.nh_ids: + new_nh = self.nh_ids[endpoint] + else: + new_nh = self.helper.get_created_entry(asic_db, self.ASIC_NEXT_HOP, self.nhops) + self.nh_ids[endpoint] = new_nh + self.nhops.add(new_nh) + + self.helper.check_object(asic_db, self.ASIC_NEXT_HOP, new_nh, expected_attr) + if no_update: + count = 0 + new_route = self.helper.get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, count) + + if count: + self.route_id[vrf_name + ":" + prefix] = new_route + + #Check if the route is in expected VRF + asic_vrs = set() + for idx in range(count): + self.helper.check_object(asic_db, self.ASIC_ROUTE_ENTRY, new_route[idx], + { + "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": new_nh, + } + ) + rt_key = json.loads(new_route[idx]) + asic_vrs.add(rt_key['vr']) + found_route = False + if rt_key['dest'] == prefix: + found_route = True + + assert found_route + + if count: + assert asic_vrs == vr_ids + + self.routes.update(new_route) + + return True + + def check_vrf_routes_ecmp(self, dvs, prefix, vrf_name, tunnel, nh_count, no_update=0): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + vr_ids = self.vrf_route_ids(dvs, vrf_name) + count = len(vr_ids) + + new_nhg = self.helper.get_created_entry(asic_db, self.ASIC_NEXT_HOP_GRP, self.nhop_grp) + self.nh_grp_id.add(new_nhg) + + if no_update: + count = 0 + + new_route = self.helper.get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, count) + if count: + self.route_id[vrf_name + ":" + prefix] = new_route + + #Check if the route is in expected VRF + asic_vrs = set() + for idx in range(count): + self.helper.check_object(asic_db, self.ASIC_ROUTE_ENTRY, new_route[idx], + { + "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": new_nhg, + } + ) + rt_key = json.loads(new_route[idx]) + asic_vrs.add(rt_key['vr']) + + found_route = False + if rt_key['dest'] == prefix: + found_route = True + + assert found_route + + if count: + assert asic_vrs == vr_ids + + self.routes.update(new_route) + + new_nhg_members = self.helper.get_created_entries(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, self.nhop_grp_members, nh_count) + + for idx in range(nh_count): + self.nh_grp_member_id.add(new_nhg_members[idx]) + self.helper.check_object(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, new_nhg_members[idx], + { + "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": new_nhg, + } + ) + + nhid_list = list() + + nhg_member_tbl = swsscommon.Table(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS) + + for k in new_nhg_members: + (status, fvs) = nhg_member_tbl.get(k) + assert status + + for v in fvs: + if v[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID": + nhid = v[1] + nhid_list.append(nhid) + + return nhid_list + + def check_add_tunnel_nexthop(self, dvs, nh_id, endpoint, tunnel, mac, vni): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + # Check routes in ingress VRF + expected_attr = { + "SAI_NEXT_HOP_ATTR_TYPE": "SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP", + "SAI_NEXT_HOP_ATTR_IP": endpoint, + "SAI_NEXT_HOP_ATTR_TUNNEL_ID": self.tunnel[tunnel], + "SAI_NEXT_HOP_ATTR_TUNNEL_VNI": vni, + "SAI_NEXT_HOP_ATTR_TUNNEL_MAC": mac, + } + + self.helper.check_object(asic_db, self.ASIC_NEXT_HOP, nh_id, expected_attr) + + def check_del_tunnel_nexthop(self, dvs, vrf_name, endpoint, tunnel, mac="", vni=0): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + del_nh_ids = self.helper.get_deleted_entries(asic_db, self.ASIC_NEXT_HOP, self.nhops, 1) + self.helper.check_deleted_object(asic_db, self.ASIC_NEXT_HOP, del_nh_ids[0]) + self.helper.check_deleted_object(asic_db, self.ASIC_NEXT_HOP, self.nh_ids[endpoint]) + assert del_nh_ids[0] == self.nh_ids[endpoint] + self.nh_ids.pop(endpoint) + return True + + def check_vrf_routes_ecmp_nexthop_grp_del(self, dvs, nh_count): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + nh_grp_id_len = len(self.nh_grp_id) + assert nh_grp_id_len == 1 + + for nh_grp_id in self.nh_grp_id: + self.helper.check_deleted_object(asic_db, self.ASIC_NEXT_HOP_GRP, nh_grp_id) + self.nh_grp_id.clear() + + nh_grp_member_id_len = len(self.nh_grp_member_id) + assert nh_grp_member_id_len == nh_count + + for nh_grp_member_id in self.nh_grp_member_id: + self.helper.check_deleted_object(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, nh_grp_member_id) + + self.nh_grp_member_id.clear() + + def check_del_vrf_routes(self, dvs, prefix, vrf_name): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + del_route = self.helper.get_deleted_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, 1) + + for idx in range(1): + rt_key = json.loads(del_route[idx]) + found_route = False + if rt_key['dest'] == prefix: + found_route = True + + assert found_route + + self.helper.check_deleted_object(asic_db, self.ASIC_ROUTE_ENTRY, self.route_id[vrf_name + ":" + prefix]) + self.route_id.clear() + + return True + + def create_vrf(self, dvs, vrf_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + initial_entries = set(tbl.getKeys()) + + attrs = [ + ("vni", "0"), + ] + tbl = swsscommon.Table(conf_db, "VRF") + fvs = swsscommon.FieldValuePairs(attrs) + tbl.set(vrf_name, fvs) + time.sleep(2) + + tbl = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + current_entries = set(tbl.getKeys()) + assert len(current_entries - initial_entries) == 1 + + new_vr_ids = self.helper.get_created_entries(asic_db, self.ASIC_VRF_TABLE, self.vnet_vr_ids, 1) + self.vnet_vr_ids.update(new_vr_ids) + self.vr_map[vrf_name] = { 'ing':new_vr_ids[0], 'egr':new_vr_ids[0]} + + return list(current_entries - initial_entries)[0] + + def remove_vrf(self, dvs, vrf_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(conf_db, "VRF") + tbl._del(vrf_name) + time.sleep(2) + + def is_vrf_attributes_correct(self, db, table, key, expected_attributes): + tbl = swsscommon.Table(db, table) + keys = set(tbl.getKeys()) + assert key in keys, "The created key wasn't found" + + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + # filter the fake 'NULL' attribute out + fvs = filter(lambda x : x != ('NULL', 'NULL'), fvs) + + attr_keys = {entry[0] for entry in fvs} + assert attr_keys == set(expected_attributes.keys()) + + for name, value in fvs: + assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ + (value, name, expected_attributes[name]) + diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index 30ab875f5d..69f25a6b0e 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -69,6 +69,8 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/debugcounterorch.cpp \ $(top_srcdir)/orchagent/natorch.cpp \ $(top_srcdir)/orchagent/muxorch.cpp \ + $(top_srcdir)/orchagent/mlagorch.cpp \ + $(top_srcdir)/orchagent/isolationgrouporch.cpp \ $(top_srcdir)/orchagent/macsecorch.cpp \ $(top_srcdir)/orchagent/lagid.cpp diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index 5d5fe5de64..4b1ce101ae 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -330,16 +330,18 @@ namespace aclorch_test ASSERT_EQ(gIntfsOrch, nullptr); gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); - TableConnector applDbFdb(m_app_db.get(), APP_FDB_TABLE_NAME); - TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); + const int fdborch_pri = 20; vector app_fdb_tables = { { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, - { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri} + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_MCLAG_FDB_TABLE_NAME, fdborch_pri} }; - + + TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); + TableConnector stateMclagDbFdb(m_state_db.get(), STATE_MCLAG_REMOTE_FDB_TABLE_NAME); ASSERT_EQ(gFdbOrch, nullptr); - gFdbOrch = new FdbOrch(m_app_db.get(), app_fdb_tables, stateDbFdb, gPortsOrch); + gFdbOrch = new FdbOrch(m_app_db.get(), app_fdb_tables, stateDbFdb, stateMclagDbFdb, gPortsOrch); ASSERT_EQ(gNeighOrch, nullptr); gNeighOrch = new NeighOrch(m_app_db.get(), APP_NEIGH_TABLE_NAME, gIntfsOrch, gFdbOrch, gPortsOrch, m_chassis_app_db.get()); diff --git a/tests/mock_tests/mock_orchagent_main.cpp b/tests/mock_tests/mock_orchagent_main.cpp index 62a03dc770..1dbb16032a 100644 --- a/tests/mock_tests/mock_orchagent_main.cpp +++ b/tests/mock_tests/mock_orchagent_main.cpp @@ -19,6 +19,7 @@ bool gSairedisRecord = true; bool gSwssRecord = true; bool gLogRotate = false; bool gSaiRedisLogRotate = false; +bool gP2PTunnelSupported = true; ofstream gRecordOfs; string gRecordFile; string gMySwitchType = "switch"; diff --git a/tests/mock_tests/mock_orchagent_main.h b/tests/mock_tests/mock_orchagent_main.h index 181ebac889..502a6071a0 100644 --- a/tests/mock_tests/mock_orchagent_main.h +++ b/tests/mock_tests/mock_orchagent_main.h @@ -32,6 +32,7 @@ extern MacAddress gVxlanMacAddress; extern sai_object_id_t gSwitchId; extern sai_object_id_t gVirtualRouterId; extern sai_object_id_t gUnderlayIfId; +extern bool gP2PTunnelSupported; extern SwitchOrch *gSwitchOrch; extern CrmOrch *gCrmOrch; diff --git a/tests/mock_tests/portsorch_ut.cpp b/tests/mock_tests/portsorch_ut.cpp index 32b1d373ee..2c65a42f09 100644 --- a/tests/mock_tests/portsorch_ut.cpp +++ b/tests/mock_tests/portsorch_ut.cpp @@ -1,3 +1,7 @@ +#define private public // make Directory::m_values available to clean it. +#include "directory.h" +#undef private + #include "ut_helper.h" #include "mock_orchagent_main.h" #include "mock_table.h" @@ -36,17 +40,51 @@ namespace portsorch_test virtual void SetUp() override { ::testing_db::reset(); + + // Create dependencies ... + + const int portsorch_base_pri = 40; + + vector ports_tables = { + { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, + { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, + { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, + { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, + { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } + }; + + ASSERT_EQ(gPortsOrch, nullptr); + + vector flex_counter_tables = { + CFG_FLEX_COUNTER_TABLE_NAME + }; + auto* flexCounterOrch = new FlexCounterOrch(m_config_db.get(), flex_counter_tables); + gDirectory.set(flexCounterOrch); + + gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + vector buffer_tables = { APP_BUFFER_POOL_TABLE_NAME, + APP_BUFFER_PROFILE_TABLE_NAME, + APP_BUFFER_QUEUE_TABLE_NAME, + APP_BUFFER_PG_TABLE_NAME, + APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, + APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; + + ASSERT_EQ(gBufferOrch, nullptr); + gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); } virtual void TearDown() override { ::testing_db::reset(); + delete gPortsOrch; gPortsOrch = nullptr; delete gBufferOrch; gBufferOrch = nullptr; - } + // clear orchs saved in directory + gDirectory.m_values.clear(); + } static void SetUpTestCase() { // Init switch and create dependencies @@ -92,6 +130,7 @@ namespace portsorch_test ut_helper::uninitSaiApi(); } + }; TEST_F(PortsOrchTest, PortReadinessColdBoot) @@ -132,27 +171,7 @@ namespace portsorch_test pgTableCfg.set(ossCfg.str(), { { "profile", "[BUFFER_PROFILE|test_profile]" } }); } - // Create dependencies ... - - const int portsorch_base_pri = 40; - - vector ports_tables = { - { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, - { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, - { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, - { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, - { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } - }; - - ASSERT_EQ(gPortsOrch, nullptr); - - vector flex_counter_tables = { - CFG_FLEX_COUNTER_TABLE_NAME - }; - auto* flexCounterOrch = new FlexCounterOrch(m_config_db.get(), flex_counter_tables); - gDirectory.set(flexCounterOrch); - - gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + // Recreate buffer orch to read populated data vector buffer_tables = { APP_BUFFER_POOL_TABLE_NAME, APP_BUFFER_PROFILE_TABLE_NAME, APP_BUFFER_QUEUE_TABLE_NAME, @@ -160,7 +179,6 @@ namespace portsorch_test APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; - ASSERT_EQ(gBufferOrch, nullptr); gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); // Populate pot table with SAI ports @@ -223,7 +241,6 @@ namespace portsorch_test TEST_F(PortsOrchTest, PortReadinessWarmBoot) { - Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); Table pgTable = Table(m_app_db.get(), APP_BUFFER_PG_TABLE_NAME); Table profileTable = Table(m_app_db.get(), APP_BUFFER_PROFILE_TABLE_NAME); @@ -268,30 +285,6 @@ namespace portsorch_test portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); portTable.set("PortInitDone", { { "lanes", "0" } }); - // Create dependencies ... - - const int portsorch_base_pri = 40; - - vector ports_tables = { - { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, - { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, - { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, - { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, - { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } - }; - - ASSERT_EQ(gPortsOrch, nullptr); - gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); - vector buffer_tables = { APP_BUFFER_POOL_TABLE_NAME, - APP_BUFFER_PROFILE_TABLE_NAME, - APP_BUFFER_QUEUE_TABLE_NAME, - APP_BUFFER_PG_TABLE_NAME, - APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, - APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; - - ASSERT_EQ(gBufferOrch, nullptr); - gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); - // warm start, bake fill refill consumer gBufferOrch->bake(); @@ -338,30 +331,6 @@ namespace portsorch_test // Get SAI default ports to populate DB auto ports = ut_helper::getInitialSaiPorts(); - // Create dependencies ... - - const int portsorch_base_pri = 40; - - vector ports_tables = { - { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, - { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, - { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, - { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, - { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } - }; - - ASSERT_EQ(gPortsOrch, nullptr); - gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); - vector buffer_tables = { APP_BUFFER_POOL_TABLE_NAME, - APP_BUFFER_PROFILE_TABLE_NAME, - APP_BUFFER_QUEUE_TABLE_NAME, - APP_BUFFER_PG_TABLE_NAME, - APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, - APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; - - ASSERT_EQ(gBufferOrch, nullptr); - gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); - // Populate port table with SAI ports for (const auto &it : ports) { @@ -460,6 +429,106 @@ namespace portsorch_test ts.clear(); } + /* This test checks that a LAG member validation happens on orchagent level + * and no SAI call is executed in case a port requested to be a LAG member + * is already a LAG member. + */ + TEST_F(PortsOrchTest, LagMemberDoesNotCallSAIApiWhenPortIsAlreadyALagMember) + { + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + Table lagTable = Table(m_app_db.get(), APP_LAG_TABLE_NAME); + Table lagMemberTable = Table(m_app_db.get(), APP_LAG_MEMBER_TABLE_NAME); + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + /* + * Next we will prepare some configuration data to be consumed by PortsOrch + * 32 Ports, 2 LAGs, 1 port is LAG member. + */ + + // Populate pot table with SAI ports + for (const auto &it : ports) + { + portTable.set(it.first, it.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + portTable.set("PortInitDone", { { } }); + + lagTable.set("PortChannel999", + { + {"admin_status", "up"}, + {"mtu", "9100"} + } + ); + lagTable.set("PortChannel0001", + { + {"admin_status", "up"}, + {"mtu", "9100"} + } + ); + lagMemberTable.set( + std::string("PortChannel999") + lagMemberTable.getTableNameSeparator() + ports.begin()->first, + { {"status", "enabled"} }); + + // refill consumer + gPortsOrch->addExistingData(&portTable); + gPortsOrch->addExistingData(&lagTable); + gPortsOrch->addExistingData(&lagMemberTable); + + static_cast(gPortsOrch)->doTask(); + + // check LAG, VLAN tasks were processed + // port table may require one more doTask iteration + for (auto tableName: {APP_LAG_TABLE_NAME, APP_LAG_MEMBER_TABLE_NAME}) + { + vector ts; + auto exec = gPortsOrch->getExecutor(tableName); + auto consumer = static_cast(exec); + ts.clear(); + consumer->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + } + + // Set first port as a LAG member while this port is still a member of different LAG. + lagMemberTable.set( + std::string("PortChannel0001") + lagMemberTable.getTableNameSeparator() + ports.begin()->first, + { {"status", "enabled"} }); + + // save original api since we will spy + auto orig_lag_api = sai_lag_api; + sai_lag_api = new sai_lag_api_t(); + memcpy(sai_lag_api, orig_lag_api, sizeof(*sai_lag_api)); + + bool lagMemberCreateCalled = false; + + auto lagSpy = SpyOn(&sai_lag_api->create_lag_member); + lagSpy->callFake([&](sai_object_id_t *oid, sai_object_id_t swoid, uint32_t count, const sai_attribute_t * attrs) -> sai_status_t + { + lagMemberCreateCalled = true; + return orig_lag_api->create_lag_member(oid, swoid, count, attrs); + } + ); + + gPortsOrch->addExistingData(&lagMemberTable); + + static_cast(gPortsOrch)->doTask(); + sai_lag_api = orig_lag_api; + + // verify there is a pending task to do. + vector ts; + auto exec = gPortsOrch->getExecutor(APP_LAG_MEMBER_TABLE_NAME); + auto consumer = static_cast(exec); + ts.clear(); + consumer->dumpPendingTasks(ts); + ASSERT_FALSE(ts.empty()); + + // verify there was no SAI call executed. + ASSERT_FALSE(lagMemberCreateCalled); + } + /* * The scope of this test is to verify that LAG member is * added to a LAG before any other object on LAG is created, like RIF, bridge port in warm mode. @@ -474,7 +543,6 @@ namespace portsorch_test */ TEST_F(PortsOrchTest, LagMemberIsCreatedBeforeOtherObjectsAreCreatedOnLag) { - Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); Table lagTable = Table(m_app_db.get(), APP_LAG_TABLE_NAME); Table lagMemberTable = Table(m_app_db.get(), APP_LAG_MEMBER_TABLE_NAME); @@ -484,29 +552,6 @@ namespace portsorch_test // Get SAI default ports to populate DB auto ports = ut_helper::getInitialSaiPorts(); - // Create dependencies ... - const int portsorch_base_pri = 40; - - vector ports_tables = { - { APP_PORT_TABLE_NAME, portsorch_base_pri + 5 }, - { APP_VLAN_TABLE_NAME, portsorch_base_pri + 2 }, - { APP_VLAN_MEMBER_TABLE_NAME, portsorch_base_pri }, - { APP_LAG_TABLE_NAME, portsorch_base_pri + 4 }, - { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } - }; - - ASSERT_EQ(gPortsOrch, nullptr); - gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); - vector buffer_tables = { APP_BUFFER_POOL_TABLE_NAME, - APP_BUFFER_PROFILE_TABLE_NAME, - APP_BUFFER_QUEUE_TABLE_NAME, - APP_BUFFER_PG_TABLE_NAME, - APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, - APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; - - ASSERT_EQ(gBufferOrch, nullptr); - gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); - /* * Next we will prepare some configuration data to be consumed by PortsOrch * 32 Ports, 1 LAG, 1 port is LAG member and LAG is in Vlan. @@ -598,5 +643,4 @@ namespace portsorch_test ASSERT_FALSE(bridgePortCalledBeforeLagMember); // bridge port created on lag before lag member was created } - } diff --git a/tests/test_buffer_traditional.py b/tests/test_buffer_traditional.py new file mode 100644 index 0000000000..b21862c296 --- /dev/null +++ b/tests/test_buffer_traditional.py @@ -0,0 +1,145 @@ +import pytest +import time + + +class TestBuffer(object): + LOSSLESS_PGS = [3, 4] + INTF = "Ethernet0" + + def setup_db(self, dvs): + self.app_db = dvs.get_app_db() + self.asic_db = dvs.get_asic_db() + self.config_db = dvs.get_config_db() + self.counter_db = dvs.get_counters_db() + + # enable PG watermark + self.set_pg_wm_status('enable') + + def get_pg_oid(self, pg): + fvs = dict() + fvs = self.counter_db.get_entry("COUNTERS_PG_NAME_MAP", "") + return fvs[pg] + + def set_pg_wm_status(self, state): + fvs = {'FLEX_COUNTER_STATUS': state} + self.config_db.update_entry("FLEX_COUNTER_TABLE", "PG_WATERMARK", fvs) + time.sleep(1) + + def teardown(self): + # disable PG watermark + self.set_pg_wm_status('disable') + + def get_asic_buf_profile(self): + return set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) + + def check_new_profile_in_asic_db(self, profile): + retry_count = 0 + self.new_profile = None + while retry_count < 5: + retry_count += 1 + diff = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE")) - self.orig_profiles + if len(diff) == 1: + self.new_profile = diff.pop() + break + else: + time.sleep(1) + assert self.new_profile, "Can't get SAI OID for newly created profile {} after retry {} times".format(profile, retry_count) + + def get_asic_buf_pg_profiles(self): + self.buf_pg_profile = dict() + for pg in self.pg_name_map: + buf_pg_entries = self.asic_db.get_entry("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP", self.pg_name_map[pg]) + self.buf_pg_profile[pg] = buf_pg_entries["SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE"] + + def change_cable_len(self, cable_len): + fvs = dict() + fvs[self.INTF] = cable_len + self.config_db.update_entry("CABLE_LENGTH", "AZURE", fvs) + + @pytest.fixture + def setup_teardown_test(self, dvs): + try: + self.setup_db(dvs) + pg_name_map = dict() + for pg in self.LOSSLESS_PGS: + pg_name = "{}:{}".format(self.INTF, pg) + pg_name_map[pg_name] = self.get_pg_oid(pg_name) + yield pg_name_map + finally: + self.teardown() + + def test_zero_cable_len_profile_update(self, dvs, setup_teardown_test): + self.pg_name_map = setup_teardown_test + orig_cable_len = None + orig_speed = None + try: + dvs.runcmd("config interface startup {}".format(self.INTF)) + self.orig_profiles = self.get_asic_buf_profile() + # get orig cable length and speed + fvs = self.config_db.get_entry("CABLE_LENGTH", "AZURE") + orig_cable_len = fvs[self.INTF] + fvs = self.config_db.get_entry("PORT", self.INTF) + orig_speed = fvs["speed"] + + if orig_speed == "100000": + test_speed = "40000" + elif orig_speed == "40000": + test_speed = "100000" + test_cable_len = "0m" + + # check if the lossless profile for the test speed is already present + fvs = dict() + new_lossless_profile = "pg_lossless_{}_{}_profile".format(test_speed, orig_cable_len) + fvs = self.app_db.get_entry("BUFFER_PROFILE_TABLE", new_lossless_profile) + if len(fvs): + profile_exp_cnt_diff = 0 + else: + profile_exp_cnt_diff = 1 + + # get the orig buf profiles attached to the pgs + self.get_asic_buf_pg_profiles() + + # change cable length to 'test_cable_len' + self.change_cable_len(test_cable_len) + + # change intf speed to 'test_speed' + dvs.runcmd("config interface speed {} {}".format(self.INTF, test_speed)) + test_lossless_profile = "pg_lossless_{}_{}_profile".format(test_speed, test_cable_len) + # buffer profile should not get created + self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", test_lossless_profile) + + # buffer pgs should still point to the original buffer profile + orig_lossless_profile = "pg_lossless_{}_{}_profile".format(orig_speed, orig_cable_len) + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", self.INTF + ":3-4", {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(orig_lossless_profile)}) + fvs = dict() + for pg in self.pg_name_map: + fvs["SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE"] = self.buf_pg_profile[pg] + self.asic_db.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP", self.pg_name_map[pg], fvs) + + # change cable length to 'orig_cable_len' + self.change_cable_len(orig_cable_len) + + # change intf speed to 'test_speed' + dvs.runcmd("config interface speed {} {}".format(self.INTF, test_speed)) + if profile_exp_cnt_diff != 0: + # new profile will get created + self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", new_lossless_profile) + self.check_new_profile_in_asic_db(new_lossless_profile) + # verify that buffer pgs point to the new profile + fvs = dict() + for pg in self.pg_name_map: + fvs["SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE"] = self.new_profile + self.asic_db.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP", self.pg_name_map[pg], fvs) + + else: + # verify that buffer pgs do not point to the old profile since we cannot deduce the new profile oid + fvs = dict() + for pg in self.pg_name_map: + fvs["SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE"] = self.buf_pg_profile[pg] + self.asic_db.wait_for_field_negative_match("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP", self.pg_name_map[pg], fvs) + finally: + if orig_cable_len: + self.change_cable_len(orig_cable_len) + if orig_speed: + dvs.runcmd("config interface speed {} {}".format(self.INTF, orig_speed)) + dvs.runcmd("config interface shutdown {}".format(self.INTF)) diff --git a/tests/test_evpn_fdb.py b/tests/test_evpn_fdb.py index 0989815b9d..31d75535c7 100644 --- a/tests/test_evpn_fdb.py +++ b/tests/test_evpn_fdb.py @@ -1,118 +1,6 @@ from swsscommon import swsscommon import time - -def create_entry(tbl, key, pairs): - fvs = swsscommon.FieldValuePairs(pairs) - tbl.set(key, fvs) - - # FIXME: better to wait until DB create them - time.sleep(1) - -def create_entry_tbl(db, table, key, pairs): - tbl = swsscommon.Table(db, table) - create_entry(tbl, key, pairs) - -def create_entry_pst(db, table, key, pairs): - tbl = swsscommon.ProducerStateTable(db, table) - create_entry(tbl, key, pairs) - -def delete_entry_pst(db, table, key): - tbl = swsscommon.ProducerStateTable(db, table) - tbl._del(key) - -def how_many_entries_exist(db, table): - tbl = swsscommon.Table(db, table) - return len(tbl.getKeys()) - -def remove_mac(db, table, mac, vlan): - tbl = swsscommon.Table(db, table) - tbl._del("Vlan" + vlan + "|" + mac.lower()) - time.sleep(1) - -def delete_entry_tbl(db, table, key): - tbl = swsscommon.Table(db, table) - tbl._del(key) - time.sleep(1) - -def create_evpn_nvo(db, nvoname, tnlname): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - attrs = [ - ("source_vtep", tnlname), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - db, - "VXLAN_EVPN_NVO", nvoname, - attrs, - ) - -def remove_evpn_nvo(db, nvoname): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - delete_entry_tbl(db,"VXLAN_EVPN_NVO", nvoname,) - -def create_vxlan_tunnel(db, name, src_ip): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("src_ip", src_ip), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - db, - "VXLAN_TUNNEL", name, - attrs, - ) - -def remove_vxlan_tunnel(db, tnlname): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - db, - "VXLAN_TUNNEL", tnlname, - ) - -def create_vxlan_tunnel_map(db, tnlname, mapname, vni_id, vlan_id): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - db, - "VXLAN_TUNNEL_MAP", "%s|%s" % (tnlname, mapname), - attrs, - ) - -def remove_vxlan_tunnel_map(db, tnlname, mapname): - #conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - db, - "VXLAN_TUNNEL_MAP", "%s|%s" % (tnlname, mapname), - ) - -def create_evpn_remote_vni(db, vlan_id, remotevtep, vnid): - #app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - create_entry_pst( - db, - "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remotevtep), - [ - ("vni", vnid), - ], - ) - -def remove_evpn_remote_vni(db, vlan_id, remotevtep ): - #app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - delete_entry_pst( - db, - "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remotevtep), - ) +from evpn_tunnel import VxlanTunnel,VxlanEvpnHelper def get_vxlan_p2p_tunnel_bp(db, remote_ip): tnl_id = None @@ -159,6 +47,8 @@ def get_vxlan_p2p_tunnel_bp(db, remote_ip): def test_evpnFdb(dvs, testlog): + vxlan_obj = VxlanTunnel() + helper = VxlanEvpnHelper() dvs.setup_db() dvs.runcmd("sonic-clear fdb all") @@ -168,7 +58,7 @@ def test_evpnFdb(dvs, testlog): switch_id = dvs.getSwitchOid() print("Switch_id="+str(switch_id)) - vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") # create vlan print("Creating Vlan3") @@ -176,7 +66,7 @@ def test_evpnFdb(dvs, testlog): dvs.create_vlan("3") time.sleep(2) - vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") assert vlan_after - vlan_before == 1, "The Vlan3 wasn't created" print("Vlan3 is created") @@ -185,8 +75,8 @@ def test_evpnFdb(dvs, testlog): assert vlan_oid_3 is not None, "Could not find Vlan_oid" print("Vlan-3 vlan_oid="+str(vlan_oid_3)) - 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") + bp_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") print("Making Ethernet0 as a member of Vlan3") #dvs.runcmd("config vlan member add 3 Ethernet0") @@ -194,8 +84,8 @@ def test_evpnFdb(dvs, testlog): time.sleep(2) # check that the vlan information was propagated - 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") + bp_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") assert bp_after - bp_before == 1, "The bridge port wasn't created" assert vm_after - vm_before == 1, "The vlan member wasn't added" @@ -207,30 +97,30 @@ def test_evpnFdb(dvs, testlog): #create SIP side of tunnel source_tnl_name = "source_vtep_name" source_tnl_ip = "7.7.7.7" - create_vxlan_tunnel(dvs.cdb, source_tnl_name, source_tnl_ip) + vxlan_obj.create_vxlan_tunnel(dvs, source_tnl_name, source_tnl_ip) time.sleep(1) nvo_name = "evpn_nvo" - create_evpn_nvo(dvs.cdb, nvo_name, source_tnl_name) + vxlan_obj.create_evpn_nvo(dvs, nvo_name, source_tnl_name) time.sleep(1) map_name_vlan_3 = "map_3_3" - create_vxlan_tunnel_map(dvs.cdb, source_tnl_name, map_name_vlan_3, "3", "Vlan3") + vxlan_obj.create_vxlan_tunnel_map(dvs, source_tnl_name, map_name_vlan_3, "3", "Vlan3") time.sleep(1) remote_ip_6 = "6.6.6.6" - create_evpn_remote_vni(dvs.pdb, "Vlan3", remote_ip_6, "3") + vxlan_obj.create_evpn_remote_vni(dvs, "Vlan3", remote_ip_6, "3") remote_ip_8 = "8.8.8.8" - create_evpn_remote_vni(dvs.pdb, "Vlan3", remote_ip_8, "3") + vxlan_obj.create_evpn_remote_vni(dvs, "Vlan3", remote_ip_8, "3") time.sleep(1) #UT-1 Evpn Mac add from remote when tunnels are already created mac = "52:54:00:25:06:E9" print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -260,7 +150,7 @@ def test_evpnFdb(dvs, testlog): #UT-2 Evpn Mac del from remote mac = "52:54:00:25:06:E9" print("Deleting Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") - delete_entry_pst( + helper.delete_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower() ) @@ -285,7 +175,7 @@ def test_evpnFdb(dvs, testlog): print("Creating Local dynamic FDB Vlan3:"+mac.lower()+":Ethernet0 in APP-DB") # Create Dynamic MAC entry in APP DB - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -317,7 +207,7 @@ def test_evpnFdb(dvs, testlog): print("FDB Vlan3:"+mac+":Ethernet0 is created in STATE-DB") print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -357,7 +247,7 @@ def test_evpnFdb(dvs, testlog): #UT-4 Evpn Sticky Mac add from remote mac = "52:54:00:25:06:E9" print("Creating Evpn Sticky FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -385,7 +275,7 @@ def test_evpnFdb(dvs, testlog): #UT-8 Evpn Mac add from remote when tunnels are already created mac = "52:54:00:25:06:E9" print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -415,7 +305,7 @@ def test_evpnFdb(dvs, testlog): tnl_bp_oid_8 = get_vxlan_p2p_tunnel_bp(dvs.adb, remote_ip_8) print("Creating Evpn FDB Vlan3:"+mac.lower()+":8.8.8.8 in APP-DB") - create_entry_pst( + helper.create_entry_pst( dvs.pdb, "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), [ @@ -445,7 +335,7 @@ def test_evpnFdb(dvs, testlog): #UT-9 Local mac move (delete and learn) when remote is already added mac = "52:54:00:25:06:E9" print("Deleting FDB Vlan3:52-54-00-25-06-E9:8.8.8.8 in ASIC-DB") - delete_entry_tbl(dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\""+mac+"\",\"switch_id\":\""+switch_id+"\"}") + helper.delete_entry_tbl(dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\""+mac+"\",\"switch_id\":\""+switch_id+"\"}") ntf = swsscommon.NotificationProducer(dvs.adb, "NOTIFICATIONS") fvp = swsscommon.FieldValuePairs() @@ -457,7 +347,7 @@ def test_evpnFdb(dvs, testlog): #raw_input("Check ASIC_DB.........") print("Creating FDB Vlan3:52-54-00-25-06-E9:Ethernet0 in ASIC-DB") - create_entry_tbl( + helper.create_entry_tbl( dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\"52:54:00:25:06:E9\",\"switch_id\":\""+switch_id+"\"}", [ diff --git a/tests/test_evpn_fdb_p2mp.py b/tests/test_evpn_fdb_p2mp.py new file mode 100644 index 0000000000..6684245107 --- /dev/null +++ b/tests/test_evpn_fdb_p2mp.py @@ -0,0 +1,376 @@ +from swsscommon import swsscommon +import time +from evpn_tunnel import VxlanTunnel,VxlanEvpnHelper + +DVS_FAKE_PLATFORM = "mellanox" + +def get_vxlan_p2mp_tunnel_bp(db, src_ip): + tnl_id = None + bp = None + print("src_ip = " + src_ip) + attributes = [("SAI_TUNNEL_ATTR_TYPE", "SAI_TUNNEL_TYPE_VXLAN"), + ("SAI_TUNNEL_ATTR_PEER_MODE", "SAI_TUNNEL_PEER_MODE_P2MP"), + ("SAI_TUNNEL_ATTR_ENCAP_SRC_IP", src_ip) + ] + tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL") + keys = tbl.getKeys() + for key in keys: + status, fvs = tbl.get(key) + assert status, "Error reading from table ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" + attrs = dict(attributes) + num_match = 0 + for k, v in fvs: + print("attr:value="+str(k)+":"+str(v)) + if k in attrs and attrs[k] == v: + num_match += 1 + if num_match == len(attributes): + tnl_id = str(key) + break + else: + tnl_id = None + + print("tnl_id = "+str(tnl_id)) + if tnl_id != None: + tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + keys = tbl.getKeys() + for key in keys: + status, fvs = tbl.get(key) + assert status, "Error reading from table ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT" + for k, v in fvs: + print("attr:value="+str(k)+":"+str(v)) + if k == "SAI_BRIDGE_PORT_ATTR_TUNNEL_ID" and tnl_id == v: + bp = key + break + if bp != None: + break + else: + pass + print("bp = "+str(bp)) + return bp + + +def test_evpnFdbP2MP(dvs, testlog): + vxlan_obj = VxlanTunnel() + helper = VxlanEvpnHelper() + dvs.setup_db() + + dvs.runcmd("sonic-clear fdb all") + time.sleep(2) + + #Find switch_id + switch_id = dvs.getSwitchOid() + print("Switch_id="+str(switch_id)) + + vlan_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + + # create vlan + print("Creating Vlan3") + dvs.create_vlan("3") + time.sleep(2) + + vlan_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + assert vlan_after - vlan_before == 1, "The Vlan3 wasn't created" + print("Vlan3 is created") + + # Find the vlan_oid to be used in DB communications + vlan_oid_3 = dvs.getVlanOid("3") + assert vlan_oid_3 is not None, "Could not find Vlan_oid" + print("Vlan-3 vlan_oid="+str(vlan_oid_3)) + + bp_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + print("Making Ethernet0 as a member of Vlan3") + dvs.create_vlan_member("3", "Ethernet0") + time.sleep(2) + + # check that the vlan information was propagated + bp_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert bp_after - bp_before == 1, "The bridge port wasn't created" + assert vm_after - vm_before == 1, "The vlan member wasn't added" + print("Ethernet0 is a member of Vlan3") + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = dvs.get_map_iface_bridge_port_id(dvs.adb) + + #create SIP side of tunnel + source_tnl_name = "source_vtep_name" + source_tnl_ip = "7.7.7.7" + vxlan_obj.create_vxlan_tunnel(dvs, source_tnl_name, source_tnl_ip) + time.sleep(1) + + nvo_name = "evpn_nvo" + vxlan_obj.create_evpn_nvo(dvs, nvo_name, source_tnl_name) + time.sleep(1) + + map_name_vlan_3 = "map_3_3" + vxlan_obj.create_vxlan_tunnel_map(dvs, source_tnl_name, map_name_vlan_3, "3", "Vlan3") + time.sleep(1) + + remote_ip_6 = "6.6.6.6" + vxlan_obj.create_evpn_remote_vni(dvs, "Vlan3", remote_ip_6, "3") + remote_ip_8 = "8.8.8.8" + vxlan_obj.create_evpn_remote_vni(dvs, "Vlan3", remote_ip_8, "3") + time.sleep(1) + + #UT-1 Evpn Mac add from remote when tunnels are already created + mac = "52:54:00:25:06:E9" + print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + helper.create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + tnl_bp_oid_6 = get_vxlan_p2mp_tunnel_bp(dvs.adb, source_tnl_ip) + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok == True, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + time.sleep(1) + + #UT-2 Evpn Mac del from remote + mac = "52:54:00:25:06:E9" + print("Deleting Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + helper.delete_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower() + ) + time.sleep(1) + + # check that the FDB entry is deleted from ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok == False, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is deleted from ASIC-DB") + + time.sleep(1) + + #UT-3 Evpn Mac add from remote when local mac is already present + mac = "52:54:00:25:06:E9" + + print("Creating Local dynamic FDB Vlan3:"+mac.lower()+":Ethernet0 in APP-DB") + # Create Dynamic MAC entry in APP DB + helper.create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + time.sleep(1) + + # check that the FDB entry was added in ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + assert ok, str(extra) + print("Dynamic FDB Vlan3:"+mac.lower()+":Ethernet0 is created in Asic-DB") + + # check that the FDB entry was added in STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + assert mac1_found, str(extra) + print("FDB Vlan3:"+mac+":Ethernet0 is created in STATE-DB") + + print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + helper.create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", str(dvs.getVlanOid("3")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + # check that the Local FDB entry is deleted from STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:"+mac.lower(), + [("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + assert mac1_found == False, str(extra) + print("FDB Vlan3:"+mac+":Ethernet0 is deleted from STATE-DB") + + time.sleep(1) + + + #UT-4 Evpn Sticky Mac add from remote + mac = "52:54:00:25:06:E9" + print("Creating Evpn Sticky FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + helper.create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "static"), + ("vni", "3") + ] + ) + time.sleep(1) + + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", str(dvs.getVlanOid("3")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok, str(extra) + print("EVPN Sticky FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + #UT-8 Evpn Mac add from remote when tunnels are already created + mac = "52:54:00:25:06:E9" + print("Creating Evpn FDB Vlan3:"+mac.lower()+":6.6.6.6 in APP-DB") + helper.create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_6), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_6), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_6)), + ] + ) + assert ok == True, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_6+" is created in ASIC-DB") + + time.sleep(1) + + tnl_bp_oid_8 = get_vxlan_p2mp_tunnel_bp(dvs.adb, source_tnl_ip) + + print("Creating Evpn FDB Vlan3:"+mac.lower()+":8.8.8.8 in APP-DB") + helper.create_entry_pst( + dvs.pdb, + "VXLAN_FDB_TABLE", "Vlan3:"+mac.lower(), + [ + ("remote_vtep", remote_ip_8), + ("type", "dynamic"), + ("vni", "3") + ] + ) + time.sleep(1) + + # check that the FDB entry is inserted into ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", mac), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true"), + ("SAI_FDB_ENTRY_ATTR_ENDPOINT_IP", remote_ip_8), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", str(tnl_bp_oid_8)), + ] + ) + assert ok == True, str(extra) + print("EVPN FDB Vlan3:"+mac.lower()+":"+remote_ip_8+" is created in ASIC-DB") + + time.sleep(1) + + #UT-9 Local mac move (delete and learn) when remote is already added + mac = "52:54:00:25:06:E9" + print("Deleting FDB Vlan3:52-54-00-25-06-E9:8.8.8.8 in ASIC-DB") + helper.delete_entry_tbl(dvs.adb, "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\""+mac+"\",\"switch_id\":\""+switch_id+"\"}") + + ntf = swsscommon.NotificationProducer(dvs.adb, "NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_3+"\\\",\\\"mac\\\":\\\""+mac+"\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_AGED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+str(tnl_bp_oid_8)+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + print("Creating FDB Vlan3:52-54-00-25-06-E9:Ethernet0 in ASIC-DB") + helper.create_entry_tbl( + dvs.adb, + "ASIC_STATE", "SAI_OBJECT_TYPE_FDB_ENTRY:{\"bvid\":\""+vlan_oid_3+"\",\"mac\":\"52:54:00:25:06:E9\",\"switch_id\":\""+switch_id+"\"}", + [ + ("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"]), + ] + ) + + ntf = swsscommon.NotificationProducer(dvs.adb, "NOTIFICATIONS") + fvp = swsscommon.FieldValuePairs() + ntf_data = "[{\"fdb_entry\":\"{\\\"bvid\\\":\\\""+vlan_oid_3+"\\\",\\\"mac\\\":\\\"52:54:00:25:06:E9\\\",\\\"switch_id\\\":\\\""+switch_id+"\\\"}\",\"fdb_event\":\"SAI_FDB_EVENT_LEARNED\",\"list\":[{\"id\":\"SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID\",\"value\":\""+iface_2_bridge_port_id["Ethernet0"]+"\"}]}]" + ntf.send("fdb_event", ntf_data, fvp) + + time.sleep(2) + + # check that the FDB entry was added in ASIC DB + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52:54:00:25:06:E9"), ("bvid", vlan_oid_3)], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])] + ) + assert ok, str(extra) + print("FDB Vlan3:52-54-00-25-06-E9:Ethernet0 is created in ASIC-DB") + + # check that the FDB entry was added in STATE DB + mac1_found, extra = dvs.is_table_entry_exists(dvs.sdb, "FDB_TABLE", + "Vlan3:52:54:00:25:06:e9", + [("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + assert mac1_found, str(extra) + print("FDB Vlan3:52-54-00-25-06-E9:Ethernet0 is created in STATE-DB") + + + dvs.remove_vlan_member("3", "Ethernet0") + dvs.remove_vlan("3") diff --git a/tests/test_evpn_l3_vxlan.py b/tests/test_evpn_l3_vxlan.py index 6f541b20a6..c7422fd6c1 100644 --- a/tests/test_evpn_l3_vxlan.py +++ b/tests/test_evpn_l3_vxlan.py @@ -4,1007 +4,16 @@ import random import pytest from pprint import pprint - - -def create_entry(tbl, key, pairs): - fvs = swsscommon.FieldValuePairs(pairs) - tbl.set(key, fvs) - time.sleep(1) - - -def create_entry_tbl(db, table, separator, key, pairs): - tbl = swsscommon.Table(db, table) - create_entry(tbl, key, pairs) - -def delete_entry_tbl(db, table, key): - tbl = swsscommon.Table(db, table) - tbl._del(key) - time.sleep(1) - -def create_entry_pst(db, table, separator, key, pairs): - tbl = swsscommon.ProducerStateTable(db, table) - create_entry(tbl, key, pairs) - -def delete_entry_pst(db, table, key): - tbl = swsscommon.ProducerStateTable(db, table) - tbl._del(key) - time.sleep(1) - -def how_many_entries_exist(db, table): - tbl = swsscommon.Table(db, table) - return len(tbl.getKeys()) - - -def entries(db, table): - tbl = swsscommon.Table(db, table) - return set(tbl.getKeys()) - - -def get_exist_entries(dvs, table): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, table) - return set(tbl.getKeys()) - - -def get_created_entry(db, table, existed_entries): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - new_entries = list(entries - existed_entries) - assert len(new_entries) == 1, "Wrong number of created entries." - return new_entries[0] - - -def get_created_entries(db, table, existed_entries, count): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - new_entries = list(entries - existed_entries) - assert len(new_entries) == count, "Wrong number of created entries." - new_entries.sort() - return new_entries - -def get_deleted_entries(db, table, existed_entries, count): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - old_entries = list(existed_entries - entries) - assert len(old_entries) == count, "Wrong number of deleted entries." - old_entries.sort() - return old_entries - -def get_default_vr_id(dvs): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - table = 'ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER' - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert len(keys) == 1, "Wrong number of virtual routers found" - - return keys[0] - -def check_object(db, table, key, expected_attributes): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert key in keys, "The desired key is not presented" - - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - assert len(fvs) >= len(expected_attributes), "Incorrect attributes" - - attr_keys = {entry[0] for entry in fvs} - - for name, value in fvs: - if name in expected_attributes: - assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ - (value, name, expected_attributes[name]) - -def check_deleted_object(db, table, key): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert key not in keys, "The desired key is not removed" - -def get_key_with_attr(db, table, expected_attributes ): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - retkey = list() - #assert key in keys, "The desired key is not presented" - - for key in keys: - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - assert len(fvs) >= len(expected_attributes), "Incorrect attributes" - - attr_keys = {entry[0] for entry in fvs} - - num_match = 0 - for name, value in fvs: - if name in expected_attributes: - if expected_attributes[name] == value: - num_match += 1 - if num_match == len(expected_attributes): - retkey.append(key) - - return retkey - -def create_vrf_routes(dvs, prefix, vrf_name, endpoint, ifname, mac="", vni=0): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - attrs = [ - ("nexthop", endpoint), - ("ifname", ifname), - ] - - if vni: - attrs.append(('vni_label', vni)) - - if mac: - attrs.append(('router_mac', mac)) - - create_entry_pst( - app_db, - "ROUTE_TABLE", ':', "%s:%s" % (vrf_name, prefix), - attrs, - ) - - time.sleep(2) - -def delete_vrf_routes(dvs, prefix, vrf_name): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - delete_entry_pst(app_db, "ROUTE_TABLE", "%s:%s" % (vrf_name, prefix)) - - time.sleep(2) - -def create_vrf_routes_ecmp(dvs, prefix, vrf_name, ecmp_nexthop_attributes): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - create_entry_pst( - app_db, - "ROUTE_TABLE", ':', "%s:%s" % (vrf_name, prefix), - ecmp_nexthop_attributes, - ) - - time.sleep(2) - -def create_vlan(dvs, vlan_name, vlan_ids): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - vlan_id = vlan_name[4:] - - # create vlan - create_entry_tbl( - conf_db, - "VLAN", '|', vlan_name, - [ - ("vlanid", vlan_id), - ], - ) - - time.sleep(1) - - vlan_oid = get_created_entry(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN", vlan_ids) - - check_object(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN", vlan_oid, - { - "SAI_VLAN_ATTR_VLAN_ID": vlan_id, - } - ) - - return vlan_oid - -def remove_vlan(dvs, vlan): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(conf_db, "VLAN") - tbl._del("Vlan" + vlan) - time.sleep(1) - -def create_vlan_member(dvs, vlan, interface, tagging_mode="untagged"): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(conf_db, "VLAN_MEMBER") - fvs = swsscommon.FieldValuePairs([("tagging_mode", tagging_mode)]) - tbl.set("Vlan" + vlan + "|" + interface, fvs) - time.sleep(1) - -def remove_vlan_member(dvs, vlan, interface): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(conf_db, "VLAN_MEMBER") - tbl._del("Vlan" + vlan + "|" + interface) - time.sleep(1) - -def create_vlan_interface(dvs, vlan_name, ifname, vrf_name, ipaddr): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - # create a vlan member in config db - create_entry_tbl( - conf_db, - "VLAN_MEMBER", '|', "%s|%s" % (vlan_name, ifname), - [ - ("tagging_mode", "untagged"), - ], - ) - - time.sleep(1) - - # create vlan interface in config db - create_entry_tbl( - conf_db, - "VLAN_INTERFACE", '|', vlan_name, - [ - ("vrf_name", vrf_name), - ], - ) - - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - create_entry_pst( - app_db, - "INTF_TABLE", ':', vlan_name, - [ - ("vrf_name", vrf_name), - ], - ) - time.sleep(2) - - create_entry_tbl( - conf_db, - "VLAN_INTERFACE", '|', "%s|%s" % (vlan_name, ipaddr), - [ - ("family", "IPv4"), - ], - ) - - time.sleep(2) - - -def delete_vlan_interface(dvs, ifname, ipaddr): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - delete_entry_tbl(conf_db, "VLAN_INTERFACE", "%s|%s" % (ifname, ipaddr)) - time.sleep(2) - - delete_entry_tbl(conf_db, "VLAN_INTERFACE", ifname) - time.sleep(2) - -def create_evpn_nvo(dvs, nvoname, tnlname): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("source_vtep", tnlname), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_EVPN_NVO", '|', nvoname, - attrs, - ) - -def remove_evpn_nvo(dvs, nvoname): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - delete_entry_tbl(conf_db,"VXLAN_EVPN_NVO", nvoname,) - -def create_vxlan_tunnel(dvs, name, src_ip): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("src_ip", src_ip), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_TUNNEL", '|', name, - attrs, - ) - -def create_vxlan_tunnel_map(dvs, tnlname, mapname, vni_id, vlan_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_TUNNEL_MAP", '|', "%s|%s" % (tnlname, mapname), - attrs, - ) - - -def create_vxlan_vrf_tunnel_map(dvs, vrfname, vni_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ] - - # create the VXLAN VRF tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VRF", '|', vrfname, - attrs, - ) - -def remove_vxlan_vrf_tunnel_map(dvs, vrfname): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", "0"), - ] - - # remove the VXLAN VRF tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VRF", '|', vrfname, - attrs, - ) - -def create_evpn_remote_vni(dvs, vlan_id, remotevtep, vnid): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - create_entry_pst( - app_db, - "VXLAN_REMOTE_VNI_TABLE", ':', "%s:%s" % (vlan_id, remotevtep), - [ - ("vni", vnid), - ], - ) - time.sleep(2) - -def remove_vxlan_tunnel(dvs, tnlname): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - conf_db, - "VXLAN_TUNNEL", tnlname, - ) - -def remove_vxlan_tunnel_map(dvs, tnlname, mapname,vni_id, vlan_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - conf_db, - "VXLAN_TUNNEL_MAP", "%s|%s" % (tnlname, mapname), - ) - -def remove_evpn_remote_vni(dvs, vlan_id, remotevtep ): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - delete_entry_pst( - app_db, - "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remotevtep), - ) - time.sleep(2) - -def create_vlan1(dvs, vlan_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - vlan_id = vlan_name[4:] - - # create vlan - create_entry_tbl( - conf_db, - "VLAN", '|', vlan_name, - [ - ("vlanid", vlan_id), - ], - ) - -def get_switch_mac(dvs): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_SWITCH') - - entries = tbl.getKeys() - mac = None - for entry in entries: - status, fvs = tbl.get(entry) - assert status, "Got an error when get a key" - for key, value in fvs: - if key == 'SAI_SWITCH_ATTR_SRC_MAC_ADDRESS': - mac = value - break - else: - assert False, 'Don\'t found switch mac' - - return mac - -loopback_id = 0 -def_vr_id = 0 -switch_mac = None - - -class VxlanTunnel(object): - - ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" - ASIC_TUNNEL_MAP = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP" - ASIC_TUNNEL_MAP_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY" - ASIC_TUNNEL_TERM_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" - ASIC_BRIDGE_PORT = "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT" - ASIC_VRF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER" - ASIC_RIF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE" - ASIC_ROUTE_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY" - ASIC_NEXT_HOP = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP" - ASIC_NEXT_HOP_GRP = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP" - ASIC_NEXT_HOP_GRP_MEMBERS = "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER" - - tunnel_map_ids = set() - tunnel_map_entry_ids = set() - tunnel_map_vrf_entry_ids = set() - tunnel_ids = set() - tunnel_term_ids = set() - bridgeport_ids = set() - tunnel_map_map = {} - tunnel = {} - tunnelterm = {} - mapentry_map = {} - diptunnel_map = {} - diptunterm_map = {} - diptunstate_map = {} - bridgeport_map = {} - vlan_id_map = {} - vlan_member_map = {} - vr_map = {} - vnet_vr_ids = set() - nh_ids = {} - nh_grp_id = set() - nh_grp_member_id = set() - route_id = {} - - def fetch_exist_entries(self, dvs): - self.tunnel_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TABLE) - self.tunnel_map_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP) - self.tunnel_map_entry_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP_ENTRY) - self.tunnel_term_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TERM_ENTRY) - self.bridgeport_ids = get_exist_entries(dvs, self.ASIC_BRIDGE_PORT) - self.vnet_vr_ids = get_exist_entries(dvs, self.ASIC_VRF_TABLE) - self.rifs = get_exist_entries(dvs, self.ASIC_RIF_TABLE) - self.routes = get_exist_entries(dvs, self.ASIC_ROUTE_ENTRY) - self.nhops = get_exist_entries(dvs, self.ASIC_NEXT_HOP) - self.nhop_grp = get_exist_entries(dvs, self.ASIC_NEXT_HOP_GRP) - self.nhop_grp_members = get_exist_entries(dvs, self.ASIC_NEXT_HOP_GRP_MEMBERS) - - global switch_mac - - if switch_mac is None: - switch_mac = get_switch_mac(dvs) - - def check_vxlan_tunnel_map_entry_delete(self, dvs, tunnel_name, vidlist, vnilist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) - - for x in range(1): - status, fvs = tbl.get(self.mapentry_map[tunnel_name + vidlist[x]]) - assert status == False, "SIP Tunnel Map entry not deleted" - - def check_vxlan_tunnel_vlan_map_entry(self, dvs, tunnel_name, vidlist, vnidlist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) - - expected_attributes_1 = { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': self.tunnel_map_map[tunnel_name][0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], - } - - for x in range(1): - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] - ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, expected_attributes_1) - assert len(ret) > 0, "SIP TunnelMap entry not created" - assert len(ret) == 1, "More than 1 SIP TunnMapEntry created" - self.mapentry_map[tunnel_name + vidlist[x]] = ret[0] - - - def check_vxlan_sip_tunnel_delete(self, dvs, tunnel_name): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) - status, fvs = tbl.get(self.tunnelterm[tunnel_name]) - assert status == False, "SIP Tunnel Term entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) - status, fvs = tbl.get(self.tunnel[tunnel_name]) - assert status == False, "SIP Tunnel entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP) - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][0]) - assert status == False, "SIP Tunnel mapper0 not deleted" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][1]) - assert status == False, "SIP Tunnel mapper1 not deleted" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][2]) - assert status == False, "SIP Tunnel mapper2 not deleted" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][3]) - assert status == False, "SIP Tunnel mapper3 not deleted" - - def check_vxlan_sip_tunnel(self, dvs, tunnel_name, src_ip, vidlist, vnidlist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) - tunnel_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TABLE, self.tunnel_ids) - tunnel_term_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, self.tunnel_term_ids) - tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) - - # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP) == (len(self.tunnel_map_ids) + 4), "The TUNNEL_MAP wasn't created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TABLE) == (len(self.tunnel_ids) + 1), "The TUNNEL wasn't created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) == (len(self.tunnel_term_ids) + 1), "The TUNNEL_TERM_TABLE_ENTRY wasm't created" - - check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[2], - { - 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', - } - ) - - check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[3], - { - 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', - } - ) - - decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] - encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] - - check_object(asic_db, self.ASIC_TUNNEL_TABLE, tunnel_id, - { - 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, - 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, - 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, - } - ) - - expected_attributes = { - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE': 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP', - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP': src_ip, - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID': tunnel_id, - } - - check_object(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, tunnel_term_id, expected_attributes) - - expected_attributes_1 = { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], - } - - for x in range(1): - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[x], expected_attributes_1) - - self.tunnel_map_ids.update(tunnel_map_id) - self.tunnel_ids.add(tunnel_id) - self.tunnel_term_ids.add(tunnel_term_id) - self.tunnel_map_map[tunnel_name] = tunnel_map_id - self.tunnel[tunnel_name] = tunnel_id - self.tunnelterm[tunnel_name] = tunnel_term_id - - def check_vxlan_dip_tunnel_delete(self, dvs, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(state_db, 'VXLAN_TUNNEL_TABLE') - status, fvs = tbl.get(self.diptunstate_map[dip]) - assert status == False, "State Table entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) - status, fvs = tbl.get(self.diptunnel_map[dip]) - assert status == False, "Tunnel entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_BRIDGE_PORT) - status, fvs = tbl.get(self.bridgeport_map[dip]) - assert status == False, "Tunnel bridgeport entry not deleted" - - def check_vxlan_dip_tunnel(self, dvs, vtep_name, src_ip, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - expected_state_attributes = { - 'src_ip': src_ip, - 'dst_ip': dip, - 'tnl_src': 'EVPN', - } - - ret = get_key_with_attr(state_db, 'VXLAN_TUNNEL_TABLE', expected_state_attributes) - assert len(ret) > 0, "Tunnel Statetable entry not created" - assert len(ret) == 1, "More than 1 Tunn statetable entry created" - self.diptunstate_map[dip] = ret[0] - - tunnel_map_id = self.tunnel_map_map[vtep_name] - - decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] - encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] - - expected_tun_attributes = { - 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2P', - 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, - 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, - 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, - 'SAI_TUNNEL_ATTR_ENCAP_DST_IP': dip, - } - - ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_TABLE, expected_tun_attributes) - assert len(ret) > 0, "Tunnel entry not created" - assert len(ret) == 1, "More than 1 tunnel entry created" - - self.diptunnel_map[dip] = ret[0] - tunnel_id = ret[0] - - expected_bridgeport_attributes = { - 'SAI_BRIDGE_PORT_ATTR_TYPE': 'SAI_BRIDGE_PORT_TYPE_TUNNEL', - 'SAI_BRIDGE_PORT_ATTR_TUNNEL_ID': tunnel_id, - 'SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE': 'SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE', - 'SAI_BRIDGE_PORT_ATTR_ADMIN_STATE': 'true', - } - - ret = get_key_with_attr(asic_db, self.ASIC_BRIDGE_PORT, expected_bridgeport_attributes) - assert len(ret) > 0, "Bridgeport entry not created" - assert len(ret) == 1, "More than 1 bridgeport entry created" - - self.bridgeport_map[dip] = ret[0] - - def check_vlan_extension_delete(self, dvs, vlan_name, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER') - status, fvs = tbl.get(self.vlan_member_map[dip+vlan_name]) - assert status == False, "VLAN Member entry not deleted" - - def check_vlan_extension(self, dvs, vlan_name, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - expected_vlan_attributes = { - 'SAI_VLAN_ATTR_VLAN_ID': vlan_name, - } - ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN', expected_vlan_attributes) - assert len(ret) > 0, "VLAN entry not created" - assert len(ret) == 1, "More than 1 VLAN entry created" - - self.vlan_id_map[vlan_name] = ret[0] - - expected_vlan_member_attributes = { - 'SAI_VLAN_MEMBER_ATTR_VLAN_ID': self.vlan_id_map[vlan_name], - 'SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID': self.bridgeport_map[dip], - } - ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER', expected_vlan_member_attributes) - assert len(ret) > 0, "VLAN Member not created" - assert len(ret) == 1, "More than 1 VLAN member created" - self.vlan_member_map[dip+vlan_name] = ret[0] - - def check_vxlan_tunnel_vrf_map_entry(self, dvs, tunnel_name, vrf_name, vni_id): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - if (self.tunnel_map_map.get(tunnel_name) is None): - tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) - else: - tunnel_map_id = self.tunnel_map_map[tunnel_name] - - tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) - - # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created too early" - - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1], - { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[3], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY': self.vr_map[vrf_name].get('ing'), - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE': vni_id, - } - ) - - self.tunnel_map_vrf_entry_ids.update(tunnel_map_entry_id[1]) - - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[2], - { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[2], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE': self.vr_map[vrf_name].get('egr'), - } - ) - - self.tunnel_map_vrf_entry_ids.update(tunnel_map_entry_id[2]) - self.tunnel_map_entry_ids.update(tunnel_map_entry_id) - - def check_vxlan_tunnel_vrf_map_entry_remove(self, dvs, tunnel_name, vrf_name, vni_id): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tunnel_map_entry_id = get_deleted_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 2) - check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0]) - check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1]) - for vrf_map_id in self.tunnel_map_vrf_entry_ids: - check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, vrf_map_id) - - def check_router_interface(self, dvs, name, vlan_oid, route_count): - # Check RIF in ingress VRF - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - global switch_mac - - expected_attr = { - "SAI_ROUTER_INTERFACE_ATTR_VIRTUAL_ROUTER_ID": self.vr_map[name].get('ing'), - "SAI_ROUTER_INTERFACE_ATTR_SRC_MAC_ADDRESS": switch_mac, - } - - if vlan_oid: - expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_VLAN'}) - expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_VLAN_ID': vlan_oid}) - else: - expected_attr.update({'SAI_ROUTER_INTERFACE_ATTR_TYPE': 'SAI_ROUTER_INTERFACE_TYPE_PORT'}) - - new_rif = get_created_entry(asic_db, self.ASIC_RIF_TABLE, self.rifs) - check_object(asic_db, self.ASIC_RIF_TABLE, new_rif, expected_attr) - - #IP2ME route will be created with every router interface - new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, route_count) - - self.rifs.add(new_rif) - self.routes.update(new_route) - - def check_del_router_interface(self, dvs, name): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - old_rif = get_deleted_entries(asic_db, self.ASIC_RIF_TABLE, self.rifs, 1) - check_deleted_object(asic_db, self.ASIC_RIF_TABLE, old_rif[0]) - - self.rifs.remove(old_rif[0]) - - def vrf_route_ids(self, dvs, vrf_name): - vr_set = set() - - vr_set.add(self.vr_map[vrf_name].get('ing')) - return vr_set - - def check_vrf_routes(self, dvs, prefix, vrf_name, endpoint, tunnel, mac="", vni=0, no_update=0): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - vr_ids = self.vrf_route_ids(dvs, vrf_name) - count = len(vr_ids) - - # Check routes in ingress VRF - expected_attr = { - "SAI_NEXT_HOP_ATTR_TYPE": "SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP", - "SAI_NEXT_HOP_ATTR_IP": endpoint, - "SAI_NEXT_HOP_ATTR_TUNNEL_ID": self.tunnel[tunnel], - } - - if vni: - expected_attr.update({'SAI_NEXT_HOP_ATTR_TUNNEL_VNI': vni}) - - if mac: - expected_attr.update({'SAI_NEXT_HOP_ATTR_TUNNEL_MAC': mac}) - - if endpoint in self.nh_ids: - new_nh = self.nh_ids[endpoint] - else: - new_nh = get_created_entry(asic_db, self.ASIC_NEXT_HOP, self.nhops) - self.nh_ids[endpoint] = new_nh - self.nhops.add(new_nh) - - check_object(asic_db, self.ASIC_NEXT_HOP, new_nh, expected_attr) - if no_update: - count = 0 - new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, count) - - if count: - self.route_id[vrf_name + ":" + prefix] = new_route - - #Check if the route is in expected VRF - asic_vrs = set() - for idx in range(count): - check_object(asic_db, self.ASIC_ROUTE_ENTRY, new_route[idx], - { - "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": new_nh, - } - ) - rt_key = json.loads(new_route[idx]) - asic_vrs.add(rt_key['vr']) - found_route = False - if rt_key['dest'] == prefix: - found_route = True - - assert found_route - - if count: - assert asic_vrs == vr_ids - - self.routes.update(new_route) - - return True - - def check_vrf_routes_ecmp(self, dvs, prefix, vrf_name, tunnel, nh_count, no_update=0): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - vr_ids = self.vrf_route_ids(dvs, vrf_name) - count = len(vr_ids) - - new_nhg = get_created_entry(asic_db, self.ASIC_NEXT_HOP_GRP, self.nhop_grp) - self.nh_grp_id.add(new_nhg) - - if no_update: - count = 0 - - new_route = get_created_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, count) - if count: - self.route_id[vrf_name + ":" + prefix] = new_route - - #Check if the route is in expected VRF - asic_vrs = set() - for idx in range(count): - check_object(asic_db, self.ASIC_ROUTE_ENTRY, new_route[idx], - { - "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": new_nhg, - } - ) - rt_key = json.loads(new_route[idx]) - asic_vrs.add(rt_key['vr']) - - found_route = False - if rt_key['dest'] == prefix: - found_route = True - - assert found_route - - if count: - assert asic_vrs == vr_ids - - self.routes.update(new_route) - - new_nhg_members = get_created_entries(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, self.nhop_grp_members, nh_count) - - for idx in range(nh_count): - self.nh_grp_member_id.add(new_nhg_members[idx]) - check_object(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, new_nhg_members[idx], - { - "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": new_nhg, - } - ) - - nhid_list = list() - - nhg_member_tbl = swsscommon.Table(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS) - - for k in new_nhg_members: - (status, fvs) = nhg_member_tbl.get(k) - assert status - - for v in fvs: - if v[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID": - nhid = v[1] - nhid_list.append(nhid) - - return nhid_list - - def check_add_tunnel_nexthop(self, dvs, nh_id, endpoint, tunnel, mac, vni): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - # Check routes in ingress VRF - expected_attr = { - "SAI_NEXT_HOP_ATTR_TYPE": "SAI_NEXT_HOP_TYPE_TUNNEL_ENCAP", - "SAI_NEXT_HOP_ATTR_IP": endpoint, - "SAI_NEXT_HOP_ATTR_TUNNEL_ID": self.tunnel[tunnel], - "SAI_NEXT_HOP_ATTR_TUNNEL_VNI": vni, - "SAI_NEXT_HOP_ATTR_TUNNEL_MAC": mac, - } - - check_object(asic_db, self.ASIC_NEXT_HOP, nh_id, expected_attr) - - def check_del_tunnel_nexthop(self, dvs, vrf_name, endpoint, tunnel, mac="", vni=0): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - del_nh_ids = get_deleted_entries(asic_db, self.ASIC_NEXT_HOP, self.nhops, 1) - check_deleted_object(asic_db, self.ASIC_NEXT_HOP, del_nh_ids[0]) - check_deleted_object(asic_db, self.ASIC_NEXT_HOP, self.nh_ids[endpoint]) - assert del_nh_ids[0] == self.nh_ids[endpoint] - self.nh_ids.pop(endpoint) - return True - - def check_vrf_routes_ecmp_nexthop_grp_del(self, dvs, nh_count): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - nh_grp_id_len = len(self.nh_grp_id) - assert nh_grp_id_len == 1 - - for nh_grp_id in self.nh_grp_id: - check_deleted_object(asic_db, self.ASIC_NEXT_HOP_GRP, nh_grp_id) - self.nh_grp_id.clear() - - nh_grp_member_id_len = len(self.nh_grp_member_id) - assert nh_grp_member_id_len == nh_count - - for nh_grp_member_id in self.nh_grp_member_id: - check_deleted_object(asic_db, self.ASIC_NEXT_HOP_GRP_MEMBERS, nh_grp_member_id) - - self.nh_grp_member_id.clear() - - def check_del_vrf_routes(self, dvs, prefix, vrf_name): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - del_route = get_deleted_entries(asic_db, self.ASIC_ROUTE_ENTRY, self.routes, 1) - - for idx in range(1): - rt_key = json.loads(del_route[idx]) - found_route = False - if rt_key['dest'] == prefix: - found_route = True - - assert found_route - - check_deleted_object(asic_db, self.ASIC_ROUTE_ENTRY, self.route_id[vrf_name + ":" + prefix]) - self.route_id.clear() - - return True - - def create_vrf(self, dvs, vrf_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") - initial_entries = set(tbl.getKeys()) - - attrs = [ - ("vni", "0"), - ] - tbl = swsscommon.Table(conf_db, "VRF") - fvs = swsscommon.FieldValuePairs(attrs) - tbl.set(vrf_name, fvs) - time.sleep(2) - - tbl = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") - current_entries = set(tbl.getKeys()) - assert len(current_entries - initial_entries) == 1 - - new_vr_ids = get_created_entries(asic_db, self.ASIC_VRF_TABLE, self.vnet_vr_ids, 1) - self.vnet_vr_ids.update(new_vr_ids) - self.vr_map[vrf_name] = { 'ing':new_vr_ids[0], 'egr':new_vr_ids[0]} - - return list(current_entries - initial_entries)[0] - - def remove_vrf(self, dvs, vrf_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(conf_db, "VRF") - tbl._del(vrf_name) - time.sleep(2) - - - def is_vrf_attributes_correct(self, db, table, key, expected_attributes): - tbl = swsscommon.Table(db, table) - keys = set(tbl.getKeys()) - assert key in keys, "The created key wasn't found" - - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - # filter the fake 'NULL' attribute out - fvs = filter(lambda x : x != ('NULL', 'NULL'), fvs) - - attr_keys = {entry[0] for entry in fvs} - assert attr_keys == set(expected_attributes.keys()) - - for name, value in fvs: - assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ - (value, name, expected_attributes[name]) - +from evpn_tunnel import VxlanTunnel,VxlanEvpnHelper class TestL3Vxlan(object): def get_vxlan_obj(self): return VxlanTunnel() + def get_vxlan_helper(self): + return VxlanEvpnHelper() + def setup_db(self, dvs): self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) @@ -1015,6 +24,7 @@ def setup_db(self, dvs): # @pytest.mark.dev_sanity def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() self.setup_db(dvs) tunnel_name = 'tunnel_2' @@ -1025,15 +35,15 @@ def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): print ("\n\nTesting Create and Delete SIP Tunnel and VRF VNI Map entries") print ("\tCreate SIP Tunnel") - create_vlan1(dvs,"Vlan100") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) print ("\tCreate Vlan-VNI map and VRF-VNI map") - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.create_vrf(dvs, "Vrf-RED") - create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') print ("\tTesting VRF-VNI map in APP DB") vlanlist = ['100'] @@ -1046,7 +56,7 @@ def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): for an in range(len(exp_attrs)): exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] - check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) exp_attrs1 = [ ("vni", "1000"), @@ -1056,31 +66,31 @@ def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): for an in range(len(exp_attrs1)): exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] - check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) print ("\tTesting SIP Tunnel Creation") vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) print ("\tTesting Tunnel Vlan VNI Map Entry") - vxlan_obj.check_vxlan_tunnel_vlan_map_entry(dvs, tunnel_name, vlanlist, vnilist) + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting Tunnel VRF VNI Map Entry") vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting Tunnel VRF VNI Map Entry removal") - remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') vxlan_obj.remove_vrf(dvs, "Vrf-RED") vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting Tunnel Vlan VNI Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - remove_evpn_nvo(dvs, 'nvo1') - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) - remove_vlan(dvs, "100") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.remove_vlan(dvs, "100") # Test 2 - Create and Delete DIP Tunnel on adding and removing prefix route @@ -1088,6 +98,7 @@ def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): # @pytest.mark.dev_sanity def test_prefix_route_create_dip_tunnel(self, dvs, testlog): vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() self.setup_db(dvs) tunnel_name = 'tunnel_2' @@ -1097,17 +108,17 @@ def test_prefix_route_create_dip_tunnel(self, dvs, testlog): print ("\n\nTesting Create and Delete DIP Tunnel on adding and removing prefix route") print ("\tCreate SIP Tunnel") - vlan_ids = get_exist_entries(dvs, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") - vlan_oid = create_vlan(dvs,"Vlan100", vlan_ids) - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vlan_ids = helper.get_exist_entries(dvs, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_oid = vxlan_obj.create_vlan(dvs,"Vlan100", vlan_ids) + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) print ("\tCreate Vlan-VNI map and VRF-VNI map") - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') print ("\tTesting VRF-VNI map in APP DB") vxlan_obj.create_vrf(dvs, "Vrf-RED") - create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') vlanlist = ['100'] vnilist = ['1000'] @@ -1119,7 +130,7 @@ def test_prefix_route_create_dip_tunnel(self, dvs, testlog): for an in range(len(exp_attrs)): exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] - check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) exp_attrs1 = [ ("vni", "1000"), @@ -1129,31 +140,31 @@ def test_prefix_route_create_dip_tunnel(self, dvs, testlog): for an in range(len(exp_attrs1)): exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] - check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) print ("\tTesting SIP Tunnel Creation") vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) print ("\tTesting Tunnel Vlan Map Entry") - vxlan_obj.check_vxlan_tunnel_vlan_map_entry(dvs, tunnel_name, vlanlist, vnilist) + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting Tunnel Vrf Map Entry") vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting VLAN 100 interface creation") - create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vlan_oid, 2) print ("\tTest VRF IPv4 Route with Tunnel Nexthop Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTesting DIP tunnel 7.7.7.7 creation") vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') print ("\tTest VRF IPv4 Route with Tunnel Nexthop Delete") - delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -1161,24 +172,24 @@ def test_prefix_route_create_dip_tunnel(self, dvs, testlog): vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '7.7.7.7') print ("\tTesting Tunnel Vrf Map Entry removal") - remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting Vlan 100 interface delete") - delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") vxlan_obj.check_del_router_interface(dvs, "Vlan100") print ("\tTesting Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - remove_evpn_nvo(dvs, 'nvo1') - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') vxlan_obj.remove_vrf(dvs, "Vrf-RED") - remove_vlan_member(dvs, "100", "Ethernet24") - remove_vlan(dvs, "100") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") # Test 3 - Create and Delete DIP Tunnel and Test IPv4 route and overlay nexthop add and delete @@ -1186,6 +197,7 @@ def test_prefix_route_create_dip_tunnel(self, dvs, testlog): # @pytest.mark.dev_sanity def test_dip_tunnel_ipv4_routes(self, dvs, testlog): vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() self.setup_db(dvs) tunnel_name = 'tunnel_2' @@ -1195,15 +207,15 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\n\nTesting IPv4 Route and Overlay Nexthop Add and Delete") print ("\tCreate SIP Tunnel") - create_vlan1(dvs,"Vlan100") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) print ("\tCreate Vlan-VNI map and VRF-VNI map") - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.create_vrf(dvs, "Vrf-RED") - create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') print ("\tTesting VRF-VNI map in APP DB") vlanlist = ['100'] @@ -1216,7 +228,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): for an in range(len(exp_attrs)): exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] - check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) exp_attrs1 = [ ("vni", "1000"), @@ -1226,26 +238,26 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): for an in range(len(exp_attrs1)): exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] - check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) print ("\tTesting SIP Tunnel Creation") vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) print ("\tTesting Tunnel Vlan Map Entry") - vxlan_obj.check_vxlan_tunnel_vlan_map_entry(dvs, tunnel_name, vlanlist, vnilist) + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting Tunnel Vrf Map Entry") vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting First DIP tunnel creation to 7.7.7.7") - create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') print ("\tTesting VLAN 100 extension") vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') print ("\tTesting Second DIP tunnel creation to 8.8.8.8") - create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '8.8.8.8') print ("\tTesting VLAN 100 extension to 8.8.8.8 and 7.7.7.7") @@ -1253,27 +265,27 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') print ("\tTesting VLAN 100 interface creation") - create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vxlan_obj.vlan_id_map['100'], 2) print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Delete") - delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') print ("\n\nTesting IPv4 Route and Overlay Nexthop Update") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") @@ -1281,7 +293,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route and Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -1296,7 +308,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): ] print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") - create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count) @@ -1306,9 +318,9 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') - check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) - check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') @@ -1316,7 +328,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from non-ECMP to ECMP") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') ecmp_nexthop_attr = [ @@ -1327,7 +339,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): ] print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") - create_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count, 1) @@ -1337,7 +349,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from ECMP to non-ECMP") print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Update") - create_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") @@ -1348,41 +360,41 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') print ("\n\nTest DIP and SIP Tunnel Deletion ") print ("\tTesting Tunnel Vrf VNI Map Entry removal") - remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting LastVlan removal and DIP tunnel delete for 7.7.7.7") - remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') vxlan_obj.check_vlan_extension_delete(dvs, '100', '7.7.7.7') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '7.7.7.7') print ("\tTesting LastVlan removal and DIP tunnel delete for 8.8.8.8") - remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') vxlan_obj.check_vlan_extension_delete(dvs, '100', '8.8.8.8') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '8.8.8.8') print ("\tTesting Vlan 100 interface delete") - delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") vxlan_obj.check_del_router_interface(dvs, "Vlan100") print ("\tTesting Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - remove_evpn_nvo(dvs, 'nvo1') - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') vxlan_obj.remove_vrf(dvs, "Vrf-RED") - remove_vlan_member(dvs, "100", "Ethernet24") - remove_vlan(dvs, "100") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") # Test 4 - Create and Delete DIP Tunnel and Test IPv6 route and overlay nexthop add and delete @@ -1390,6 +402,7 @@ def test_dip_tunnel_ipv4_routes(self, dvs, testlog): # @pytest.mark.dev_sanity def test_dip_tunnel_ipv6_routes(self, dvs, testlog): vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() self.setup_db(dvs) tunnel_name = 'tunnel_2' @@ -1399,16 +412,16 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\n\nTesting IPv6 Route and Overlay Nexthop Add and Delete") print ("\tCreate SIP Tunnel") - create_vlan1(dvs,"Vlan100") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) print ("\tCreate Vlan-VNI map and VRF-VNI map") - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') print ("\tTesting VRF-VNI map in APP DB") vxlan_obj.create_vrf(dvs, "Vrf-RED") - create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') vlanlist = ['100'] vnilist = ['1000'] @@ -1421,7 +434,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] print ("\tCheck VRF Table in APP DB") - check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) exp_attrs1 = [ ("vni", "1000"), @@ -1431,26 +444,26 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): for an in range(len(exp_attrs1)): exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] - check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) print ("\tTesting SIP Tunnel Creation") vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) print ("\tTesting Tunnel Vlan Map Entry") - vxlan_obj.check_vxlan_tunnel_vlan_map_entry(dvs, tunnel_name, vlanlist, vnilist) + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting Tunnel Vrf Map Entry") vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting First DIP tunnel creation to 7.7.7.7") - create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') print ("\tTesting VLAN 100 extension") vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') print ("\tTesting Second DIP tunnel creation to 8.8.8.8") - create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '8.8.8.8') print ("\tTesting VLAN 100 extension to 8.8.8.8 and 7.7.7.7") @@ -1459,27 +472,27 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): vxlan_obj.fetch_exist_entries(dvs) print ("\tTesting VLAN 100 interface creation") - create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "2001::8/64") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "2001::8/64") vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vxlan_obj.vlan_id_map['100'], 2) print ("\tTest VRF IPv6 Route with Tunnel Nexthop Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv6 Route with Tunnel Nexthop Delete") - delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') print ("\n\nTesting IPv6 Route and Overlay Nexthop Update") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") - create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") @@ -1487,7 +500,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route and Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') @@ -1502,7 +515,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): ] print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") - create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count) @@ -1512,9 +525,9 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') - check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) - check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') @@ -1522,7 +535,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from non-ECMP to ECMP") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") vxlan_obj.fetch_exist_entries(dvs) - create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") @@ -1533,7 +546,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), ] - create_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + vxlan_obj.create_vrf_route_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) nh_count = 2 ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count, 1) @@ -1543,7 +556,7 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from ECMP to non-ECMP") print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Update") - create_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") @@ -1554,41 +567,41 @@ def test_dip_tunnel_ipv6_routes(self, dvs, testlog): print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Delete") vxlan_obj.fetch_exist_entries(dvs) - delete_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') print ("\n\nTest DIP and SIP Tunnel Deletion ") print ("\tTesting Tunnel Vrf Map Entry removal") - remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') print ("\tTesting LastVlan removal and DIP tunnel delete for 7.7.7.7") - remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') vxlan_obj.check_vlan_extension_delete(dvs, '100', '7.7.7.7') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '7.7.7.7') print ("\tTesting LastVlan removal and DIP tunnel delete for 8.8.8.8") - remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') vxlan_obj.check_vlan_extension_delete(dvs, '100', '8.8.8.8') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '8.8.8.8') print ("\tTesting Vlan 100 interface delete") - delete_vlan_interface(dvs, "Vlan100", "2001::8/64") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "2001::8/64") vxlan_obj.check_del_router_interface(dvs, "Vlan100") print ("\tTesting Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print ("\tTesting SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - remove_evpn_nvo(dvs, 'nvo1') - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') vxlan_obj.remove_vrf(dvs, "Vrf-RED") - remove_vlan_member(dvs, "100", "Ethernet24") - remove_vlan(dvs, "100") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") # Add Dummy always-pass test at end as workaroud diff --git a/tests/test_evpn_l3_vxlan_p2mp.py b/tests/test_evpn_l3_vxlan_p2mp.py new file mode 100644 index 0000000000..5edeb7d8f5 --- /dev/null +++ b/tests/test_evpn_l3_vxlan_p2mp.py @@ -0,0 +1,596 @@ +from swsscommon import swsscommon +import time +import json +import random +import pytest +from pprint import pprint +from evpn_tunnel import VxlanTunnel,VxlanEvpnHelper + +DVS_FAKE_PLATFORM = "mellanox" + +class TestL3VxlanP2MP(object): + + def get_vxlan_obj(self): + return VxlanTunnel() + + def get_vxlan_helper(self): + return VxlanEvpnHelper() + + def setup_db(self, dvs): + self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + +# Test 1 - Create and Delete SIP Tunnel and VRF VNI Map entries +# @pytest.mark.skip(reason="Starting Route Orch, VRF Orch to be merged") +# @pytest.mark.dev_sanity + def test_sip_tunnel_vrf_vni_map(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() + + self.setup_db(dvs) + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + vrf_map_name = 'evpn_map_1000_Vrf-RED' + + vxlan_obj.fetch_exist_entries(dvs) + + print ("\n\nTesting Create and Delete SIP Tunnel and VRF VNI Map entries") + print ("\tCreate SIP Tunnel") + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + + print ("\tCreate Vlan-VNI map and VRF-VNI map") + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + + vxlan_obj.create_vrf(dvs, "Vrf-RED") + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + + print ("\tTesting VRF-VNI map in APP DB") + vlanlist = ['100'] + vnilist = ['1000'] + + exp_attrs = [ + ("vni", "1000"), + ] + exp_attr = {} + for an in range(len(exp_attrs)): + exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] + + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + + exp_attrs1 = [ + ("vni", "1000"), + ("vlan", "Vlan100"), + ] + exp_attr1 = {} + for an in range(len(exp_attrs1)): + exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] + + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + + print ("\tTesting SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + + print ("\tTesting Tunnel Vlan VNI Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting Tunnel VRF VNI Map Entry") + vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting Tunnel VRF VNI Map Entry removal") + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.remove_vrf(dvs, "Vrf-RED") + vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting Tunnel Vlan VNI Map entry removal") + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting SIP Tunnel Deletion") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.remove_vlan(dvs, "100") + + +# Test 2 - Remote end point add +# @pytest.mark.skip(reason="Starting Route Orch, VRF Orch to be merged") +# @pytest.mark.dev_sanity + def test_prefix_route_create_remote_endpoint(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() + + self.setup_db(dvs) + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + vrf_map_name = 'evpn_map_1000_Vrf-RED' + vxlan_obj.fetch_exist_entries(dvs) + + print ("\tCreate SIP Tunnel") + vlan_ids = vxlan_obj.helper.get_exist_entries(dvs, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_oid = vxlan_obj.create_vlan(dvs,"Vlan100", vlan_ids) + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + + print ("\tCreate Vlan-VNI map and VRF-VNI map") + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + + print ("\tTesting VRF-VNI map in APP DB") + vxlan_obj.create_vrf(dvs, "Vrf-RED") + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + + vlanlist = ['100'] + vnilist = ['1000'] + + exp_attrs = [ + ("vni", "1000"), + ] + exp_attr = {} + for an in range(len(exp_attrs)): + exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] + + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + + exp_attrs1 = [ + ("vni", "1000"), + ("vlan", "Vlan100"), + ] + exp_attr1 = {} + for an in range(len(exp_attrs1)): + exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] + + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + + print ("\tTesting SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + + print ("\tTesting Tunnel Vlan Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting Tunnel Vrf Map Entry") + vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting VLAN 100 interface creation") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") + vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vlan_oid, 2) + + print ("\tTest VRF IPv4 Route with Tunnel Nexthop Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv4 Route with Tunnel Nexthop Delete") + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + + print ("\tTesting Tunnel Vrf Map Entry removal") + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting Vlan 100 interface delete") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") + vxlan_obj.check_del_router_interface(dvs, "Vlan100") + + print ("\tTesting Tunnel Map entry removal") + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting SIP Tunnel Deletion") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.remove_vrf(dvs, "Vrf-RED") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") + + +# Test 3 - Create and Delete remote end point and Test IPv4 route and overlay nexthop add and delete +# @pytest.mark.dev_sanity + def test_remote_ipv4_routes(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() + + self.setup_db(dvs) + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + vrf_map_name = 'evpn_map_1000_Vrf-RED' + vxlan_obj.fetch_exist_entries(dvs) + + print ("\n\nTesting IPv4 Route and Overlay Nexthop Add and Delete") + print ("\tCreate SIP Tunnel") + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + + print ("\tCreate Vlan-VNI map and VRF-VNI map") + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + + vxlan_obj.create_vrf(dvs, "Vrf-RED") + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + + print ("\tTesting VRF-VNI map in APP DB") + vlanlist = ['100'] + vnilist = ['1000'] + + exp_attrs = [ + ("vni", "1000"), + ] + exp_attr = {} + for an in range(len(exp_attrs)): + exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] + + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + + exp_attrs1 = [ + ("vni", "1000"), + ("vlan", "Vlan100"), + ] + exp_attr1 = {} + for an in range(len(exp_attrs1)): + exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] + + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + + print ("\tTesting SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + + print ("\tTesting Tunnel Vlan Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting Tunnel Vrf Map Entry") + vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting First Remote end point to 7.7.7.7") + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + + print ("\tTesting VLAN 100 extension") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print ("\tTesting Second remote end point to 8.8.8.8") + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + + print ("\tTesting VLAN 100 extension to 8.8.8.8 and 7.7.7.7") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print ("\tTesting VLAN 100 interface creation") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "100.100.3.1/24") + vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vxlan_obj.vlan_id_map['100'], 2) + + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Delete") + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + + print ("\n\nTesting IPv4 Route and Overlay Nexthop Update") + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) + + print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv4 Route and Tunnel Nexthop 8.8.8.8 Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + + print ("\n\nTest VRF IPv4 Route with ECMP Tunnel Nexthop Add and Delete") + vxlan_obj.fetch_exist_entries(dvs) + + ecmp_nexthop_attr = [ + ("nexthop", "7.7.7.7,8.8.8.8"), + ("ifname", "Vlan100,Vlan100"), + ("vni_label", "1000,1000"), + ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), + ] + + print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") + vxlan_obj.create_vrf_route_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + + nh_count = 2 + ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count) + assert nh_count == len(ecmp_nhid_list) + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[0], '7.7.7.7', tunnel_name, '00:11:11:11:11:11', '1000') + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[1], '8.8.8.8', tunnel_name, '00:22:22:22:22:22', '1000') + + print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) + + vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) + vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + + print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from non-ECMP to ECMP") + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 7.7.7.7 Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + ecmp_nexthop_attr = [ + ("nexthop", "7.7.7.7,8.8.8.8"), + ("ifname", "Vlan100,Vlan100"), + ("vni_label", "1000,1000"), + ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), + ] + + print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") + vxlan_obj.create_vrf_route_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', ecmp_nexthop_attr) + + nh_count = 2 + ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "80.80.1.0/24", 'Vrf-RED', tunnel_name, nh_count, 1) + assert nh_count == len(ecmp_nhid_list) + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[0], '7.7.7.7', tunnel_name, '00:11:11:11:11:11', '1000') + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[1], '8.8.8.8', tunnel_name, '00:22:22:22:22:22', '1000') + + print ("\n\nTest VRF IPv4 Route with Tunnel Nexthop update from ECMP to non-ECMP") + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Update") + vxlan_obj.create_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.check_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) + + print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest Tunnel Nexthop ECMP Group is deleted") + vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) + + print ("\tTest VRF IPv4 Route with Tunnel Nexthop 8.8.8.8 Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_route(dvs, "80.80.1.0/24", 'Vrf-RED') + + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "80.80.1.0/24", 'Vrf-RED') + + print ("\n\nTest Remote end point remove and SIP Tunnel Deletion ") + print ("\tTesting Tunnel Vrf VNI Map Entry removal") + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting LastVlan removal and remote end point delete for 7.7.7.7") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print ("\tTesting LastVlan removal and remote end point delete for 8.8.8.8") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + + print ("\tTesting Vlan 100 interface delete") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "100.100.3.1/24") + vxlan_obj.check_del_router_interface(dvs, "Vlan100") + + print ("\tTesting Tunnel Map entry removal") + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting SIP Tunnel Deletion") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.remove_vrf(dvs, "Vrf-RED") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") + + +# Test 4 - Create and Delete remote endpoint and Test IPv6 route and overlay nexthop add and delete +# @pytest.mark.skip(reason="Starting Route Orch, VRF Orch to be merged") +# @pytest.mark.dev_sanity + def test_remote_ipv6_routes(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + helper = self.get_vxlan_helper() + + self.setup_db(dvs) + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + vrf_map_name = 'evpn_map_1000_Vrf-RED' + vxlan_obj.fetch_exist_entries(dvs) + + print ("\n\nTesting IPv6 Route and Overlay Nexthop Add and Delete") + print ("\tCreate SIP Tunnel") + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + + print ("\tCreate Vlan-VNI map and VRF-VNI map") + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + + print ("\tTesting VRF-VNI map in APP DB") + vxlan_obj.create_vrf(dvs, "Vrf-RED") + vxlan_obj.create_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED', '1000') + + vlanlist = ['100'] + vnilist = ['1000'] + + exp_attrs = [ + ("vni", "1000"), + ] + exp_attr = {} + for an in range(len(exp_attrs)): + exp_attr[exp_attrs[an][0]] = exp_attrs[an][1] + + print ("\tCheck VRF Table in APP DB") + helper.check_object(self.pdb, "VRF_TABLE", 'Vrf-RED', exp_attr) + + exp_attrs1 = [ + ("vni", "1000"), + ("vlan", "Vlan100"), + ] + exp_attr1 = {} + for an in range(len(exp_attrs1)): + exp_attr1[exp_attrs1[an][0]] = exp_attrs1[an][1] + + helper.check_object(self.pdb, "VXLAN_VRF_TABLE", "%s:%s" % (tunnel_name, vrf_map_name), exp_attr1) + + print ("\tTesting SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + + print ("\tTesting Tunnel Vlan Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting Tunnel Vrf Map Entry") + vxlan_obj.check_vxlan_tunnel_vrf_map_entry(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting First remote endpoint creation to 7.7.7.7") + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + + print ("\tTesting VLAN 100 extension") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print ("\tTesting Second remote endpoint creation to 8.8.8.8") + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + + print ("\tTesting VLAN 100 extension to 8.8.8.8 and 7.7.7.7") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + vxlan_obj.fetch_exist_entries(dvs) + print ("\tTesting VLAN 100 interface creation") + vxlan_obj.create_vlan_interface(dvs, "Vlan100", "Ethernet24", "Vrf-RED", "2001::8/64") + vxlan_obj.check_router_interface(dvs, 'Vrf-RED', vxlan_obj.vlan_id_map['100'], 2) + + print ("\tTest VRF IPv6 Route with Tunnel Nexthop Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv6 Route with Tunnel Nexthop Delete") + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + + print ("\n\nTesting IPv6 Route and Overlay Nexthop Update") + print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest Tunnel Nexthop change from 7.7.7.7 to 8.8.8.8") + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) + + print ("\tTest Previous Tunnel Nexthop 7.7.7.7 is removed") + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv6 Route and Tunnel Nexthop 8.8.8.8 Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + + print ("\n\nTest VRF IPv6 Route with ECMP Tunnel Nexthop Add and delete") + vxlan_obj.fetch_exist_entries(dvs) + + ecmp_nexthop_attr = [ + ("nexthop", "7.7.7.7,8.8.8.8"), + ("ifname", "Vlan100,Vlan100"), + ("vni_label", "1000,1000"), + ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), + ] + + print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Add") + vxlan_obj.create_vrf_route_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + + nh_count = 2 + ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count) + assert nh_count == len(ecmp_nhid_list) + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[0], '7.7.7.7', tunnel_name, '00:11:11:11:11:11', '1000') + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[1], '8.8.8.8', tunnel_name, '00:22:22:22:22:22', '1000') + + print ("\tTest VRF IPv6 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[0]) + helper.check_deleted_object(self.adb, vxlan_obj.ASIC_NEXT_HOP, ecmp_nhid_list[1]) + + vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) + vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + + print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from non-ECMP to ECMP") + print ("\tTest VRF IPv6 Route with Tunnel Nexthop 7.7.7.7 Add") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', "Vlan100", "00:11:11:11:11:11", '1000') + vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest VRF IPv4 Route with ECMP Tunnel Nexthop [7.7.7.7 , 8.8.8.8] Udpate") + ecmp_nexthop_attr = [ + ("nexthop", "7.7.7.7,8.8.8.8"), + ("ifname", "Vlan100,Vlan100"), + ("vni_label", "1000,1000"), + ("router_mac", "00:11:11:11:11:11,00:22:22:22:22:22"), + ] + + vxlan_obj.create_vrf_route_ecmp(dvs, "2002::8/64", 'Vrf-RED', ecmp_nexthop_attr) + + nh_count = 2 + ecmp_nhid_list = vxlan_obj.check_vrf_routes_ecmp(dvs, "2002::8/64", 'Vrf-RED', tunnel_name, nh_count, 1) + assert nh_count == len(ecmp_nhid_list) + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[0], '7.7.7.7', tunnel_name, '00:11:11:11:11:11', '1000') + vxlan_obj.check_add_tunnel_nexthop(dvs, ecmp_nhid_list[1], '8.8.8.8', tunnel_name, '00:22:22:22:22:22', '1000') + + print ("\n\nTest VRF IPv6 Route with Tunnel Nexthop update from ECMP to non-ECMP") + print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Update") + vxlan_obj.create_vrf_route(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', "Vlan100", "00:22:22:22:22:22", '1000') + vxlan_obj.check_vrf_routes(dvs, "2002::8/64", 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000', 1) + + print ("\tTest Tunnel Nexthop 7.7.7.7 is deleted") + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '7.7.7.7', tunnel_name, "00:11:11:11:11:11", '1000') + + print ("\tTest Tunnel Nexthop ECMP Group is deleted") + vxlan_obj.check_vrf_routes_ecmp_nexthop_grp_del(dvs, 2) + + print ("\tTest VRF IPv6 Route with Tunnel Nexthop 8.8.8.8 Delete") + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.delete_vrf_route(dvs, "2002::8/64", 'Vrf-RED') + + vxlan_obj.check_del_tunnel_nexthop(dvs, 'Vrf-RED', '8.8.8.8', tunnel_name, "00:22:22:22:22:22", '1000') + vxlan_obj.check_del_vrf_routes(dvs, "2002::8/64", 'Vrf-RED') + + print ("\n\nTest remote endpoint and SIP Tunnel Deletion ") + print ("\tTesting Tunnel Vrf Map Entry removal") + vxlan_obj.remove_vxlan_vrf_tunnel_map(dvs, 'Vrf-RED') + vxlan_obj.check_vxlan_tunnel_vrf_map_entry_remove(dvs, tunnel_name, 'Vrf-RED', '1000') + + print ("\tTesting LastVlan removal and remote endpoint delete for 7.7.7.7") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print ("\tTesting LastVlan removal and remote endpoint delete for 8.8.8.8") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + + print ("\tTesting Vlan 100 interface delete") + vxlan_obj.delete_vlan_interface(dvs, "Vlan100", "2001::8/64") + vxlan_obj.check_del_router_interface(dvs, "Vlan100") + + print ("\tTesting Tunnel Map entry removal") + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print ("\tTesting SIP Tunnel Deletion") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.remove_vrf(dvs, "Vrf-RED") + vxlan_obj.remove_vlan_member(dvs, "100", "Ethernet24") + vxlan_obj.remove_vlan(dvs, "100") + + +# 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_evpn_tunnel.py b/tests/test_evpn_tunnel.py index c8a150595f..69918f4b1e 100644 --- a/tests/test_evpn_tunnel.py +++ b/tests/test_evpn_tunnel.py @@ -4,532 +4,9 @@ import random import pytest from pprint import pprint +from evpn_tunnel import VxlanTunnel - -def create_entry(tbl, key, pairs): - fvs = swsscommon.FieldValuePairs(pairs) - tbl.set(key, fvs) - time.sleep(1) - -def create_entry_tbl(db, table, separator, key, pairs): - tbl = swsscommon.Table(db, table) - create_entry(tbl, key, pairs) - -def delete_entry_tbl(db, table, key): - tbl = swsscommon.Table(db, table) - tbl._del(key) - time.sleep(1) - -def create_entry_pst(db, table, separator, key, pairs): - tbl = swsscommon.ProducerStateTable(db, table) - create_entry(tbl, key, pairs) - -def delete_entry_pst(db, table, key): - tbl = swsscommon.ProducerStateTable(db, table) - tbl._del(key) - time.sleep(1) - -def how_many_entries_exist(db, table): - tbl = swsscommon.Table(db, table) - return len(tbl.getKeys()) - - -def entries(db, table): - tbl = swsscommon.Table(db, table) - return set(tbl.getKeys()) - -def get_exist_entries(dvs, table): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - tbl = swsscommon.Table(db, table) - return set(tbl.getKeys()) - - -def get_created_entry(db, table, existed_entries): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - new_entries = list(entries - existed_entries) - assert len(new_entries) == 1, "Wrong number of created entries." - return new_entries[0] - -def get_created_entries(db, table, existed_entries, count): - tbl = swsscommon.Table(db, table) - entries = set(tbl.getKeys()) - new_entries = list(entries - existed_entries) - assert len(new_entries) == count, "Wrong number of created entries." - new_entries.sort() - return new_entries - -def get_default_vr_id(dvs): - db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - table = 'ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER' - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert len(keys) == 1, "Wrong number of virtual routers found" - - return keys[0] - - -def check_object(db, table, key, expected_attributes): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - assert key in keys, "The desired key is not presented" - - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - assert len(fvs) >= len(expected_attributes), "Incorrect attributes" - - attr_keys = {entry[0] for entry in fvs} - - for name, value in fvs: - if name in expected_attributes: - assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ - (value, name, expected_attributes[name]) - -def get_key_with_attr(db, table, expected_attributes ): - tbl = swsscommon.Table(db, table) - keys = tbl.getKeys() - retkey = list() - #assert key in keys, "The desired key is not presented" - - for key in keys: - status, fvs = tbl.get(key) - assert status, "Got an error when get a key" - - assert len(fvs) >= len(expected_attributes), "Incorrect attributes" - - attr_keys = {entry[0] for entry in fvs} - - num_match = 0 - for name, value in fvs: - if name in expected_attributes: - if expected_attributes[name] == value: - num_match += 1 - if num_match == len(expected_attributes): - retkey.append(key) - - return retkey - - -def create_evpn_nvo(dvs, nvoname, tnl_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("source_vtep", tnl_name), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_EVPN_NVO", '|', nvoname, - attrs, - ) - -def remove_evpn_nvo(dvs, nvoname): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - delete_entry_tbl(conf_db,"VXLAN_EVPN_NVO", nvoname,) - -def create_vxlan_tunnel(dvs, name, src_ip, dst_ip = '0.0.0.0', skip_dst_ip=True): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("src_ip", src_ip), - ] - - if not skip_dst_ip: - attrs.append(("dst_ip", dst_ip)) - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_TUNNEL", '|', name, - attrs, - ) - -def create_vxlan_tunnel_map(dvs, tnl_name, map_name, vni_id, vlan_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - create_entry_tbl( - conf_db, - "VXLAN_TUNNEL_MAP", '|', "%s|%s" % (tnl_name, map_name), - attrs, - ) - -def create_evpn_remote_vni(dvs, vlan_id, remote_vtep, vnid): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - create_entry_pst( - app_db, - "VXLAN_REMOTE_VNI_TABLE", ':', "%s:%s" % (vlan_id, remote_vtep), - [ - ("vni", vnid), - ], - ) - time.sleep(2) - -def remove_vxlan_tunnel(dvs, tnl_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - conf_db, - "VXLAN_TUNNEL", tnl_name, - ) - -def remove_vxlan_tunnel_map(dvs, tnl_name, map_name,vni_id, vlan_id): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - attrs = [ - ("vni", vni_id), - ("vlan", vlan_id), - ] - - # create the VXLAN tunnel Term entry in Config DB - delete_entry_tbl( - conf_db, - "VXLAN_TUNNEL_MAP", "%s|%s" % (tnl_name, map_name), - ) - -def remove_evpn_remote_vni(dvs, vlan_id, remote_vtep ): - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - delete_entry_pst( - app_db, - "VXLAN_REMOTE_VNI_TABLE", "%s:%s" % (vlan_id, remote_vtep), - ) - time.sleep(2) - -def create_vlan1(dvs, vlan_name): - conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - - vlan_id = vlan_name[4:] - - # create vlan - create_entry_tbl( - conf_db, - "VLAN", '|', vlan_name, - [ - ("vlanid", vlan_id), - ], - ) - -loopback_id = 0 -def_vr_id = 0 -switch_mac = None - -class VxlanTunnel(object): - - ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" - ASIC_TUNNEL_MAP = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP" - ASIC_TUNNEL_MAP_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY" - ASIC_TUNNEL_TERM_ENTRY = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" - ASIC_BRIDGE_PORT = "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT" - - tunnel_map_ids = set() - tunnel_map_entry_ids = set() - tunnel_ids = set() - tunnel_term_ids = set() - bridgeport_ids = set() - tunnel_map_map = {} - tunnel = {} - tunnel_appdb = {} - tunnel_term = {} - map_entry_map = {} - dip_tunnel_map = {} - dip_tun_state_map = {} - bridgeport_map = {} - vlan_id_map = {} - vlan_member_map = {} - - def fetch_exist_entries(self, dvs): - self.tunnel_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TABLE) - self.tunnel_map_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP) - self.tunnel_map_entry_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_MAP_ENTRY) - self.tunnel_term_ids = get_exist_entries(dvs, self.ASIC_TUNNEL_TERM_ENTRY) - self.bridgeport_ids = get_exist_entries(dvs, self.ASIC_BRIDGE_PORT) - - def check_vxlan_tunnel_map_entry_delete(self, dvs, tunnel_name, vidlist, vnilist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) - - for x in range(len(vidlist)): - status, fvs = tbl.get(self.map_entry_map[tunnel_name + vidlist[x]]) - assert status == False, "SIP Tunnel Map entry not deleted" - iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] - (exitcode, out) = dvs.runcmd(iplinkcmd) - assert exitcode != 0, "Kernel device not deleted" - - def check_vxlan_tunnel_map_entry(self, dvs, tunnel_name, vidlist, vnidlist): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) - - expected_attributes_1 = { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': self.tunnel_map_map[tunnel_name][0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], - } - - for x in range(len(vidlist)): - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] - ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, expected_attributes_1) - assert len(ret) > 0, "SIP TunnelMap entry not created" - assert len(ret) == 1, "More than 1 SIP TunnMapEntry created" - self.map_entry_map[tunnel_name + vidlist[x]] = ret[0] - iplinkcmd = "ip link show type vxlan dev " + tunnel_name + "-" + vidlist[x] - (exitcode, out) = dvs.runcmd(iplinkcmd) - assert exitcode == 0, "Kernel device not created" - - - def check_vxlan_sip_tunnel_delete(self, dvs, tunnel_name): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(app_db, "VXLAN_TUNNEL_TABLE") - status, fvs = tbl.get(self.tunnel_appdb[tunnel_name]) - assert status == False, "SIP Tunnel entry not deleted from APP_DB" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) - status, fvs = tbl.get(self.tunnel_term[tunnel_name]) - assert status == False, "SIP Tunnel Term entry not deleted from ASIC_DB" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) - status, fvs = tbl.get(self.tunnel[tunnel_name]) - assert status == False, "SIP Tunnel entry not deleted from ASIC_DB" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_MAP) - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][0]) - assert status == False, "SIP Tunnel mapper0 not deleted from ASIC_DB" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][1]) - assert status == False, "SIP Tunnel mapper1 not deleted from ASIC_DB" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][2]) - assert status == False, "SIP Tunnel mapper2 not deleted from ASIC_DB" - status, fvs = tbl.get(self.tunnel_map_map[tunnel_name][3]) - assert status == False, "SIP Tunnel mapper3 not deleted from ASIC_DB" - - def check_vxlan_sip_tunnel(self, dvs, tunnel_name, src_ip, vidlist, vnidlist, dst_ip = '0.0.0.0', skip_dst_ip = 'True'): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) - tunnel_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TABLE, self.tunnel_ids) - tunnel_term_id = get_created_entry(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, self.tunnel_term_ids) - tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 3) - - # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP) == (len(self.tunnel_map_ids) + 4), "The TUNNEL_MAP wasn't created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 3), "The TUNNEL_MAP_ENTRY is created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TABLE) == (len(self.tunnel_ids) + 1), "The TUNNEL wasn't created" - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_TERM_ENTRY) == (len(self.tunnel_term_ids) + 1), "The TUNNEL_TERM_TABLE_ENTRY wasm't created" - - check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[2], - { - 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', - } - ) - - check_object(asic_db, self.ASIC_TUNNEL_MAP, tunnel_map_id[3], - { - 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', - } - ) - - decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] - encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] - #'SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE': loopback_id, - - check_object(asic_db, self.ASIC_TUNNEL_TABLE, tunnel_id, - { - 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, - 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, - 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2MP', - 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, - } - ) - - expected_attributes = { - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE': 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP', - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP': src_ip, - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID': tunnel_id, - } - - if not skip_dst_ip: - expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP'] = dst_ip - expected_attributes['SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE'] = 'SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P' - - check_object(asic_db, self.ASIC_TUNNEL_TERM_ENTRY, tunnel_term_id, expected_attributes) - - expected_attributes_1 = { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VLAN_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vidlist[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vnidlist[0], - } - - for x in range(len(vidlist)): - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE'] = vidlist[x] - expected_attributes_1['SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY'] = vnidlist[x] - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[x], expected_attributes_1) - - expected_siptnl_attributes = { - 'src_ip': src_ip, - } - - if not skip_dst_ip: - expected_siptnl_attributes['dst_ip'] = dst_ip - - ret = get_key_with_attr(app_db, "VXLAN_TUNNEL_TABLE", expected_siptnl_attributes) - assert len(ret) > 0, "SIP Tunnel entry not created in APPDB" - assert len(ret) == 1, "More than 1 Tunn statetable entry created" - self.tunnel_appdb[tunnel_name] = ret[0] - - self.tunnel_map_ids.update(tunnel_map_id) - self.tunnel_ids.add(tunnel_id) - self.tunnel_term_ids.add(tunnel_term_id) - self.tunnel_map_map[tunnel_name] = tunnel_map_id - self.tunnel[tunnel_name] = tunnel_id - self.tunnel_term[tunnel_name] = tunnel_term_id - - def check_vxlan_dip_tunnel_delete(self, dvs, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(state_db, 'VXLAN_TUNNEL_TABLE') - status, fvs = tbl.get(self.dip_tun_state_map[dip]) - assert status == False, "State Table entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_TUNNEL_TABLE) - status, fvs = tbl.get(self.dip_tunnel_map[dip]) - assert status == False, "Tunnel entry not deleted" - - tbl = swsscommon.Table(asic_db, self.ASIC_BRIDGE_PORT) - status, fvs = tbl.get(self.bridgeport_map[dip]) - assert status == False, "Tunnel bridgeport entry not deleted" - - def check_vxlan_dip_tunnel(self, dvs, vtep_name, src_ip, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - - expected_state_attributes = { - 'src_ip': src_ip, - 'dst_ip': dip, - 'tnl_src': 'EVPN', - } - - ret = get_key_with_attr(state_db, 'VXLAN_TUNNEL_TABLE', expected_state_attributes) - assert len(ret) > 0, "Tunnel Statetable entry not created" - assert len(ret) == 1, "More than 1 Tunn statetable entry created" - self.dip_tun_state_map[dip] = ret[0] - - - tunnel_map_id = self.tunnel_map_map[vtep_name] - - decapstr = '2:' + tunnel_map_id[0] + ',' + tunnel_map_id[2] - encapstr = '2:' + tunnel_map_id[1] + ',' + tunnel_map_id[3] - - print(decapstr) - print(encapstr) - - expected_tun_attributes = { - 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_VXLAN', - 'SAI_TUNNEL_ATTR_PEER_MODE': 'SAI_TUNNEL_PEER_MODE_P2P', - 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': decapstr, - 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': encapstr, - 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip, - 'SAI_TUNNEL_ATTR_ENCAP_DST_IP': dip, - } - - ret = get_key_with_attr(asic_db, self.ASIC_TUNNEL_TABLE, expected_tun_attributes) - assert len(ret) > 0, "Tunnel entry not created" - assert len(ret) == 1, "More than 1 tunnel entry created" - - self.dip_tunnel_map[dip] = ret[0] - tunnel_id = ret[0] - - expected_bridgeport_attributes = { - 'SAI_BRIDGE_PORT_ATTR_TYPE': 'SAI_BRIDGE_PORT_TYPE_TUNNEL', - 'SAI_BRIDGE_PORT_ATTR_TUNNEL_ID': tunnel_id, - 'SAI_BRIDGE_PORT_ATTR_FDB_LEARNING_MODE': 'SAI_BRIDGE_PORT_FDB_LEARNING_MODE_DISABLE', - 'SAI_BRIDGE_PORT_ATTR_ADMIN_STATE': 'true', - } - - ret = get_key_with_attr(asic_db, self.ASIC_BRIDGE_PORT, expected_bridgeport_attributes) - assert len(ret) > 0, "Bridgeport entry not created" - assert len(ret) == 1, "More than 1 bridgeport entry created" - - self.bridgeport_map[dip] = ret[0] - - def check_vlan_extension_delete(self, dvs, vlan_name, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - - tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER') - status, fvs = tbl.get(self.vlan_member_map[dip+vlan_name]) - assert status == False, "VLAN Member entry not deleted" - - def check_vlan_extension(self, dvs, vlan_name, dip): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - expected_vlan_attributes = { - 'SAI_VLAN_ATTR_VLAN_ID': vlan_name, - } - ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN', expected_vlan_attributes) - assert len(ret) > 0, "VLAN entry not created" - assert len(ret) == 1, "More than 1 VLAN entry created" - - self.vlan_id_map[vlan_name] = ret[0] - - expected_vlan_member_attributes = { - 'SAI_VLAN_MEMBER_ATTR_VLAN_ID': self.vlan_id_map[vlan_name], - 'SAI_VLAN_MEMBER_ATTR_BRIDGE_PORT_ID': self.bridgeport_map[dip], - } - ret = get_key_with_attr(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER', expected_vlan_member_attributes) - assert len(ret) > 0, "VLAN Member not created" - assert len(ret) == 1, "More than 1 VLAN member created" - self.vlan_member_map[dip+vlan_name] = ret[0] - - def check_vxlan_tunnel_entry(self, dvs, tunnel_name, vnet_name, vni_id): - asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) - app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) - - time.sleep(2) - - if (self.tunnel_map_map.get(tunnel_name) is None): - tunnel_map_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 2) - else: - tunnel_map_id = self.tunnel_map_map[tunnel_name] - - tunnel_map_entry_id = get_created_entries(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 2) - - # check that the vxlan tunnel termination are there - assert how_many_entries_exist(asic_db, self.ASIC_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 2), "The TUNNEL_MAP_ENTRY is created too early" - - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0], - { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VIRTUAL_ROUTER_ID_TO_VNI', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[1], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_KEY': self.vr_map[vnet_name].get('ing'), - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_VALUE': vni_id, - } - ) - - check_object(asic_db, self.ASIC_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[1], - { - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VNI_TO_VIRTUAL_ROUTER_ID', - 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_id[0], - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VNI_ID_KEY': vni_id, - 'SAI_TUNNEL_MAP_ENTRY_ATTR_VIRTUAL_ROUTER_ID_VALUE': self.vr_map[vnet_name].get('egr'), - } - ) - - self.tunnel_map_entry_ids.update(tunnel_map_entry_id) +DVS_FAKE_PLATFORM = "broadcom" class TestVxlanOrch(object): @@ -547,13 +24,13 @@ def test_p2mp_tunnel(self, dvs, testlog): vxlan_obj.fetch_exist_entries(dvs) - create_vlan1(dvs,"Vlan100") - create_vlan1(dvs,"Vlan101") - create_vlan1(dvs,"Vlan102") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vlan1(dvs,"Vlan101") + vxlan_obj.create_vlan1(dvs,"Vlan102") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vlanlist = ['100', '101', '102'] vnilist = ['1000', '1001', '1002'] @@ -565,14 +42,14 @@ def test_p2mp_tunnel(self, dvs, testlog): vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print("Testing Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print("Testing SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') # Test 2 - DIP Tunnel Tests def test_p2p_tunnel(self, dvs, testlog): @@ -586,24 +63,24 @@ def test_p2p_tunnel(self, dvs, testlog): vnilist = ['1000', '1001', '1002'] vxlan_obj.fetch_exist_entries(dvs) - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) - create_evpn_nvo(dvs, 'nvo1', tunnel_name) - create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') print("Testing DIP tunnel creation") vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') print("Testing VLAN 100 extension") vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') - create_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7', '1001') - create_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7', '1002') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7', '1001') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7', '1002') print("Testing DIP tunnel not created again") vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') @@ -615,7 +92,7 @@ def test_p2p_tunnel(self, dvs, testlog): vxlan_obj.check_vlan_extension(dvs, '102', '7.7.7.7') print("Testing another DIP tunnel to 8.8.8.8") - create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') print("Testing DIP tunnel creation to 8.8.8.8") vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '8.8.8.8') print("Testing VLAN 100 extension to 8.8.8.8 and 7.7.7.7") @@ -623,32 +100,32 @@ def test_p2p_tunnel(self, dvs, testlog): vxlan_obj.check_vlan_extension(dvs, '100', '7.7.7.7') print("Testing Vlan Extension removal") - remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') - remove_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7') vxlan_obj.check_vlan_extension_delete(dvs, '100', '7.7.7.7') vxlan_obj.check_vlan_extension_delete(dvs, '101', '7.7.7.7') print("Testing DIP tunnel not deleted") vxlan_obj.check_vxlan_dip_tunnel(dvs, tunnel_name, '6.6.6.6', '7.7.7.7') print("Testing Last Vlan removal and DIP tunnel delete") - remove_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7') vxlan_obj.check_vlan_extension_delete(dvs, '102', '7.7.7.7') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '7.7.7.7') print("Testing Last Vlan removal and DIP tunnel delete for 8.8.8.8") - remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') vxlan_obj.check_vlan_extension_delete(dvs, '100', '8.8.8.8') vxlan_obj.check_vxlan_dip_tunnel_delete(dvs, '8.8.8.8') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print("Testing SIP Tunnel Deletion") - remove_evpn_nvo(dvs, 'nvo1') - remove_vxlan_tunnel(dvs, tunnel_name) - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') # Test 3 - Create and Delete SIP Tunnel and Map entries @@ -662,13 +139,13 @@ def test_p2mp_tunnel_with_dip(self, dvs, testlog): vxlan_obj.fetch_exist_entries(dvs) - create_vlan1(dvs,"Vlan100") - create_vlan1(dvs,"Vlan101") - create_vlan1(dvs,"Vlan102") - create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6', '2.2.2.2', False) - create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vlan1(dvs,"Vlan101") + vxlan_obj.create_vlan1(dvs,"Vlan102") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6', '2.2.2.2', False) + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vlanlist = ['100', '101', '102'] vnilist = ['1000', '1001', '1002'] @@ -680,11 +157,11 @@ def test_p2mp_tunnel_with_dip(self, dvs, testlog): vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) print("Testing Tunnel Map entry removal") - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') - remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) print("Testing SIP Tunnel Deletion") - remove_vxlan_tunnel(dvs, tunnel_name) - vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name) + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') diff --git a/tests/test_evpn_tunnel_p2mp.py b/tests/test_evpn_tunnel_p2mp.py new file mode 100644 index 0000000000..a446c22384 --- /dev/null +++ b/tests/test_evpn_tunnel_p2mp.py @@ -0,0 +1,119 @@ +from swsscommon import swsscommon +import time +import json +import random +import pytest +from pprint import pprint +from evpn_tunnel import VxlanTunnel + +DVS_FAKE_PLATFORM = "mellanox" + +class TestVxlanOrchP2MP(object): + + def get_vxlan_obj(self): + return VxlanTunnel() + +# Test 1 - Create and Delete SIP Tunnel and Map entries + def test_p2mp_tunnel(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + + tunnel_name = 'tunnel_1' + map_name = 'map_1000_100' + map_name_1 = 'map_1001_101' + map_name_2 = 'map_1002_102' + + vxlan_obj.fetch_exist_entries(dvs) + + vxlan_obj.create_vlan1(dvs,"Vlan100") + vxlan_obj.create_vlan1(dvs,"Vlan101") + vxlan_obj.create_vlan1(dvs,"Vlan102") + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + + vlanlist = ['100', '101', '102'] + vnilist = ['1000', '1001', '1002'] + + print("Testing SIP Tunnel Creation") + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + + print("Testing Tunnel Map Entry") + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + print("Testing Tunnel Map entry removal") + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print("Testing SIP Tunnel Deletion") + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') + +# Test 2 - Vlan extension Tests + def test_vlan_extension(self, dvs, testlog): + vxlan_obj = self.get_vxlan_obj() + + tunnel_name = 'tunnel_2' + map_name = 'map_1000_100' + map_name_1 = 'map_1001_101' + map_name_2 = 'map_1002_102' + vlanlist = ['100', '101', '102'] + vnilist = ['1000', '1001', '1002'] + + vxlan_obj.fetch_exist_entries(dvs) + vxlan_obj.create_vxlan_tunnel(dvs, tunnel_name, '6.6.6.6') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.create_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + + vxlan_obj.check_vxlan_sip_tunnel(dvs, tunnel_name, '6.6.6.6', vlanlist, vnilist) + vxlan_obj.check_vxlan_tunnel_map_entry(dvs, tunnel_name, vlanlist, vnilist) + + vxlan_obj.create_evpn_nvo(dvs, 'nvo1', tunnel_name) + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7', '1000') + + print("Testing VLAN 100 extension") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7', '1001') + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7', '1002') + + print("Testing VLAN 101 extension") + vxlan_obj.check_vlan_extension_p2mp(dvs, '101', '6.6.6.6', '7.7.7.7') + + print("Testing VLAN 102 extension") + vxlan_obj.check_vlan_extension_p2mp(dvs, '102', '6.6.6.6', '7.7.7.7') + + print("Testing another remote endpoint to 8.8.8.8") + vxlan_obj.create_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8', '1000') + print("Testing remote endpoint creation to 8.8.8.8") + + print("Testing VLAN 100 extension to 8.8.8.8 and 7.7.7.7") + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + vxlan_obj.check_vlan_extension_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + + print("Testing Vlan Extension removal") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '7.7.7.7') + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan101', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '101', '6.6.6.6', '7.7.7.7') + + print("Testing Last Vlan removal and remote endpoint delete") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan102', '7.7.7.7') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '102', '6.6.6.6', '7.7.7.7') + + print("Testing Last Vlan removal and remote endpoint delete for 8.8.8.8") + vxlan_obj.remove_evpn_remote_vni(dvs, 'Vlan100', '8.8.8.8') + vxlan_obj.check_vlan_extension_delete_p2mp(dvs, '100', '6.6.6.6', '8.8.8.8') + + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name, '1000', 'Vlan100') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_1, '1001', 'Vlan101') + vxlan_obj.remove_vxlan_tunnel_map(dvs, tunnel_name, map_name_2, '1002', 'Vlan102') + vxlan_obj.check_vxlan_tunnel_map_entry_delete(dvs, tunnel_name, vlanlist, vnilist) + + print("Testing SIP Tunnel Deletion") + vxlan_obj.remove_evpn_nvo(dvs, 'nvo1') + vxlan_obj.remove_vxlan_tunnel(dvs, tunnel_name) + vxlan_obj.check_vxlan_sip_tunnel_delete(dvs, tunnel_name, '6.6.6.6') diff --git a/tests/test_interface.py b/tests/test_interface.py index ac1446481a..a57970b1e5 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -1964,6 +1964,236 @@ def test_LoopbackInterfaceIpv4AddressWithVrf(self, dvs, testlog): assert False + def create_ipv6_link_local(self, interface): + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + else: + tbl_name = "INTERFACE" + + fvs = swsscommon.FieldValuePairs([("ipv6_use_link_local_only", "enable")]) + tbl = swsscommon.Table(self.cdb, tbl_name) + tbl.set(interface, fvs) + time.sleep(1) + + def remove_ipv6_link_local(self, interface): + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + else: + tbl_name = "INTERFACE" + tbl = swsscommon.Table(self.cdb, tbl_name) + tbl._del(interface) + time.sleep(1) + + def test_InterfaceIpv6LinkLocalOnly(self, dvs, testlog): + # Check enable/disable ipv6-link-local mode for physical interface + self.setup_db(dvs) + + # create ipv6 link local interface + self.create_ipv6_link_local("Ethernet8") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("Ethernet8") + assert status == True + for fv in fvs: + if fv[0] == "ipv6_use_link_local_only": + ipv6_link_local_found = True + assert fv[1] == "enable" + + assert ipv6_link_local_found + + # bring up interface + self.set_admin_status(dvs, "Ethernet8", "up") + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one port based router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) >= 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + # the default MTU without any configuration is 9100 + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "9100" + + # remove ipv6 link local interface + self.remove_ipv6_link_local("Ethernet8") + + # bring down interface + self.set_admin_status(dvs, "Ethernet8", "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Ethernet8" + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface + assert len(intf_entries) == 1 + + def test_LagInterfaceIpv6LinkLocalOnly(self, dvs, testlog): + # Check enable/disable ipv6-link-local mode for lag interface + self.setup_db(dvs) + + # create port channel + self.create_port_channel("PortChannel001") + + # bring up interface + self.set_admin_status(dvs, "PortChannel001", "up") + + # create ipv6 link local interface + self.create_ipv6_link_local("PortChannel001") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("PortChannel001") + assert status == True + for fv in fvs: + if fv[0] == "ipv6_use_link_local_only": + ipv6_link_local_found = True + assert fv[1] == "enable" + + assert ipv6_link_local_found + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one port based router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) >= 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + # the default MTU without any configuration is 9100 + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "9100" + + # remove ipv6 link local interface + self.remove_ipv6_link_local("PortChannel001") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:PortChannel001") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "PortChannel001" + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface + assert len(intf_entries) == 1 + + # remove port channel + self.remove_port_channel("PortChannel001") + + + def test_VLanInterfaceIpv6LinkLocalOnly(self, dvs, testlog): + # Check enable/disable ipv6-link-local mode for vlan interface + self.setup_db(dvs) + + # create vlan + self.create_vlan("10") + + # add vlan member + self.create_vlan_member("10", "Ethernet0") + + # bring up interface + self.set_admin_status(dvs, "Ethernet0", "up") + self.set_admin_status(dvs, "Vlan10", "up") + + # create ipv6 link local interface + self.create_ipv6_link_local("Vlan10") + + # check asic database and get vlan_oid + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + vlan_entries = [k for k in tbl.getKeys() if k != dvs.asicdb.default_vlan_id] + assert len(vlan_entries) == 1 + vlan_oid = vlan_entries[0] + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + (status, fvs) = tbl.get("Vlan10") + assert status == True + for fv in fvs: + if fv[0] == "ipv6_use_link_local_only": + ipv6_link_local_found = True + assert fv[1] == "enable" + + assert ipv6_link_local_found + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one vlan router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + if len(fvs) >= 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_VLAN" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VLAN_ID": + assert fv[1] == vlan_oid + + + # remove ipv6 link local interface + self.remove_ipv6_link_local("Vlan10") + + # remove vlan member + self.remove_vlan_member("10", "Ethernet0") + + # remove vlan + self.remove_vlan("10") + + # bring down interface + self.set_admin_status(dvs, "Ethernet0", "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "INTF_TABLE:Vlan10") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + tbl = swsscommon.Table(self.pdb, "INTF_TABLE") + intf_entries = tbl.getKeys() + for entry in intf_entries: + assert entry[0] != "Vlan10" + + # check ASIC router interface database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface + assert len(intf_entries) == 1 + + # 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(): diff --git a/tests/test_mclag_cfg.py b/tests/test_mclag_cfg.py new file mode 100644 index 0000000000..0a79c767da --- /dev/null +++ b/tests/test_mclag_cfg.py @@ -0,0 +1,238 @@ +# Common file to test all MCLAG related changes +from swsscommon import swsscommon +import time +import re +import json +import pytest +import platform +from distutils.version import StrictVersion + + +def delete_table_keys(db, table): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + for key in keys: + tbl.delete(key) + +#check table entry exits with this key +def check_table_exists(db, table, key): + error_info = [ ] + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + if key not in keys: + error_info.append("The table with desired key %s not found" % key) + return False, error_info + return True, error_info + +#check table entry doesn't exits with this key +def check_table_doesnt_exists(db, table, key): + error_info = [ ] + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + if key in keys: + error_info.append("unexcpected: The table with desired key %s is found" % key) + return False, error_info + return True, error_info + + + + +# Test MCLAG Configs +class TestMclagConfig(object): + CFG_MCLAG_DOMAIN_TABLE = "MCLAG_DOMAIN" + CFG_MCLAG_INTERFACE_TABLE = "MCLAG_INTERFACE" + + PORTCHANNEL1 = "PortChannel11" + PORTCHANNEL2 = "PortChannel50" + PORTCHANNEL3 = "PortChannel51" + + MCLAG_DOMAIN_ID = "4095" + MCLAG_SRC_IP = "10.5.1.1" + MCLAG_PEER_IP = "10.5.1.2" + MCLAG_PEER_LINK = PORTCHANNEL1 + + MCLAG_DOMAIN_2 = "111" + + MCLAG_SESS_TMOUT_VALID_LIST = ["3","3600"] + MCLAG_KA_VALID_LIST = ["1","60"] + + MCLAG_KA_INVALID_LIST = ["0","61"] + MCLAG_SESS_TMOUT_INVALID_LIST = ["0","3601"] + + MCLAG_INTERFACE1 = PORTCHANNEL2 + MCLAG_INTERFACE2 = PORTCHANNEL3 + + + # Testcase 1 Verify Configuration of MCLAG Domain with src, peer ip and peer link config gets updated in CONFIG_DB + @pytest.mark.dev_sanity + def test_mclag_cfg_domain_add(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + #cleanup existing entries + delete_table_keys(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE) + delete_table_keys(self.cfg_db, self.CFG_MCLAG_INTERFACE_TABLE) + + cmd_string ="config mclag add {} {} {} {}".format(self.MCLAG_DOMAIN_ID, self.MCLAG_SRC_IP, self.MCLAG_PEER_IP, self.MCLAG_PEER_LINK) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + ok,error_info = dvs.all_table_entry_has(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID, + [ + ("source_ip",self.MCLAG_SRC_IP), + ("peer_ip",self.MCLAG_PEER_IP), + ("peer_link",self.MCLAG_PEER_LINK) + ] + ) + assert ok,error_info + + # Testcase 2 Verify that second domain addition fails when there is already a domain configured + @pytest.mark.dev_sanity + def test_mclag_cfg_domain_add_2nd(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + cmd_string ="config mclag add {} {} {} {}".format(self.MCLAG_DOMAIN_2, self.MCLAG_SRC_IP, self.MCLAG_PEER_IP, self.MCLAG_PEER_LINK) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether second domain config is not added to config db + key_string = self.MCLAG_DOMAIN_2 + ok,error_info = check_table_doesnt_exists(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, key_string) + assert ok,error_info + + + + # Testcase 3 Verify Configuration of MCLAG Interface to existing domain + @pytest.mark.dev_sanity + def test_mclag_cfg_intf_add(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + cmd_string ="config mclag member add {} {}".format(self.MCLAG_DOMAIN_ID, self.MCLAG_INTERFACE1) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether mclag interface config is reflected + key_string = self.MCLAG_DOMAIN_ID + "|" + self.MCLAG_INTERFACE1 + ok,error_info = check_table_exists(self.cfg_db, self.CFG_MCLAG_INTERFACE_TABLE, key_string) + assert ok,error_info + + # Testcase 4 Verify remove and add mclag interface + @pytest.mark.dev_sanity + def test_mclag_cfg_intf_remove_and_add(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + cmd_string ="config mclag member del {} {}".format(self.MCLAG_DOMAIN_ID, self.MCLAG_INTERFACE1) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether mclag interface is removed + key_string = self.MCLAG_DOMAIN_ID + "|" + self.MCLAG_INTERFACE1 + ok,error_info = check_table_doesnt_exists(self.cfg_db, self.CFG_MCLAG_INTERFACE_TABLE, key_string) + assert ok,error_info + + #add different mclag interface + cmd_string ="config mclag member del {} {}".format(self.MCLAG_DOMAIN_ID, self.MCLAG_INTERFACE2) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether new mclag interface is added + key_string = self.MCLAG_DOMAIN_ID + "|" + self.MCLAG_INTERFACE2 + ok,error_info = check_table_doesnt_exists(self.cfg_db, self.CFG_MCLAG_INTERFACE_TABLE, key_string) + assert ok,error_info + + # Testcase 5 Verify Configuration of valid values for session timeout + @pytest.mark.dev_sanity + def test_mclag_cfg_session_timeout_valid_values(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + for value in self.MCLAG_SESS_TMOUT_VALID_LIST: + cmd_string ="config mclag session-timeout {} {}".format(self.MCLAG_DOMAIN_ID, value) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + ok,error_info = dvs.all_table_entry_has(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID, + [ + ("source_ip",self.MCLAG_SRC_IP), + ("peer_ip",self.MCLAG_PEER_IP), + ("peer_link",self.MCLAG_PEER_LINK), + ("session_timeout",value) + ] + ) + assert ok,error_info + + # Testcase 6 Verify Configuration of valid values for KA timer + @pytest.mark.dev_sanity + def test_mclag_cfg_ka_valid_values(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + for value in self.MCLAG_KA_VALID_LIST: + cmd_string ="config mclag keepalive-interval {} {}".format(self.MCLAG_DOMAIN_ID, value) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + ok,error_info = dvs.all_table_entry_has(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID, + [ + ("source_ip",self.MCLAG_SRC_IP), + ("peer_ip",self.MCLAG_PEER_IP), + ("peer_link",self.MCLAG_PEER_LINK), + ("keepalive_interval",value) + ] + ) + assert ok,error_info + + + + # Testcase 7 Verify Configuration of invalid values for KA + @pytest.mark.dev_sanity + def test_mclag_cfg_ka_invalid_values(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + for value in self.MCLAG_KA_INVALID_LIST: + cmd_string ="config mclag keepalive-interval {} {}".format(self.MCLAG_DOMAIN_ID, value) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + found,error_info = dvs.all_table_entry_has(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID, + [ + ("keepalive_interval",value) + ] + ) + assert found == False, "invalid keepalive value %s written to CONFIG_DB" % value + + # Testcase 8 Verify Configuration of invalid values for session timeout + @pytest.mark.dev_sanity + def test_mclag_cfg_session_invalid_values(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + for value in self.MCLAG_SESS_TMOUT_INVALID_LIST: + cmd_string ="config mclag session-timeout {} {}".format(self.MCLAG_DOMAIN_ID, value) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + found,error_info = dvs.all_table_entry_has(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID, + [ + ("session_timeout",value) + ] + ) + assert found == False, "invalid keepalive value %s written to CONFIG_DB" % value + + # Testcase 9 Verify Deletion of MCLAG Domain + @pytest.mark.dev_sanity + def test_mclag_cfg_domain_del(self, dvs, testlog): + self.cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + cmd_string ="config mclag del {}".format(self.MCLAG_DOMAIN_ID) + dvs.runcmd(cmd_string) + time.sleep(2) + + #check whether domain cfg table contents are same as configured values + ok, error_info = check_table_doesnt_exists(self.cfg_db, self.CFG_MCLAG_DOMAIN_TABLE, self.MCLAG_DOMAIN_ID) + assert ok,error_info + + #make sure mclag interface tables entries are also deleted when mclag domain is deleted + key_string = self.MCLAG_DOMAIN_ID + ok,error_info = check_table_doesnt_exists(self.cfg_db, self.CFG_MCLAG_INTERFACE_TABLE, key_string) + assert ok,error_info + diff --git a/tests/test_mclag_fdb.py b/tests/test_mclag_fdb.py new file mode 100644 index 0000000000..5049859437 --- /dev/null +++ b/tests/test_mclag_fdb.py @@ -0,0 +1,505 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +import pytest +from distutils.version import StrictVersion + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + +def get_port_oid(dvs, port_name): + counters_db = swsscommon.DBConnector(swsscommon.COUNTERS_DB, dvs.redis_sock, 0) + port_map_tbl = swsscommon.Table(counters_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(dvs, port_oid): + tbl = swsscommon.Table(dvs.adb, "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 create_port_channel(dvs, alias): + tbl = swsscommon.Table(dvs.cdb, "PORTCHANNEL") + fvs = swsscommon.FieldValuePairs([("admin_status", "up"), + ("mtu", "9100")]) + tbl.set(alias, fvs) + time.sleep(1) + +def remove_port_channel(dvs, alias): + tbl = swsscommon.Table(dvs.cdb, "PORTCHANNEL") + tbl._del(alias) + time.sleep(1) + +def add_port_channel_members(dvs, lag, members): + tbl = swsscommon.Table(dvs.cdb, "PORTCHANNEL_MEMBER") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + for member in members: + tbl.set(lag + "|" + member, fvs) + time.sleep(1) + +def remove_port_channel_members(dvs, lag, members): + tbl = swsscommon.Table(dvs.cdb, "PORTCHANNEL_MEMBER") + for member in members: + tbl._del(lag + "|" + member) + time.sleep(1) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + +# Test-1 Verify basic config add + +@pytest.mark.dev_sanity +def test_mclagFdb_basic_config_add(dvs, testlog): + dvs.setup_db() + dvs.runcmd("sonic-clear fdb all") + 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 PortChannel0005 + create_port_channel(dvs, "PortChannel0005") + + # add members to PortChannel0005 + add_port_channel_members(dvs, "PortChannel0005", ["Ethernet4"]) + + # create PortChannel0006 + create_port_channel(dvs, "PortChannel0006") + + # add members to PortChannel0006 + add_port_channel_members(dvs, "PortChannel0006", ["Ethernet8"]) + + # create PortChannel0008 + create_port_channel(dvs, "PortChannel0008") + + # add members to PortChannel0008 + add_port_channel_members(dvs, "PortChannel0008", ["Ethernet12"]) + + # create vlan + dvs.create_vlan("200") + + # Add vlan members + dvs.create_vlan_member("200", "PortChannel0005") + dvs.create_vlan_member("200", "PortChannel0006") + dvs.create_vlan_member("200", "PortChannel0008") + + # check that the vlan information was propagated + vlan_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + bp_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + vm_after = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") + + assert vlan_after - vlan_before == 1, "The Vlan200 wasn't created" + assert bp_after - bp_before == 3, "The bridge port wasn't created" + assert vm_after - vm_before == 3, "The vlan member wasn't added" + +# Test-2 Remote Dynamic MAC Add + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_dynamic_mac_add(dvs, testlog): + dvs.setup_db() + # create FDB entry in APP_DB MCLAG_FDB_TABLE + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + +# Test-3 Remote Dynamic MAC Delete + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_dynamic_mac_delete(dvs, testlog): + dvs.setup_db() + + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The MCLAG fdb entry not deleted" + + +# Test-4 Remote Static MAC Add + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_static_mac_add(dvs, testlog): + dvs.setup_db() + + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "static"), + ] + ) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG static fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC")] + ) + + assert ok, str(extra) + +# Test-5 Remote Static MAC Del + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_static_mac_del(dvs, testlog): + dvs.setup_db() + + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The MCLAG static fdb entry not deleted" + + +# Test-6 Verify Remote to Local Move. + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_to_local_mac_move(dvs, testlog): + dvs.setup_db() + + #Add remote MAC to MCLAG_FDB_TABLE on PortChannel0005 + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + + #Move MAC to PortChannel0008 on Local FDB_TABLE + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0008"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC")] + ) + + assert ok, str(extra) + +# Test-7 Verify Remote MAC del should not del local MAC after move + +@pytest.mark.dev_sanity +def test_mclagFdb_local_mac_move_del(dvs, testlog): + dvs.setup_db() + + #Cleanup the FDB from MCLAG_FDB_TABLE + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + # Verify that the local FDB entry still inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC")] + ) + + assert ok, str(extra) + + + #delete the local FDB and Verify + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry was not deleted" + + +# Test-8 Verify Local to Remote Move. + +@pytest.mark.dev_sanity +def test_mclagFdb_local_to_remote_move(dvs, testlog): + dvs.setup_db() + + #Add local MAC to FDB_TABLE on PortChannel0008 + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0008"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC")] + ) + + assert ok, str(extra) + + #Move MAC to PortChannel0005 on Remote FDB_TABLE + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + +# Test-9 Verify local MAC del should not del remote MAC after move + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_move_del(dvs, testlog): + dvs.setup_db() + + #Cleanup the local FDB + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + # check that the remote FDB entry still inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + + #delete the remote FDB and Verify + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry not deleted" + + + +# Test-10 Verify remote MAC move in remote node is updated locally. + +@pytest.mark.dev_sanity +def test_mclagFdb_remote_move_peer_node(dvs, testlog): + dvs.setup_db() + + #Add remote MAC to MCLAG_FDB_TABLE on PortChannel0005 + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + + # Move remote MAC in MCLAG_FDB_TABLE to PortChannel0006 + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0006"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC"), + ("SAI_FDB_ENTRY_ATTR_ALLOW_MAC_MOVE", "true")] + ) + + assert ok, str(extra) + + #delete the remote FDB and Verify + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry not deleted" + + +# Test-11 Verify remote dynamic MAC move rejection in presecense of local static MAC. + +@pytest.mark.dev_sanity +def test_mclagFdb_static_mac_dynamic_move_reject(dvs, testlog): + dvs.setup_db() + + #Add local MAC to FDB_TABLE on PortChannel0008 + create_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0008"), + ("type", "static"), + ] + ) + + # check that the static FDB entry was inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC")] + ) + + assert ok, str(extra) + + #Add remote MAC to MCLAG_FDB_TABLE on PortChannel0005 + create_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + [ + ("port", "PortChannel0005"), + ("type", "dynamic"), + ] + ) + + # check that still static FDB entry is inserted into ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The MCLAG fdb entry not inserted to ASIC" + + ok, extra = dvs.is_fdb_entry_exists(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "3C:85:99:5E:00:01"), ("bvid", str(dvs.getVlanOid("200")))], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_STATIC")] + ) + + assert ok, str(extra) + + delete_entry_pst( + dvs.pdb, + "FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + + time.sleep(2) + # check that the FDB entry was deleted from ASIC DB + assert how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The MCLAG static fdb entry not deleted" + + delete_entry_pst( + dvs.pdb, + "MCLAG_FDB_TABLE", "Vlan200:3C:85:99:5E:00:01", + ) + +# Test-12 Verify cleanup of the basic config. + +@pytest.mark.dev_sanity +def test_mclagFdb_basic_config_del(dvs, testlog): + dvs.setup_db() + + dvs.remove_vlan_member("200", "PortChannel0005") + dvs.remove_vlan_member("200", "PortChannel0006") + dvs.remove_vlan_member("200", "PortChannel0008") + dvs.remove_vlan("200") + remove_port_channel_members(dvs, "PortChannel0005", + ["Ethernet4"]) + remove_port_channel_members(dvs, "PortChannel0006", + ["Ethernet8"]) + remove_port_channel_members(dvs, "PortChannel0008", + ["Ethernet12"]) + remove_port_channel(dvs, "PortChannel0005") + remove_port_channel(dvs, "PortChannel0006") + remove_port_channel(dvs, "PortChannel0008") + diff --git a/tests/test_mirror.py b/tests/test_mirror.py index 73cfc02ccf..f74ff6fa0d 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -805,12 +805,11 @@ def test_AclBindMirrorPerStage(self, dvs, testlog): self.remove_ip_address("Ethernet32", "20.0.0.0/31") self.set_interface_status(dvs, "Ethernet32", "down") - def test_AclBindMirror(self, dvs, testlog): + def _test_AclBindMirror(self, dvs, testlog, create_seq_test=False): """ This test tests ACL associated with mirror session with DSCP value The DSCP value is tested on both with mask and without mask """ - self.setup_db(dvs) session = "MIRROR_SESSION" acl_table = "MIRROR_TABLE" @@ -820,16 +819,18 @@ def test_AclBindMirror(self, dvs, testlog): self.set_interface_status(dvs, "Ethernet32", "up") self.add_ip_address("Ethernet32", "20.0.0.0/31") self.add_neighbor("Ethernet32", "20.0.0.1", "02:04:06:08:10:12") - self.add_route(dvs, "4.4.4.4", "20.0.0.1") + if create_seq_test == False: + self.add_route(dvs, "4.4.4.4", "20.0.0.1") # create mirror session self.create_mirror_session(session, "3.3.3.3", "4.4.4.4", "0x6558", "8", "100", "0") - assert self.get_mirror_session_state(session)["status"] == "active" + assert self.get_mirror_session_state(session)["status"] == ("active" if create_seq_test == False else "inactive") - # assert mirror session in asic database + # check mirror session in asic database tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") - assert len(tbl.getKeys()) == 1 - mirror_session_oid = tbl.getKeys()[0] + assert len(tbl.getKeys()) == (1 if create_seq_test == False else 0) + if create_seq_test == False: + mirror_session_oid = tbl.getKeys()[0] # create acl table self.create_acl_table(acl_table, ["Ethernet0", "Ethernet4"]) @@ -837,10 +838,25 @@ def test_AclBindMirror(self, dvs, testlog): # create acl rule with dscp value 48 self.create_mirror_acl_dscp_rule(acl_table, acl_rule, "48", session) - # assert acl rule is created + # acl rule creation check tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") rule_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_entries] - assert len(rule_entries) == 1 + assert len(rule_entries) == (1 if create_seq_test == False else 0) + + if create_seq_test == True: + self.add_route(dvs, "4.4.4.4", "20.0.0.1") + + assert self.get_mirror_session_state(session)["status"] == "active" + + # assert mirror session in asic database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + assert len(tbl.getKeys()) == 1 + mirror_session_oid = tbl.getKeys()[0] + + # assert acl rule is created + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + rule_entries = [k for k in tbl.getKeys() if k not in dvs.asicdb.default_acl_entries] + assert len(rule_entries) == 1 (status, fvs) = tbl.get(rule_entries[0]) assert status == True @@ -888,6 +904,12 @@ def test_AclBindMirror(self, dvs, testlog): self.remove_ip_address("Ethernet32", "20.0.0.0/31") self.set_interface_status(dvs, "Ethernet32", "down") + def test_AclBindMirror(self, dvs, testlog): + self.setup_db(dvs) + + self._test_AclBindMirror(dvs, testlog) + self._test_AclBindMirror(dvs, testlog, create_seq_test=True) + # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying