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..0510949eee 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -16,7 +16,8 @@ dist_swss_DATA = \ watermark_queue.lua \ watermark_pg.lua \ watermark_bufferpool.lua \ - lagids.lua + lagids.lua \ + tunnel_rates.lua bin_PROGRAMS = orchagent routeresync orchagent_restart_check @@ -64,6 +65,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..6094c12c05 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()) @@ -628,8 +760,7 @@ 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" ); if(origin == FDB_ORIGIN_VXLAN_ADVERTIZED) { @@ -652,14 +783,48 @@ void FdbOrch::doTask(Consumer& consumer) fdbData.esi = esi; fdbData.vni = vni; if (addFdbEntry(entry, port, fdbData)) + { + 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); + } + + it = consumer.m_toSync.erase(it); + } 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 +1053,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); } } @@ -1003,6 +1165,7 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, string oldType; FdbOrigin oldOrigin = FDB_ORIGIN_INVALID ; bool macUpdate = false; + auto it = m_entries.find(entry); if (it != m_entries.end()) { @@ -1061,6 +1224,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 +1258,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 +1327,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 +1379,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 +1405,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 +1416,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 +1486,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 +1544,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..82611e686f 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; 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/flex_counter/flex_counter_manager.cpp b/orchagent/flex_counter/flex_counter_manager.cpp index 299e238d37..084bb466cc 100644 --- a/orchagent/flex_counter/flex_counter_manager.cpp +++ b/orchagent/flex_counter/flex_counter_manager.cpp @@ -39,17 +39,56 @@ const unordered_map FlexCounterManager::counter_id_field_lo { CounterType::PORT, PORT_COUNTER_ID_LIST }, { CounterType::QUEUE, QUEUE_COUNTER_ID_LIST }, { CounterType::MACSEC_SA_ATTR, MACSEC_SA_ATTR_ID_LIST }, + { CounterType::TUNNEL, TUNNEL_ATTR_ID_LIST }, }; +FlexManagerDirectory g_FlexManagerDirectory; + +FlexCounterManager *FlexManagerDirectory::createFlexCounterManager(const string& group_name, + const StatsMode stats_mode, + const uint polling_interval, + const bool enabled, + FieldValueTuple fv_plugin) +{ + if (m_managers.find(group_name) != m_managers.end()) + { + if (stats_mode != m_managers[group_name]->getStatsMode()) + { + SWSS_LOG_ERROR("Stats mode mismatch with already created flex counter manager %s", + group_name.c_str()); + return NULL; + } + if (polling_interval != m_managers[group_name]->getPollingInterval()) + { + SWSS_LOG_ERROR("Polling interval mismatch with already created flex counter manager %s", + group_name.c_str()); + return NULL; + } + if (enabled != m_managers[group_name]->getEnabled()) + { + SWSS_LOG_ERROR("Enabled field mismatch with already created flex counter manager %s", + group_name.c_str()); + return NULL; + } + return m_managers[group_name]; + } + FlexCounterManager *fc_manager = new FlexCounterManager(group_name, stats_mode, polling_interval, + enabled, fv_plugin); + m_managers[group_name] = fc_manager; + return fc_manager; +} + FlexCounterManager::FlexCounterManager( const string& group_name, const StatsMode stats_mode, const uint polling_interval, - const bool enabled) : + const bool enabled, + FieldValueTuple fv_plugin) : group_name(group_name), stats_mode(stats_mode), polling_interval(polling_interval), enabled(enabled), + fv_plugin(fv_plugin), flex_counter_db(new DBConnector("FLEX_COUNTER_DB", 0)), flex_counter_group_table(new ProducerTable(flex_counter_db.get(), FLEX_COUNTER_GROUP_TABLE)), flex_counter_table(new ProducerTable(flex_counter_db.get(), FLEX_COUNTER_TABLE)) @@ -86,6 +125,11 @@ void FlexCounterManager::applyGroupConfiguration() FieldValueTuple(FLEX_COUNTER_STATUS_FIELD, status_lookup.at(enabled)) }; + if (!fvField(fv_plugin).empty()) + { + field_values.emplace_back(fv_plugin); + } + flex_counter_group_table->set(group_name, field_values); } diff --git a/orchagent/flex_counter/flex_counter_manager.h b/orchagent/flex_counter/flex_counter_manager.h index 4df99c90bd..2d531d5084 100644 --- a/orchagent/flex_counter/flex_counter_manager.h +++ b/orchagent/flex_counter/flex_counter_manager.h @@ -4,8 +4,10 @@ #include #include #include +#include #include "dbconnector.h" #include "producertable.h" +#include "table.h" #include extern "C" { @@ -24,6 +26,7 @@ enum class CounterType PORT_DEBUG, SWITCH_DEBUG, MACSEC_SA_ATTR, + TUNNEL, }; // FlexCounterManager allows users to manage a group of flex counters. @@ -38,7 +41,11 @@ class FlexCounterManager const std::string& group_name, const StatsMode stats_mode, const uint polling_interval, - const bool enabled); + const bool enabled, + swss::FieldValueTuple fv_plugin = std::make_pair("","")); + + FlexCounterManager() + {} FlexCounterManager(const FlexCounterManager&) = delete; FlexCounterManager& operator=(const FlexCounterManager&) = delete; @@ -54,6 +61,26 @@ class FlexCounterManager const std::unordered_set& counter_stats); void clearCounterIdList(const sai_object_id_t object_id); + const std::string& getGroupName() const + { + return group_name; + } + + const StatsMode& getStatsMode() const + { + return stats_mode; + } + + const uint& getPollingInterval() const + { + return polling_interval; + } + + const bool& getEnabled() const + { + return enabled; + } + protected: void applyGroupConfiguration(); @@ -68,6 +95,7 @@ class FlexCounterManager StatsMode stats_mode; uint polling_interval; bool enabled; + swss::FieldValueTuple fv_plugin; std::unordered_set installed_counters; std::shared_ptr flex_counter_db = nullptr; @@ -79,4 +107,14 @@ class FlexCounterManager static const std::unordered_map counter_id_field_lookup; }; +class FlexManagerDirectory +{ + public: + FlexCounterManager* createFlexCounterManager(const std::string& group_name, const StatsMode stats_mode, + const uint polling_interval, const bool enabled, + swss::FieldValueTuple fv_plugin = std::make_pair("","")); + private: + std::unordered_map m_managers; +}; + #endif // ORCHAGENT_FLEX_COUNTER_MANAGER_H diff --git a/orchagent/flexcounterorch.cpp b/orchagent/flexcounterorch.cpp index 41f024c639..d47b82078f 100644 --- a/orchagent/flexcounterorch.cpp +++ b/orchagent/flexcounterorch.cpp @@ -9,6 +9,7 @@ #include "bufferorch.h" #include "flexcounterorch.h" #include "debugcounterorch.h" +#include "directory.h" extern sai_port_api_t *sai_port_api; @@ -16,6 +17,7 @@ extern PortsOrch *gPortsOrch; extern FabricPortsOrch *gFabricPortsOrch; extern IntfsOrch *gIntfsOrch; extern BufferOrch *gBufferOrch; +extern Directory gDirectory; #define BUFFER_POOL_WATERMARK_KEY "BUFFER_POOL_WATERMARK" #define PORT_KEY "PORT" @@ -23,6 +25,7 @@ extern BufferOrch *gBufferOrch; #define QUEUE_KEY "QUEUE" #define PG_WATERMARK_KEY "PG_WATERMARK" #define RIF_KEY "RIF" +#define TUNNEL_KEY "TUNNEL" unordered_map flexCounterGroupMap = { @@ -38,6 +41,7 @@ unordered_map flexCounterGroupMap = {"RIF", RIF_STAT_COUNTER_FLEX_COUNTER_GROUP}, {"RIF_RATES", RIF_RATE_COUNTER_FLEX_COUNTER_GROUP}, {"DEBUG_COUNTER", DEBUG_COUNTER_FLEX_COUNTER_GROUP}, + {"TUNNEL", TUNNEL_STAT_COUNTER_FLEX_COUNTER_GROUP}, }; @@ -58,6 +62,7 @@ void FlexCounterOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); + VxlanTunnelOrch* vxlan_tunnel_orch = gDirectory.get(); if (gPortsOrch && !gPortsOrch->allPortsReady()) { return; @@ -140,6 +145,10 @@ void FlexCounterOrch::doTask(Consumer &consumer) { gFabricPortsOrch->generateQueueStats(); } + if (vxlan_tunnel_orch && (key== TUNNEL_KEY) && (value == "enable")) + { + vxlan_tunnel_orch->generateTunnelCounterMap(); + } vector fieldValues; fieldValues.emplace_back(FLEX_COUNTER_STATUS_FIELD, value); m_flexCounterGroupTable->set(flexCounterGroupMap[key], fieldValues); 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..10dd8cb788 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -85,11 +85,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) 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..a546ffdac1 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -39,6 +39,8 @@ BufferOrch *gBufferOrch; SwitchOrch *gSwitchOrch; Directory gDirectory; NatOrch *gNatOrch; +MlagOrch *gMlagOrch; +IsoGrpOrch *gIsoGrpOrch; MACsecOrch *gMacsecOrch; bool gIsNatSupported = false; @@ -104,13 +106,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, @@ -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); @@ -340,6 +357,8 @@ bool OrchDaemon::init() 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/portsorch.cpp b/orchagent/portsorch.cpp index a699ddded1..bf8b1ed557 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -266,6 +266,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 +386,11 @@ PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector tmp_lane_set; @@ -459,7 +481,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 +2046,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 +2249,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 +2291,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 +2345,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 +2631,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 +2809,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 +2818,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 +2884,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 +2925,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 +3719,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 +3761,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 +3916,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 +3940,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 +3960,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 +3985,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 +4245,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 +4301,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; @@ -5413,7 +5492,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 +5593,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 +5607,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 +5624,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 +5663,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 +5990,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 +6006,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 +6046,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 +6119,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 +6142,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..22efce3561 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -324,7 +324,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..3cf490a5fc 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(); 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..561fd5df8d 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -62,6 +62,7 @@ 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; @@ -183,6 +184,7 @@ 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); @@ -418,7 +420,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/tunnel_rates.lua b/orchagent/tunnel_rates.lua new file mode 100644 index 0000000000..ef3816210f --- /dev/null +++ b/orchagent/tunnel_rates.lua @@ -0,0 +1,85 @@ +-- KEYS - rif IDs +-- ARGV[1] - counters db index +-- ARGV[2] - counters table name +-- ARGV[3] - poll time interval +-- return log + +local logtable = {} + +local function logit(msg) + logtable[#logtable+1] = tostring(msg) +end + +local counters_db = ARGV[1] +local counters_table_name = ARGV[2] +local rates_table_name = "RATES" +local sec_to_ms = 1000 + +-- Get configuration +redis.call('SELECT', counters_db) +local smooth_interval = redis.call('HGET', rates_table_name .. ':' .. 'TUNNEL', 'TUNNEL_SMOOTH_INTERVAL') +local alpha = redis.call('HGET', rates_table_name .. ':' .. 'TUNNEL', 'TUNNEL_ALPHA') +if not alpha then + logit("Alpha is not defined") + return logtable +end +local one_minus_alpha = 1.0 - alpha +local delta = tonumber(ARGV[3]) + +local n = table.getn(KEYS) +for i = 1, n do + local state_table = rates_table_name .. ':' .. KEYS[i] .. ':' .. 'TUNNEL' + local initialized = redis.call('HGET', state_table, 'INIT_DONE') + logit(initialized) + + -- Get new COUNTERS values + local in_octets = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_IN_OCTETS') + local in_pkts = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_IN_PACKETS') + local out_octets = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_OUT_OCTETS') + local out_pkts = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_OUT_PACKETS') + + if initialized == "DONE" or initialized == "COUNTERS_LAST" then + -- Get old COUNTERS values + local in_octets_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_IN_OCTETS_last') + local in_pkts_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_IN_PACKETS_last') + local out_octets_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_OUT_OCTETS_last') + local out_pkts_last = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_OUT_PACKETS_last') + + -- Calculate new rates values + local rx_bps_new = (in_octets - in_octets_last)*sec_to_ms/delta + local tx_bps_new = (out_octets - out_octets_last)*sec_to_ms/delta + local rx_pps_new = (in_pkts - in_pkts_last)*sec_to_ms/delta + local tx_pps_new = (out_pkts - out_pkts_last)*sec_to_ms/delta + + if initialized == "DONE" then + -- Get old rates values + local rx_bps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'RX_BPS') + local rx_pps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS') + local tx_bps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'TX_BPS') + local tx_pps_old = redis.call('HGET', rates_table_name .. ':' .. KEYS[i], 'TX_PPS') + + -- Smooth the rates values and store them in DB + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_BPS', alpha*rx_bps_new + one_minus_alpha*rx_bps_old) + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS', alpha*rx_pps_new + one_minus_alpha*rx_pps_old) + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_BPS', alpha*tx_bps_new + one_minus_alpha*tx_bps_old) + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_PPS', alpha*tx_pps_new + one_minus_alpha*tx_pps_old) + else + -- Store unsmoothed initial rates values in DB + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_BPS', rx_bps_new) + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'RX_PPS', rx_pps_new) + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_BPS', tx_bps_new) + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'TX_PPS', tx_pps_new) + redis.call('HSET', state_table, 'INIT_DONE', 'DONE') + end + else + redis.call('HSET', state_table, 'INIT_DONE', 'COUNTERS_LAST') + end + + -- Set old COUNTERS values + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_IN_OCTETS_last', in_octets) + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_IN_PACKETS_last', in_pkts) + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_OUT_OCTETS_last', out_octets) + redis.call('HSET', rates_table_name .. ':' .. KEYS[i], 'SAI_TUNNEL_STAT_OUT_PACKETS_last', out_pkts) +end + +return logtable diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index 6e25454f13..5723f9dc71 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -15,6 +15,9 @@ #include "swssnet.h" #include "warm_restart.h" #include "tokenize.h" +#include "sai_serialize.h" +#include "flex_counter_manager.h" + /* Global variables */ extern sai_object_id_t gSwitchId; @@ -24,6 +27,7 @@ extern sai_next_hop_api_t *sai_next_hop_api; extern Directory gDirectory; extern PortsOrch* gPortsOrch; extern sai_object_id_t gUnderlayIfId; +extern FlexManagerDirectory g_FlexManagerDirectory; const map vxlanTunnelMap = { @@ -57,6 +61,14 @@ const map> vxlanTunnelMapKeyVal = }, }; +const vector tunnel_stat_ids = +{ + SAI_TUNNEL_STAT_IN_OCTETS, + SAI_TUNNEL_STAT_IN_PACKETS, + SAI_TUNNEL_STAT_OUT_OCTETS, + SAI_TUNNEL_STAT_OUT_PACKETS +}; + /* * Manipulators for the above Map */ @@ -482,6 +494,7 @@ bool VxlanTunnel::createTunnel(MAP_T encap, MAP_T decap, uint8_t encap_ttl) { try { + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); sai_ip_address_t ips, ipd, *ip=nullptr; uint8_t mapper_list = 0; swss::copy(ips, src_ip_); @@ -511,6 +524,11 @@ bool VxlanTunnel::createTunnel(MAP_T encap, MAP_T decap, uint8_t encap_ttl) ids_.tunnel_id = create_tunnel(&ids_, ip, NULL, gUnderlayIfId, false, encap_ttl); + if (ids_.tunnel_id != SAI_NULL_OBJECT_ID) + { + tunnel_orch->addTunnelToFlexCounter(ids_.tunnel_id, tunnel_name_); + } + ip = nullptr; if (!dst_ip_.isZero()) { @@ -840,11 +858,14 @@ bool VxlanTunnel::deleteTunnelHw(uint8_t mapper_list, tunnel_map_use_t map_src, { try { + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); + if (with_term) { remove_tunnel_termination(ids_.tunnel_term_id); } + tunnel_orch->removeTunnelFromFlexCounter(ids_.tunnel_id, tunnel_name_); remove_tunnel(ids_.tunnel_id); deleteMapperHw(mapper_list, map_src); } @@ -869,6 +890,7 @@ bool VxlanTunnel::createTunnelHw(uint8_t mapper_list, tunnel_map_use_t map_src, try { + VxlanTunnelOrch* tunnel_orch = gDirectory.get(); sai_ip_address_t ips, ipd, *ip=nullptr; swss::copy(ips, src_ip_); @@ -885,6 +907,11 @@ bool VxlanTunnel::createTunnelHw(uint8_t mapper_list, tunnel_map_use_t map_src, ids_.tunnel_id = create_tunnel(&ids_, &ips, ip, gUnderlayIfId, p2p); + if (ids_.tunnel_id != SAI_NULL_OBJECT_ID) + { + tunnel_orch->addTunnelToFlexCounter(ids_.tunnel_id, tunnel_name_); + } + if (with_term) { ids_.tunnel_term_id = create_tunnel_termination(ids_.tunnel_id, ips, @@ -1156,6 +1183,103 @@ bool VxlanTunnel::deleteDynamicDIPTunnel(const std::string dip, tunnel_user_t us //------------------- VxlanTunnelOrch Implementation --------------------------// +VxlanTunnelOrch::VxlanTunnelOrch(DBConnector *statedb, DBConnector *db, const std::string& tableName) : + Orch2(db, tableName, request_), + m_stateVxlanTable(statedb, STATE_VXLAN_TUNNEL_TABLE_NAME) +{ + FieldValueTuple fv; + string tunnel_rate_plugin = "tunnel_rates.lua"; + m_counter_db = shared_ptr(new DBConnector("COUNTERS_DB", 0)); + try + { + string tunnel_rate_script = swss::loadLuaScript(tunnel_rate_plugin); + string tunnel_rate_sha = swss::loadRedisScript(m_counter_db.get(), tunnel_rate_script); + fv = FieldValueTuple(TUNNEL_PLUGIN_FIELD, tunnel_rate_sha); + } + catch (const runtime_error &e) + { + SWSS_LOG_WARN("Tunnel flex counter group plugins was not set successfully: %s", e.what()); + } + + tunnel_stat_manager = g_FlexManagerDirectory.createFlexCounterManager(TUNNEL_STAT_COUNTER_FLEX_COUNTER_GROUP, + StatsMode::READ, TUNNEL_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, false, fv); + + m_tunnelNameTable = unique_ptr
(new Table(m_counter_db.get(), COUNTERS_TUNNEL_NAME_MAP)); + m_tunnelTypeTable = unique_ptr
(new Table(m_counter_db.get(), COUNTERS_TUNNEL_TYPE_MAP)); + + +} + +void VxlanTunnelOrch::addTunnelToFlexCounter(sai_object_id_t oid, const string &name) +{ + SWSS_LOG_ENTER(); + string type = "SAI_TUNNEL_TYPE_VXLAN"; + + string sai_oid = sai_serialize_object_id(oid); + vector tunnelNameFvs; + vector tunnelTypeFvs; + + tunnelNameFvs.emplace_back(name, sai_oid); + tunnelTypeFvs.emplace_back(sai_oid, type); + + m_tunnelNameTable->set("", tunnelNameFvs); + m_tunnelTypeTable->set("", tunnelTypeFvs); + + auto tunnel_stats = generateTunnelCounterStats(); + tunnel_stat_manager->setCounterIdList(oid, CounterType::TUNNEL, + tunnel_stats); + SWSS_LOG_DEBUG("Registered tunnel %s to Flex counter", name.c_str()); +} + +void VxlanTunnelOrch::removeTunnelFromFlexCounter(sai_object_id_t oid, const string &name) +{ + SWSS_LOG_ENTER(); + + if (oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_WARN("Not removing NULL OID to flex for tunnel %s", name.c_str()); + return; + } + + string sai_oid = sai_serialize_object_id(oid); + + m_tunnelNameTable->hdel("", name); + m_tunnelTypeTable->hdel("", sai_oid); + tunnel_stat_manager->clearCounterIdList(oid); + SWSS_LOG_DEBUG("Unregistered tunnel %s to Flex counter", name.c_str()); +} + +std::unordered_set VxlanTunnelOrch::generateTunnelCounterStats() +{ + std::unordered_set counter_stats; + + for (const auto& it: tunnel_stat_ids) + { + counter_stats.emplace(sai_serialize_tunnel_stat(it)); + } + return counter_stats; +} + +void VxlanTunnelOrch::generateTunnelCounterMap() +{ + if (m_isTunnelCounterMapGenerated) + { + return; + } + + for (const auto& it: vxlan_tunnel_table_) + { + auto tunnel_id = it.second.get()->getTunnelId(); + if (tunnel_id != SAI_NULL_OBJECT_ID) + { + addTunnelToFlexCounter(it.second.get()->getTunnelId(), it.second.get()->getTunnelName()); + } + } + + m_isTunnelCounterMapGenerated = true; +} + + sai_object_id_t VxlanTunnelOrch::createNextHopTunnel(string tunnelName, IpAddress& ipAddr, MacAddress macAddress, uint32_t vni) @@ -1334,6 +1458,7 @@ bool VxlanTunnelOrch::removeVxlanTunnelMap(string tunnelName, uint32_t vni) auto tunnel_id = vxlan_tunnel_table_[tunnelName].get()->getTunnelId(); try { + removeTunnelFromFlexCounter(tunnel_id, tunnelName); remove_tunnel(tunnel_id); } catch(const std::runtime_error& error) diff --git a/orchagent/vxlanorch.h b/orchagent/vxlanorch.h index edc65d97fe..54e3c11567 100644 --- a/orchagent/vxlanorch.h +++ b/orchagent/vxlanorch.h @@ -35,6 +35,9 @@ 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) : - Orch2(db, tableName, request_), - m_stateVxlanTable(statedb, STATE_VXLAN_TUNNEL_TABLE_NAME) - {} - + VxlanTunnelOrch(DBConnector *statedb, DBConnector *db, const std::string& tableName); bool isTunnelExists(const std::string& tunnelName) const { @@ -338,7 +337,10 @@ class VxlanTunnelOrch : public Orch2 vxlan_vni_vlan_map_table_.erase(vni); } - + unordered_set generateTunnelCounterStats(); + void generateTunnelCounterMap(); + void addTunnelToFlexCounter(sai_object_id_t oid, const std::string &name); + void removeTunnelFromFlexCounter(sai_object_id_t oid, const std::string &name); private: virtual bool addOperation(const Request& request); @@ -349,6 +351,12 @@ class VxlanTunnelOrch : public Orch2 VxlanVniVlanMapTable vxlan_vni_vlan_map_table_; VTEPTable vtep_table_; Table m_stateVxlanTable; + FlexCounterManager vxlan_tunnel_stat_manager; + bool m_isTunnelCounterMapGenerated = false; + FlexCounterManager *tunnel_stat_manager; + unique_ptr
m_tunnelNameTable; + unique_ptr
m_tunnelTypeTable; + shared_ptr m_counter_db; }; const request_description_t vxlan_tunnel_map_request_description = { 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/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/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_flex_counters.py b/tests/test_flex_counters.py index ecdc844572..b38a5594c4 100644 --- a/tests/test_flex_counters.py +++ b/tests/test_flex_counters.py @@ -8,6 +8,7 @@ BUFFER_POOL_WATERMARK_KEY = "BUFFER_POOL_WATERMARK" PORT_BUFFER_DROP_KEY = "PORT_BUFFER_DROP" PG_WATERMARK_KEY = "PG_WATERMARK" +TUNNEL_KEY = "TUNNEL" # Counter stats on FlexCountersDB PORT_STAT = "PORT_STAT_COUNTER" @@ -16,6 +17,7 @@ BUFFER_POOL_WATERMARK_STAT = "BUFFER_POOL_WATERMARK_STAT_COUNTER" PORT_BUFFER_DROP_STAT = "PORT_BUFFER_DROP_STAT" PG_WATERMARK_STAT = "PG_WATERMARK_STAT_COUNTER" +TUNNEL_STAT = "TUNNEL_STAT_COUNTER" # Counter maps on CountersDB PORT_MAP = "COUNTERS_PORT_NAME_MAP" @@ -24,7 +26,9 @@ BUFFER_POOL_WATERMARK_MAP = "COUNTERS_BUFFER_POOL_NAME_MAP" PORT_BUFFER_DROP_MAP = "COUNTERS_PORT_NAME_MAP" PG_WATERMARK_MAP = "COUNTERS_PG_NAME_MAP" +TUNNEL_MAP = "COUNTERS_TUNNEL_NAME_MAP" +TUNNEL_TYPE_MAP = "COUNTERS_TUNNEL_TYPE_MAP" NUMBER_OF_RETRIES = 10 CPU_PORT_OID = "0x0" @@ -33,7 +37,9 @@ "rif_counter":[RIF_KEY, RIF_STAT, RIF_MAP], "buffer_pool_watermark_counter":[BUFFER_POOL_WATERMARK_KEY, BUFFER_POOL_WATERMARK_STAT, BUFFER_POOL_WATERMARK_MAP], "port_buffer_drop_counter":[PORT_BUFFER_DROP_KEY, PORT_BUFFER_DROP_STAT, PORT_BUFFER_DROP_MAP], - "pg_watermark_counter":[PG_WATERMARK_KEY, PG_WATERMARK_STAT, PG_WATERMARK_MAP]} + "pg_watermark_counter":[PG_WATERMARK_KEY, PG_WATERMARK_STAT, PG_WATERMARK_MAP], + "vxlan_tunnel_counter":[TUNNEL_KEY, TUNNEL_STAT, TUNNEL_MAP]} + class TestFlexCounters(object): @@ -66,6 +72,15 @@ def verify_no_flex_counters_tables(self, counter_stat): counters_stat_keys = self.flex_db.get_keys("FLEX_COUNTER_TABLE:" + counter_stat) assert len(counters_stat_keys) == 0, "FLEX_COUNTER_TABLE:" + str(counter_stat) + " tables exist before enabling the flex counter group" + def verify_no_flex_counters_tables_after_delete(self, counter_stat): + for retry in range(NUMBER_OF_RETRIES): + counters_stat_keys = self.flex_db.get_keys("FLEX_COUNTER_TABLE:" + counter_stat + ":") + if len(counters_stat_keys) == 0: + return + else: + time.sleep(1) + assert False, "FLEX_COUNTER_TABLE:" + str(counter_stat) + " tables exist after removing the entries" + def verify_flex_counters_populated(self, map, stat): counters_keys = self.counters_db.db_connection.hgetall(map) for counter_entry in counters_keys.items(): @@ -73,6 +88,15 @@ def verify_flex_counters_populated(self, map, stat): oid = counter_entry[1] self.wait_for_id_list(stat, name, oid) + def verify_tunnel_type_vxlan(self, name_map, type_map): + counters_keys = self.counters_db.db_connection.hgetall(name_map) + for counter_entry in counters_keys.items(): + name = counter_entry[0] + oid = counter_entry[1] + fvs = self.counters_db.get_entry(type_map, "") + assert fvs != {} + assert fvs.get(oid) == "SAI_TUNNEL_TYPE_VXLAN" + def verify_only_phy_ports_created(self): port_counters_keys = self.counters_db.db_connection.hgetall(PORT_MAP) port_counters_stat_keys = self.flex_db.get_keys("FLEX_COUNTER_TABLE:" + PORT_STAT) @@ -102,6 +126,12 @@ def test_flex_counters(self, dvs, counter_type): self.config_db.db_connection.hset('INTERFACE|Ethernet0', "NULL", "NULL") self.config_db.db_connection.hset('INTERFACE|Ethernet0|192.168.0.1/24', "NULL", "NULL") + if counter_type == "vxlan_tunnel_counter": + self.config_db.db_connection.hset("VLAN|Vlan10", "vlanid", "10") + self.config_db.db_connection.hset("VXLAN_TUNNEL|vtep1", "src_ip", "1.1.1.1") + self.config_db.db_connection.hset("VXLAN_TUNNEL_MAP|vtep1|map_100_Vlan10", "vlan", "Vlan10") + self.config_db.db_connection.hset("VXLAN_TUNNEL_MAP|vtep1|map_100_Vlan10", "vni", "100") + self.enable_flex_counter_group(counter_key, counter_map) self.verify_flex_counters_populated(counter_map, counter_stat) @@ -110,3 +140,10 @@ def test_flex_counters(self, dvs, counter_type): if counter_type == "rif_counter": self.config_db.db_connection.hdel('INTERFACE|Ethernet0|192.168.0.1/24', "NULL") + + if counter_type == "vxlan_tunnel_counter": + self.verify_tunnel_type_vxlan(counter_map, TUNNEL_TYPE_MAP) + self.config_db.delete_entry("VLAN","Vlan10") + self.config_db.delete_entry("VLAN_TUNNEL","vtep1") + self.config_db.delete_entry("VLAN_TUNNEL_MAP","vtep1|map_100_Vlan10") + self.verify_no_flex_counters_tables_after_delete(counter_stat) 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