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/buffermgrd.cpp b/cfgmgr/buffermgrd.cpp index c9949fa5d4..7824de8df1 100644 --- a/cfgmgr/buffermgrd.cpp +++ b/cfgmgr/buffermgrd.cpp @@ -180,7 +180,8 @@ int main(int argc, char **argv) TableConnector(&cfgDb, CFG_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME), TableConnector(&cfgDb, CFG_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME), TableConnector(&cfgDb, CFG_DEFAULT_LOSSLESS_BUFFER_PARAMETER), - TableConnector(&stateDb, STATE_BUFFER_MAXIMUM_VALUE_TABLE) + TableConnector(&stateDb, STATE_BUFFER_MAXIMUM_VALUE_TABLE), + TableConnector(&stateDb, STATE_PORT_TABLE_NAME) }; cfgOrchList.emplace_back(new BufferMgrDynamic(&cfgDb, &stateDb, &applDb, buffer_table_connectors, db_items_ptr)); } diff --git a/cfgmgr/buffermgrdyn.cpp b/cfgmgr/buffermgrdyn.cpp index 2f7c2c846a..071fec6f78 100644 --- a/cfgmgr/buffermgrdyn.cpp +++ b/cfgmgr/buffermgrdyn.cpp @@ -43,6 +43,7 @@ BufferMgrDynamic::BufferMgrDynamic(DBConnector *cfgDb, DBConnector *stateDb, DBC m_applBufferQueueTable(applDb, APP_BUFFER_QUEUE_TABLE_NAME), m_applBufferIngressProfileListTable(applDb, APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME), m_applBufferEgressProfileListTable(applDb, APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME), + m_statePortTable(stateDb, STATE_PORT_TABLE_NAME), m_stateBufferMaximumTable(stateDb, STATE_BUFFER_MAXIMUM_VALUE_TABLE), m_stateBufferPoolTable(stateDb, STATE_BUFFER_POOL_TABLE_NAME), m_stateBufferProfileTable(stateDb, STATE_BUFFER_PROFILE_TABLE_NAME), @@ -188,6 +189,7 @@ void BufferMgrDynamic::initTableHandlerMap() m_bufferTableHandlerMap.insert(buffer_handler_pair(CFG_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME, &BufferMgrDynamic::handleBufferPortEgressProfileListTable)); m_bufferTableHandlerMap.insert(buffer_handler_pair(CFG_PORT_TABLE_NAME, &BufferMgrDynamic::handlePortTable)); m_bufferTableHandlerMap.insert(buffer_handler_pair(CFG_PORT_CABLE_LEN_TABLE_NAME, &BufferMgrDynamic::handleCableLenTable)); + m_bufferTableHandlerMap.insert(buffer_handler_pair(STATE_PORT_TABLE_NAME, &BufferMgrDynamic::handlePortStateTable)); } // APIs to handle variant kinds of keys @@ -289,11 +291,80 @@ string BufferMgrDynamic::getDynamicProfileName(const string &speed, const string return buffer_profile_key + "_profile"; } +string BufferMgrDynamic::getMaxSpeedFromList(string speedList) +{ + auto &&speedVec = tokenize(speedList, ','); + unsigned long maxSpeedNum = 0, speedNum; + for (auto &speedStr : speedVec) + { + speedNum = atol(speedStr.c_str()); + if (speedNum > maxSpeedNum) + { + maxSpeedNum = speedNum; + } + } + + return to_string(maxSpeedNum); +} + string BufferMgrDynamic::getPgPoolMode() { return m_bufferPoolLookup[INGRESS_LOSSLESS_PG_POOL_NAME].mode; } +// Conduct the effective speed and compare the new value against the old one +// Return true if they differ and false otherwise, meaning the headroom should be updated accordingly. +// +// The way to conduct the effective speed for headroom calculating +// If auto_neg enabled on the port +// if adv_speed configured +// max(adv_speed) => new effective speed +// elif sup_speed learnt +// max(sup_speed) => new effective speed +// elif PORT READY +// INITIALIZING => port.state +// else +// port speed will be the new effective speed +// return whether effective speed has been updated +bool BufferMgrDynamic::needRefreshPortDueToEffectiveSpeed(port_info_t &portInfo, string &portName) +{ + string newEffectiveSpeed; + + if (portInfo.auto_neg) + { + if (isNonZero(portInfo.adv_speeds)) + { + newEffectiveSpeed = move(getMaxSpeedFromList(portInfo.adv_speeds)); + SWSS_LOG_INFO("Port %s: maximum configured advertised speed (%s from %s) is taken as the effective speed", + portName.c_str(), portInfo.effective_speed.c_str(), portInfo.adv_speeds.c_str()); + } + else if (isNonZero(portInfo.supported_speeds)) + { + newEffectiveSpeed = move(getMaxSpeedFromList(portInfo.supported_speeds)); + SWSS_LOG_INFO("Port %s: maximum supported speed (%s from %s) is taken as the effective speed", + portName.c_str(), portInfo.effective_speed.c_str(), portInfo.supported_speeds.c_str()); + } + else if (portInfo.state == PORT_READY) + { + portInfo.state = PORT_INITIALIZING; + SWSS_LOG_NOTICE("Port %s: unable to deduct the effective speed because auto negotiation is enabled but neither configured advertised speed nor supported speed is available", portName.c_str()); + } + } + else + { + newEffectiveSpeed = portInfo.speed; + SWSS_LOG_INFO("Port %s: speed (%s) is taken as the effective speed", portName.c_str(), portInfo.effective_speed.c_str()); + } + + bool effectiveSpeedChanged = (newEffectiveSpeed != portInfo.effective_speed); + if (effectiveSpeedChanged) + { + portInfo.effective_speed = newEffectiveSpeed; + } + + return effectiveSpeedChanged; +} + // Meta flows which are called by main flows void BufferMgrDynamic::calculateHeadroomSize(buffer_profile_t &headroom) { @@ -907,10 +978,6 @@ task_process_status BufferMgrDynamic::refreshPgsForPort(const string &port, cons isHeadroomUpdated = true; } - portInfo.speed = speed; - portInfo.cable_length = cable_length; - portInfo.gearbox_model = gearbox_model; - if (isHeadroomUpdated) { checkSharedBufferPoolSize(); @@ -920,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()) { @@ -1055,7 +1120,7 @@ task_process_status BufferMgrDynamic::doUpdatePgTask(const string &pg_key, const // Not having profile_name but both speed and cable length have been configured for that port // This is because the first PG on that port is configured after speed, cable length configured // Just regenerate the profile - task_status = refreshPgsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu, pg_key); + task_status = refreshPgsForPort(port, portInfo.effective_speed, portInfo.cable_length, portInfo.mtu, pg_key); if (task_status != task_process_status::task_success) return task_status; @@ -1068,7 +1133,7 @@ task_process_status BufferMgrDynamic::doUpdatePgTask(const string &pg_key, const } else { - task_status = refreshPgsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu, pg_key); + task_status = refreshPgsForPort(port, portInfo.effective_speed, portInfo.cable_length, portInfo.mtu, pg_key); if (task_status != task_process_status::task_success) return task_status; } @@ -1113,7 +1178,7 @@ task_process_status BufferMgrDynamic::doRemovePgTask(const string &pg_key, const if (portInfo.state != PORT_ADMIN_DOWN) { - if (!portInfo.speed.empty() && !portInfo.cable_length.empty()) + if (!portInfo.effective_speed.empty() && !portInfo.cable_length.empty()) portInfo.state = PORT_READY; else portInfo.state = PORT_INITIALIZING; @@ -1150,7 +1215,10 @@ 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); - rc = refreshPgsForPort(portName, port.speed, port.cable_length, port.mtu); + if (port.state != PORT_READY) + continue; + + rc = refreshPgsForPort(portName, port.effective_speed, port.cable_length, port.mtu); if (task_process_status::task_success != rc) { SWSS_LOG_ERROR("Update the profile on %s failed", key.c_str()); @@ -1282,13 +1350,13 @@ task_process_status BufferMgrDynamic::handleCableLenTable(KeyOpFieldsValuesTuple auto &port = fvField(i); auto &cable_length = fvValue(i); port_info_t &portInfo = m_portInfoLookup[port]; - string &speed = portInfo.speed; + string &effectiveSpeed = portInfo.effective_speed; string &mtu = portInfo.mtu; SWSS_LOG_DEBUG("Handling CABLE_LENGTH table field %s length %s", port.c_str(), cable_length.c_str()); SWSS_LOG_DEBUG("Port Info for %s before handling %s %s %s", port.c_str(), - portInfo.speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); + portInfo.effective_speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); if (portInfo.cable_length == cable_length) { @@ -1296,7 +1364,7 @@ task_process_status BufferMgrDynamic::handleCableLenTable(KeyOpFieldsValuesTuple } portInfo.cable_length = cable_length; - if (speed.empty()) + if (effectiveSpeed.empty()) { SWSS_LOG_WARN("Speed for %s hasn't been configured yet, unable to calculate headroom", port.c_str()); // We don't retry here because it doesn't make sense until the speed is configured. @@ -1325,11 +1393,11 @@ task_process_status BufferMgrDynamic::handleCableLenTable(KeyOpFieldsValuesTuple { case PORT_INITIALIZING: portInfo.state = PORT_READY; - task_status = refreshPgsForPort(port, speed, cable_length, mtu); + task_status = refreshPgsForPort(port, effectiveSpeed, cable_length, mtu); break; case PORT_READY: - task_status = refreshPgsForPort(port, speed, cable_length, mtu); + task_status = refreshPgsForPort(port, effectiveSpeed, cable_length, mtu); break; case PORT_ADMIN_DOWN: @@ -1353,7 +1421,7 @@ task_process_status BufferMgrDynamic::handleCableLenTable(KeyOpFieldsValuesTuple SWSS_LOG_DEBUG("Port Info for %s after handling speed %s cable %s gb %s", port.c_str(), - portInfo.speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); + portInfo.effective_speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); } } @@ -1365,6 +1433,37 @@ task_process_status BufferMgrDynamic::handleCableLenTable(KeyOpFieldsValuesTuple return task_process_status::task_success; } +task_process_status BufferMgrDynamic::handlePortStateTable(KeyOpFieldsValuesTuple &tuple) +{ + auto &port = kfvKey(tuple); + string op = kfvOp(tuple); + + if (op == SET_COMMAND) + { + for (auto i : kfvFieldsValues(tuple)) + { + if (fvField(i) == "supported_speeds") + { + auto &portInfo = m_portInfoLookup[port]; + if (fvValue(i) != portInfo.supported_speeds) + { + portInfo.supported_speeds = fvValue(i); + SWSS_LOG_INFO("Port %s: supported speeds updated to %s", port.c_str(), portInfo.supported_speeds.c_str()); + if (portInfo.auto_neg && needRefreshPortDueToEffectiveSpeed(portInfo, port)) + { + if (isNonZero(portInfo.cable_length) && portInfo.state != PORT_ADMIN_DOWN) + { + portInfo.state = PORT_READY; + refreshPgsForPort(port, portInfo.effective_speed, portInfo.cable_length, portInfo.mtu); + } + } + } + } + } + } + + return task_process_status::task_success; +} // A tiny state machine is required for handling the events // flags: // speed_updated @@ -1380,7 +1479,8 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu { auto &port = kfvKey(tuple); string op = kfvOp(tuple); - bool speed_updated = false, mtu_updated = false, admin_status_updated = false, admin_up; + bool effective_speed_updated = false, mtu_updated = false, admin_status_updated = false, admin_up = false; + bool need_check_speed = false; SWSS_LOG_DEBUG("Processing command:%s PORT table key %s", op.c_str(), port.c_str()); @@ -1388,15 +1488,12 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu SWSS_LOG_DEBUG("Port Info for %s before handling %s %s %s", port.c_str(), - portInfo.speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); + portInfo.effective_speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); task_process_status task_status = task_process_status::task_success; if (op == SET_COMMAND) { - string old_speed; - string old_mtu; - for (auto i : kfvFieldsValues(tuple)) { if (fvField(i) == "lanes") @@ -1404,57 +1501,77 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu auto &lanes = fvValue(i); portInfo.lane_count = count(lanes.begin(), lanes.end(), ',') + 1; } - - if (fvField(i) == "speed" && fvValue(i) != portInfo.speed) + else if (fvField(i) == "speed") { - speed_updated = true; - old_speed = move(portInfo.speed); - portInfo.speed = fvValue(i); + if (fvValue(i) != portInfo.speed) + { + need_check_speed = true; + auto old_speed = move(portInfo.speed); + portInfo.speed = fvValue(i); + SWSS_LOG_INFO("Port %s: speed updated from %s to %s", port.c_str(), old_speed.c_str(), portInfo.speed.c_str()); + } } - - if (fvField(i) == "mtu" && fvValue(i) != portInfo.mtu) + else if (fvField(i) == "mtu") { - mtu_updated = true; - old_mtu = move(portInfo.mtu); - portInfo.mtu = fvValue(i); + if (fvValue(i) != portInfo.mtu) + { + auto old_mtu = move(portInfo.mtu); + mtu_updated = true; + portInfo.mtu = fvValue(i); + SWSS_LOG_INFO("Port %s: MTU updated from %s to %s", port.c_str(), old_mtu.c_str(), portInfo.mtu.c_str()); + } } - - if (fvField(i) == "admin_status") + else if (fvField(i) == "admin_status") { admin_up = (fvValue(i) == "up"); auto old_admin_up = (portInfo.state != PORT_ADMIN_DOWN); admin_status_updated = (admin_up != old_admin_up); } - } - - string &cable_length = portInfo.cable_length; - string &mtu = portInfo.mtu; - string &speed = portInfo.speed; - - bool need_refresh_all_pgs = false, need_remove_all_pgs = false; - - if (speed_updated || mtu_updated) - { - if (!cable_length.empty() && !speed.empty()) + else if (fvField(i) == "adv_speeds") { - if (speed_updated) + if (fvValue(i) != portInfo.adv_speeds) { - if (mtu_updated) + auto old_adv_speeds = move(portInfo.adv_speeds); + if (fvValue(i) == "all") { - SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to speed updated from %s to %s and MTU updated from %s to %s", - port.c_str(), old_speed.c_str(), portInfo.speed.c_str(), old_mtu.c_str(), portInfo.mtu.c_str()); + portInfo.adv_speeds.clear(); } else { - SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to speed updated from %s to %s", - port.c_str(), old_speed.c_str(), portInfo.speed.c_str()); + portInfo.adv_speeds = fvValue(i); } + need_check_speed = true; + SWSS_LOG_INFO("Port %s: advertised speed updated from %s to %s", port.c_str(), old_adv_speeds.c_str(), portInfo.adv_speeds.c_str()); } - else + } + else if (fvField(i) == "autoneg") + { + auto auto_neg = (fvValue(i) == "on"); + if (auto_neg != portInfo.auto_neg) { - SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to MTU updated from %s to %s", - port.c_str(), old_mtu.c_str(), portInfo.mtu.c_str()); + portInfo.auto_neg = auto_neg; + need_check_speed = true; + SWSS_LOG_INFO("Port %s: auto negotiation %s", port.c_str(), (portInfo.auto_neg ? "enabled" : "disabled")); } + } + } + + if (need_check_speed && needRefreshPortDueToEffectiveSpeed(portInfo, port)) + { + effective_speed_updated = true; + } + + string &cable_length = portInfo.cable_length; + string &mtu = portInfo.mtu; + string &effective_speed = portInfo.effective_speed; + + bool need_refresh_all_pgs = false, need_remove_all_pgs = false; + + if (effective_speed_updated || mtu_updated) + { + if (!cable_length.empty() && !effective_speed.empty()) + { + SWSS_LOG_INFO("Updating BUFFER_PG for port %s due to effective speed and/or MTU updated", port.c_str()); // Try updating the buffer information switch (portInfo.state) @@ -1474,7 +1591,7 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu break; case PORT_ADMIN_DOWN: - SWSS_LOG_INFO("Nothing to be done when port %s's speed or cable length updated since the port is administratively down", port.c_str()); + SWSS_LOG_INFO("Nothing to be done when port %s's effective speed or cable length updated since the port is administratively down", port.c_str()); break; default: @@ -1482,13 +1599,13 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu break; } - SWSS_LOG_DEBUG("Port Info for %s after handling speed %s cable %s gb %s", + SWSS_LOG_DEBUG("Port Info for %s after handling effective speed %s cable %s gb %s", port.c_str(), - portInfo.speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); + portInfo.effective_speed.c_str(), portInfo.cable_length.c_str(), portInfo.gearbox_model.c_str()); } else { - SWSS_LOG_WARN("Cable length or speed for %s hasn't been configured yet, unable to calculate headroom", port.c_str()); + SWSS_LOG_WARN("Cable length or effective speed for %s hasn't been configured yet, unable to calculate headroom", port.c_str()); // We don't retry here because it doesn't make sense until both cable length and speed are configured. } } @@ -1497,7 +1614,7 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu { if (admin_up) { - if (!portInfo.speed.empty() && !portInfo.cable_length.empty()) + if (!portInfo.effective_speed.empty() && !portInfo.cable_length.empty()) portInfo.state = PORT_READY; else portInfo.state = PORT_INITIALIZING; @@ -1516,15 +1633,15 @@ task_process_status BufferMgrDynamic::handlePortTable(KeyOpFieldsValuesTuple &tu } // In case both need_remove_all_pgs and need_refresh_all_pgs are true, the need_remove_all_pgs will take effect. - // This can happen when both speed (or mtu) is changed and the admin_status is down. - // In this case, we just need record the new speed (or mtu) but don't need to refresh all PGs on the port since the port is administratively down + // This can happen when both effective speed (or mtu) is changed and the admin_status is down. + // In this case, we just need record the new effective speed (or mtu) but don't need to refresh all PGs on the port since the port is administratively down if (need_remove_all_pgs) { task_status = removeAllPgsFromPort(port); } else if (need_refresh_all_pgs) { - task_status = refreshPgsForPort(port, portInfo.speed, portInfo.cable_length, portInfo.mtu); + task_status = refreshPgsForPort(port, portInfo.effective_speed, portInfo.cable_length, portInfo.mtu); } } diff --git a/cfgmgr/buffermgrdyn.h b/cfgmgr/buffermgrdyn.h index b186193ae0..3b82a27bb2 100644 --- a/cfgmgr/buffermgrdyn.h +++ b/cfgmgr/buffermgrdyn.h @@ -94,6 +94,12 @@ typedef struct { std::string cable_length; std::string mtu; std::string gearbox_model; + + bool auto_neg; + std::string effective_speed; + std::string adv_speeds; + std::string supported_speeds; + long lane_count; } port_info_t; @@ -145,6 +151,7 @@ class BufferMgrDynamic : public Orch // PORT and CABLE_LENGTH table and caches Table m_cfgPortTable; Table m_cfgCableLenTable; + Table m_statePortTable; // m_portInfoLookup // key: port name // updated only when a port's speed and cable length updated @@ -223,6 +230,7 @@ class BufferMgrDynamic : public Orch { return !value.empty() && value != "0"; } + std::string getMaxSpeedFromList(std::string speedList); // APPL_DB table operations void updateBufferPoolToDb(const std::string &name, const buffer_pool_t &pool); @@ -230,6 +238,7 @@ class BufferMgrDynamic : public Orch void updateBufferPgToDb(const std::string &key, const std::string &profile, bool add); // Meta flows + bool needRefreshPortDueToEffectiveSpeed(port_info_t &portInfo, std::string &portName); void calculateHeadroomSize(buffer_profile_t &headroom); void checkSharedBufferPoolSize(bool force_update_during_initialization); void recalculateSharedBufferPool(); @@ -251,6 +260,7 @@ class BufferMgrDynamic : public Orch task_process_status handleBufferMaxParam(KeyOpFieldsValuesTuple &t); task_process_status handleDefaultLossLessBufferParam(KeyOpFieldsValuesTuple &t); task_process_status handleCableLenTable(KeyOpFieldsValuesTuple &t); + task_process_status handlePortStateTable(KeyOpFieldsValuesTuple &t); task_process_status handlePortTable(KeyOpFieldsValuesTuple &t); task_process_status handleBufferPoolTable(KeyOpFieldsValuesTuple &t); task_process_status handleBufferProfileTable(KeyOpFieldsValuesTuple &t); 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..5a5caf5a89 100644 --- a/fdbsyncd/fdbsync.cpp +++ b/fdbsyncd/fdbsync.cpp @@ -1,721 +1,721 @@ -#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_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); + } +} + diff --git a/fdbsyncd/fdbsync.h b/fdbsyncd/fdbsync.h index ee6aa0845b..927b95dd0b 100644 --- a/fdbsyncd/fdbsync.h +++ b/fdbsyncd/fdbsync.h @@ -1,145 +1,145 @@ -#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 *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 + diff --git a/fdbsyncd/fdbsyncd.cpp b/fdbsyncd/fdbsyncd.cpp index dac4bb85e5..6b9724d89e 100644 --- a/fdbsyncd/fdbsyncd.cpp +++ b/fdbsyncd/fdbsyncd.cpp @@ -1,156 +1,156 @@ -#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.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; +} 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/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..bdd899057a 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++) 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..229dec0b15 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -503,7 +503,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)) 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/flexcounterorch.cpp b/orchagent/flexcounterorch.cpp index bf40d7c05f..41f024c639 100644 --- a/orchagent/flexcounterorch.cpp +++ b/orchagent/flexcounterorch.cpp @@ -18,6 +18,11 @@ extern IntfsOrch *gIntfsOrch; extern BufferOrch *gBufferOrch; #define BUFFER_POOL_WATERMARK_KEY "BUFFER_POOL_WATERMARK" +#define PORT_KEY "PORT" +#define PORT_BUFFER_DROP_KEY "PORT_BUFFER_DROP" +#define QUEUE_KEY "QUEUE" +#define PG_WATERMARK_KEY "PG_WATERMARK" +#define RIF_KEY "RIF" unordered_map flexCounterGroupMap = { @@ -102,26 +107,31 @@ void FlexCounterOrch::doTask(Consumer &consumer) // which is automatically satisfied upon the creation of the orch object that requires // the syncd flex counter polling service // This postponement is introduced by design to accelerate the initialization process - // - // generateQueueMap() is called as long as a field "FLEX_COUNTER_STATUS" event is heard, - // regardless of whether the key is "QUEUE" or whether the value is "enable" or "disable" - // This can be because generateQueueMap() installs a fundamental list of queue stats - // that need to be polled. So my doubt here is if queue watermark stats shall be piggybacked - // into the same function as they may not be counted as fundamental - if(gPortsOrch) + if(gPortsOrch && (value == "enable")) { - gPortsOrch->generateQueueMap(); - gPortsOrch->generatePriorityGroupMap(); + if(key == PORT_KEY) + { + gPortsOrch->generatePortCounterMap(); + m_port_counter_enabled = true; + } + else if(key == PORT_BUFFER_DROP_KEY) + { + gPortsOrch->generatePortBufferDropCounterMap(); + m_port_buffer_drop_counter_enabled = true; + } + else if(key == QUEUE_KEY) + { + gPortsOrch->generateQueueMap(); + } + else if(key == PG_WATERMARK_KEY) + { + gPortsOrch->generatePriorityGroupMap(); + } } - if(gPortsOrch) - { - gPortsOrch->generatePriorityGroupMap(); - } - if(gIntfsOrch) + if(gIntfsOrch && (key == RIF_KEY) && (value == "enable")) { gIntfsOrch->generateInterfaceMap(); } - // Install COUNTER_ID_LIST/ATTR_ID_LIST only when hearing buffer pool watermark enable event if (gBufferOrch && (key == BUFFER_POOL_WATERMARK_KEY) && (value == "enable")) { gBufferOrch->generateBufferPoolWatermarkCounterIdList(); @@ -144,3 +154,13 @@ void FlexCounterOrch::doTask(Consumer &consumer) consumer.m_toSync.erase(it++); } } + +bool FlexCounterOrch::getPortCountersState() const +{ + return m_port_counter_enabled; +} + +bool FlexCounterOrch::getPortBufferDropCountersState() const +{ + return m_port_buffer_drop_counter_enabled; +} diff --git a/orchagent/flexcounterorch.h b/orchagent/flexcounterorch.h index 27f6e64ab1..0fb9f70e4b 100644 --- a/orchagent/flexcounterorch.h +++ b/orchagent/flexcounterorch.h @@ -15,10 +15,14 @@ class FlexCounterOrch: public Orch void doTask(Consumer &consumer); FlexCounterOrch(swss::DBConnector *db, std::vector &tableNames); virtual ~FlexCounterOrch(void); + bool getPortCountersState() const; + bool getPortBufferDropCountersState() const; private: std::shared_ptr m_flexCounterDb = nullptr; std::shared_ptr m_flexCounterGroupTable = nullptr; + bool m_port_counter_enabled = false; + bool m_port_buffer_drop_counter_enabled = false; }; #endif 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 668786194d..37c2ef73df 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -479,10 +479,6 @@ task_process_status MirrorOrch::deleteEntry(const string& name) if (session.status) { - if (session.type != MIRROR_SESSION_SPAN) - { - m_routeOrch->detach(this, session.dstIp); - } if (!deactivateSession(name, session)) { SWSS_LOG_ERROR("Failed to remove mirror session %s", name.c_str()); @@ -490,6 +486,11 @@ task_process_status MirrorOrch::deleteEntry(const string& name) } } + if (session.type != MIRROR_SESSION_SPAN) + { + m_routeOrch->detach(this, session.dstIp); + } + if (!session.policer.empty()) { m_policerOrch->decreaseRefCount(session.policer); @@ -758,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); @@ -774,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/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 6b3f2be658..b9870a24eb 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -783,16 +783,16 @@ bool NeighOrch::addNeighbor(const NeighborEntry &neighborEntry, const MacAddress MuxOrch* mux_orch = gDirectory.get(); bool hw_config = isHwConfigured(neighborEntry); - if (!hw_config && mux_orch->isNeighborActive(ip_address, macAddress, alias)) + if (gMySwitchType == "voq") { - if (gMySwitchType == "voq") + if (!addVoqEncapIndex(alias, ip_address, neighbor_attrs)) { - if (!addVoqEncapIndex(alias, ip_address, neighbor_attrs)) - { - return false; - } + return false; } + } + if (!hw_config && mux_orch->isNeighborActive(ip_address, macAddress, alias)) + { status = sai_neighbor_api->create_neighbor_entry(&neighbor_entry, (uint32_t)neighbor_attrs.size(), neighbor_attrs.data()); if (status != SAI_STATUS_SUCCESS) @@ -1230,8 +1230,43 @@ void NeighOrch::doVoqSystemNeighTask(Consumer &consumer) } if (m_syncdNeighbors.find(neighbor_entry) == m_syncdNeighbors.end() || - m_syncdNeighbors[neighbor_entry].mac != mac_address) + m_syncdNeighbors[neighbor_entry].mac != mac_address || + m_syncdNeighbors[neighbor_entry].voq_encap_index != encap_index) { + + if (m_syncdNeighbors.find(neighbor_entry) != m_syncdNeighbors.end() && + m_syncdNeighbors[neighbor_entry].voq_encap_index != encap_index) + { + + // Encap index changed. Set encap index attribute with new encap index + if (!updateVoqNeighborEncapIndex(neighbor_entry, encap_index)) + { + // Setting encap index failed. SAI does not support change of encap index for + // existing neighbors. Remove the neighbor but do not errase from consumer sync + // buffer. The next iteration will add the neighbor back with new encap index + + SWSS_LOG_NOTICE("VOQ encap index set failed for neighbor %s. Removing and re-adding", kfvKey(t).c_str()); + + //Remove neigh from SAI + if (removeNeighbor(neighbor_entry)) + { + //neigh successfully deleted from SAI. Set STATE DB to signal to remove entries from kernel + m_stateSystemNeighTable->del(state_key); + } + else + { + SWSS_LOG_ERROR("Failed to remove voq neighbor %s from SAI during encap index update", kfvKey(t).c_str()); + } + it++; + } + else + { + SWSS_LOG_NOTICE("VOQ encap index updated for neighbor %s", kfvKey(t).c_str()); + it = consumer.m_toSync.erase(it); + } + continue; + } + //Add neigh to SAI if (addNeighbor(neighbor_entry, mac_address)) { @@ -1242,6 +1277,42 @@ void NeighOrch::doVoqSystemNeighTask(Consumer &consumer) if(ibif.m_type != Port::VLAN) { mac_address = gMacAddress; + + // For VS platforms, the mac of the static neigh should not be same as asic's own mac. + // This is because host originated packets will have same mac for both src and dst which + // will result in host NOT sending packet out. To address this problem which is specific + // to port type inband interfaces, set the mac to the neighbor's owner asic's mac. Since + // the owner asic's mac is not readily avaiable here, the owner asic mac is derived from + // the switch id and lower 5 bytes of asic mac which is assumed to be same for all asics + // in the VS system. + // Therefore to make VOQ chassis systems work in VS platform based setups like the setups + // using KVMs, it is required that all asics have same base mac in the format given below + // :<6th byte = switch_id> + + string platform = getenv("ASIC_VENDOR") ? getenv("ASIC_VENDOR") : ""; + + if (platform == VS_PLATFORM_SUBSTRING) + { + int8_t sw_id = -1; + uint8_t egress_asic_mac[ETHER_ADDR_LEN]; + + gMacAddress.getMac(egress_asic_mac); + + if (p.m_type == Port::LAG) + { + sw_id = (int8_t) p.m_system_lag_info.switch_id; + } + else if (p.m_type == Port::PHY || p.m_type == Port::SYSTEM) + { + sw_id = (int8_t) p.m_system_port_info.switch_id; + } + + if(sw_id != -1) + { + egress_asic_mac[5] = sw_id; + mac_address = MacAddress(egress_asic_mac); + } + } } vector fvVector; FieldValueTuple mac("neigh", mac_address.to_string()); @@ -1467,6 +1538,31 @@ void NeighOrch::voqSyncAddNeigh(string &alias, IpAddress &ip_address, const MacA sai_attribute_t attr; sai_status_t status; + // Get the encap index and store it for handling change of + // encap index for remote neighbors synced via CHASSIS_APP_DB + + attr.id = SAI_NEIGHBOR_ENTRY_ATTR_ENCAP_INDEX; + + status = sai_neighbor_api->get_neighbor_entry_attribute(&neighbor_entry, 1, &attr); + 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); + task_process_status handle_status = handleSaiGetStatus(SAI_API_NEIGHBOR, status); + if (handle_status != task_process_status::task_success) + { + return; + } + } + + if (!attr.value.u32) + { + SWSS_LOG_ERROR("Invalid neighbor encap_index for %s on %s", ip_address.to_string().c_str(), alias.c_str()); + return; + } + + NeighborEntry nbrEntry = {ip_address, alias}; + m_syncdNeighbors[nbrEntry].voq_encap_index = attr.value.u32; + //Sync only local neigh. Confirm for the local neigh and //get the system port alias for key for syncing to CHASSIS_APP_DB Port port; @@ -1495,21 +1591,6 @@ void NeighOrch::voqSyncAddNeigh(string &alias, IpAddress &ip_address, const MacA return; } - attr.id = SAI_NEIGHBOR_ENTRY_ATTR_ENCAP_INDEX; - - status = sai_neighbor_api->get_neighbor_entry_attribute(&neighbor_entry, 1, &attr); - 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; - } - - if (!attr.value.u32) - { - SWSS_LOG_ERROR("Invalid neighbor encap_index for %s on %s", ip_address.to_string().c_str(), alias.c_str()); - return; - } - vector attrs; FieldValueTuple eiFv ("encap_index", to_string(attr.value.u32)); @@ -1555,3 +1636,42 @@ void NeighOrch::voqSyncDelNeigh(string &alias, IpAddress &ip_address) string key = alias + m_tableVoqSystemNeighTable->getTableNameSeparator().c_str() + ip_address.to_string(); m_tableVoqSystemNeighTable->del(key); } + +bool NeighOrch::updateVoqNeighborEncapIndex(const NeighborEntry &neighborEntry, uint32_t encap_index) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + IpAddress ip_address = neighborEntry.ip_address; + string alias = neighborEntry.alias; + + sai_object_id_t rif_id = m_intfsOrch->getRouterIntfsId(alias); + if (rif_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Failed to get rif_id for %s", alias.c_str()); + return false; + } + + sai_neighbor_entry_t neighbor_entry; + neighbor_entry.rif_id = rif_id; + neighbor_entry.switch_id = gSwitchId; + copy(neighbor_entry.ip_address, ip_address); + + sai_attribute_t neighbor_attr; + neighbor_attr.id = SAI_NEIGHBOR_ENTRY_ATTR_ENCAP_INDEX; + neighbor_attr.value.u32 = encap_index; + + status = sai_neighbor_api->set_neighbor_entry_attribute(&neighbor_entry, &neighbor_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to update voq encap index for neighbor %s on %s, rv:%d", + ip_address.to_string().c_str(), alias.c_str(), status); + return false; + } + + SWSS_LOG_NOTICE("Updated voq encap index for neighbor %s on %s", ip_address.to_string().c_str(), alias.c_str()); + + m_syncdNeighbors[neighborEntry].voq_encap_index = encap_index; + + return true; +} diff --git a/orchagent/neighorch.h b/orchagent/neighorch.h index 8a3ec30c53..3b5867a949 100644 --- a/orchagent/neighorch.h +++ b/orchagent/neighorch.h @@ -27,6 +27,7 @@ struct NeighborData { MacAddress mac; bool hw_configured = false; // False means, entry is not written to HW + uint32_t voq_encap_index = 0; }; /* NeighborTable: NeighborEntry, neighbor MAC address */ @@ -108,6 +109,7 @@ class NeighOrch : public Orch, public Subject, public Observer bool addVoqEncapIndex(string &alias, IpAddress &ip, vector &neighbor_attrs); void voqSyncAddNeigh(string &alias, IpAddress &ip_address, const MacAddress &mac, sai_neighbor_entry_t &neighbor_entry); void voqSyncDelNeigh(string &alias, IpAddress &ip_address); + bool updateVoqNeighborEncapIndex(const NeighborEntry &neighborEntry, uint32_t encap_index); bool resolveNeighborEntry(const NeighborEntry &, const MacAddress &); void clearResolvedNeighborEntry(const NeighborEntry &); diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index 464e6aa688..2afc2c20f4 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -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 f8f00cd225..673c5f71ec 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -358,7 +358,11 @@ bool OrchDaemon::init() CFG_FLEX_COUNTER_TABLE_NAME }; - m_orchList.push_back(new FlexCounterOrch(m_configDb, flex_counter_tables)); + auto* flexCounterOrch = new FlexCounterOrch(m_configDb, flex_counter_tables); + m_orchList.push_back(flexCounterOrch); + + gDirectory.set(flexCounterOrch); + gDirectory.set(gPortsOrch); vector pfc_wd_tables = { CFG_PFC_WD_TABLE_NAME 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 81866a9ae1..4ac36258b9 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 @@ -284,9 +290,9 @@ static char* hostif_vlan_tag[] = { PortsOrch::PortsOrch(DBConnector *db, DBConnector *stateDb, vector &tableNames, DBConnector *chassisAppDb) : Orch(db, tableNames), m_portStateTable(stateDb, STATE_PORT_TABLE_NAME), - port_stat_manager(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, PORT_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, true), - port_buffer_drop_stat_manager(PORT_BUFFER_DROP_STAT_FLEX_COUNTER_GROUP, StatsMode::READ, PORT_BUFFER_DROP_STAT_POLLING_INTERVAL_MS, true), - queue_stat_manager(QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, QUEUE_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, true) + port_stat_manager(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, PORT_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, false), + port_buffer_drop_stat_manager(PORT_BUFFER_DROP_STAT_FLEX_COUNTER_GROUP, StatsMode::READ, PORT_BUFFER_DROP_STAT_POLLING_INTERVAL_MS, false), + queue_stat_manager(QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, QUEUE_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS, false) { SWSS_LOG_ENTER(); @@ -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()) { @@ -2273,25 +2324,32 @@ bool PortsOrch::initPort(const string &alias, const string &role, const int inde vector fields; fields.push_back(tuple); m_counterTable->set("", fields); + // Install a flex counter for this port to track stats - std::unordered_set counter_stats; - for (const auto& it: port_stat_ids) + auto flex_counters_orch = gDirectory.get(); + /* Delay installing the counters if they are yet enabled + If they are enabled, install the counters immediately */ + if (flex_counters_orch->getPortCountersState()) { - counter_stats.emplace(sai_serialize_port_stat(it)); + auto port_counter_stats = generateCounterStats(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP); + port_stat_manager.setCounterIdList(p.m_port_id, CounterType::PORT, port_counter_stats); } - port_stat_manager.setCounterIdList(p.m_port_id, CounterType::PORT, counter_stats); - std::unordered_set port_buffer_drop_stats; - for (const auto& it: port_buffer_drop_stat_ids) + if (flex_counters_orch->getPortBufferDropCountersState()) { - port_buffer_drop_stats.emplace(sai_serialize_port_stat(it)); + auto port_buffer_drop_stats = generateCounterStats(PORT_BUFFER_DROP_STAT_FLEX_COUNTER_GROUP); + port_buffer_drop_stat_manager.setCounterIdList(p.m_port_id, CounterType::PORT, port_buffer_drop_stats); } - port_buffer_drop_stat_manager.setCounterIdList(p.m_port_id, CounterType::PORT, port_buffer_drop_stats); PortUpdate update = { p, true }; notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); 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 @@ -2318,8 +2376,11 @@ void PortsOrch::deInitPort(string alias, sai_object_id_t port_id) p.m_port_id = port_id; /* remove port from flex_counter_table for updating counters */ - port_stat_manager.clearCounterIdList(p.m_port_id); - + auto flex_counters_orch = gDirectory.get(); + if ((flex_counters_orch->getPortCountersState())) + { + port_stat_manager.clearCounterIdList(p.m_port_id); + } /* remove port name map from counter table */ m_counter_db->hdel(COUNTERS_PORT_NAME_MAP, alias); @@ -2570,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)); @@ -2751,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()) @@ -2770,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) { @@ -2836,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; @@ -2877,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()); @@ -3671,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; @@ -3704,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"))) { @@ -3855,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()); @@ -3875,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()); @@ -3891,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()); @@ -3912,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()); } @@ -5072,6 +5149,48 @@ void PortsOrch::generatePriorityGroupMapPerPort(const Port& port) CounterCheckOrch::getInstance().addPort(port); } +void PortsOrch::generatePortCounterMap() +{ + if (m_isPortCounterMapGenerated) + { + return; + } + + auto port_counter_stats = generateCounterStats(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP); + for (const auto& it: m_portList) + { + // Set counter stats only for PHY ports to ensure syncd will not try to query the counter statistics from the HW for non-PHY ports. + if (it.second.m_type != Port::Type::PHY) + { + continue; + } + port_stat_manager.setCounterIdList(it.second.m_port_id, CounterType::PORT, port_counter_stats); + } + + m_isPortCounterMapGenerated = true; +} + +void PortsOrch::generatePortBufferDropCounterMap() +{ + if (m_isPortBufferDropCounterMapGenerated) + { + return; + } + + auto port_buffer_drop_stats = generateCounterStats(PORT_BUFFER_DROP_STAT_FLEX_COUNTER_GROUP); + for (const auto& it: m_portList) + { + // Set counter stats only for PHY ports to ensure syncd will not try to query the counter statistics from the HW for non-PHY ports. + if (it.second.m_type != Port::Type::PHY) + { + continue; + } + port_buffer_drop_stat_manager.setCounterIdList(it.second.m_port_id, CounterType::PORT, port_buffer_drop_stats); + } + + m_isPortBufferDropCounterMapGenerated = true; +} + void PortsOrch::doTask(NotificationConsumer &consumer) { SWSS_LOG_ENTER(); @@ -5366,7 +5485,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) @@ -5463,7 +5586,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(); @@ -5477,7 +5600,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, ',')) { @@ -5494,31 +5617,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; } @@ -5533,7 +5656,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; } @@ -5860,7 +5983,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; @@ -5872,7 +5999,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", @@ -5908,80 +6039,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; @@ -6055,7 +6112,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. @@ -6074,7 +6135,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 @@ -6232,3 +6297,23 @@ void PortsOrch::voqSyncDelLagMember(Port &lag, Port &port) string key = lag.m_system_lag_info.alias + ":" + port.m_system_port_info.alias; m_tableVoqSystemLagMemberTable->del(key); } + +std::unordered_set PortsOrch::generateCounterStats(const string& type) +{ + std::unordered_set counter_stats; + if (type == PORT_STAT_COUNTER_FLEX_COUNTER_GROUP) + { + for (const auto& it: port_stat_ids) + { + counter_stats.emplace(sai_serialize_port_stat(it)); + } + } + else if (type == PORT_BUFFER_DROP_STAT_FLEX_COUNTER_GROUP) + { + for (const auto& it: port_buffer_drop_stat_ids) + { + counter_stats.emplace(sai_serialize_port_stat(it)); + } + } + return counter_stats; +} diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 6a92b9d9dd..22efce3561 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -13,6 +13,7 @@ #include "gearboxutils.h" #include "saihelper.h" #include "lagid.h" +#include "flexcounterorch.h" #define FCS_LEN 4 @@ -125,6 +126,8 @@ class PortsOrch : public Orch, public Subject void generateQueueMap(); void generatePriorityGroupMap(); + void generatePortCounterMap(); + void generatePortBufferDropCounterMap(); void refreshPortStatus(); bool removeAclTableGroup(const Port &p); @@ -290,6 +293,9 @@ class PortsOrch : public Orch, public Subject bool m_isPriorityGroupMapGenerated = false; void generatePriorityGroupMapPerPort(const Port& port); + bool m_isPortCounterMapGenerated = false; + bool m_isPortBufferDropCounterMapGenerated = false; + bool setPortAutoNeg(sai_object_id_t id, int an); bool setPortFecMode(sai_object_id_t id, int fec); bool setPortInterfaceType(sai_object_id_t id, sai_port_interface_type_t interface_type); @@ -318,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; @@ -333,6 +338,8 @@ class PortsOrch : public Orch, public Subject void voqSyncDelLagMember(Port &lag, Port &port); unique_ptr m_lagIdAllocator; + std::unordered_set generateCounterStats(const string& type); + }; #endif /* SWSS_PORTSORCH_H */ 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/saihelper.cpp b/orchagent/saihelper.cpp index dac7b1e92f..15febdb518 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -418,7 +418,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/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/mock_orchagent_main.h b/tests/mock_tests/mock_orchagent_main.h index 587055b87e..181ebac889 100644 --- a/tests/mock_tests/mock_orchagent_main.h +++ b/tests/mock_tests/mock_orchagent_main.h @@ -15,6 +15,8 @@ #include "vxlanorch.h" #include "policerorch.h" #include "fgnhgorch.h" +#include "flexcounterorch.h" +#include "directory.h" extern int gBatchSize; extern bool gSwssRecord; @@ -42,6 +44,7 @@ extern FdbOrch *gFdbOrch; extern MirrorOrch *gMirrorOrch; extern BufferOrch *gBufferOrch; extern VRFOrch *gVrfOrch; +extern Directory gDirectory; extern sai_acl_api_t *sai_acl_api; extern sai_switch_api_t *sai_switch_api; diff --git a/tests/mock_tests/portsorch_ut.cpp b/tests/mock_tests/portsorch_ut.cpp index 365c5144b5..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,22 +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); - - 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, @@ -155,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 @@ -218,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); @@ -263,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(); @@ -333,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) { @@ -455,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. @@ -469,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); @@ -479,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. @@ -593,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_dynamic.py b/tests/test_buffer_dynamic.py index b1bc1528a3..26f423d58a 100644 --- a/tests/test_buffer_dynamic.py +++ b/tests/test_buffer_dynamic.py @@ -588,3 +588,51 @@ def test_shutdownPort(self, dvs, testlog): # Shutdown interface dvs.runcmd("config interface shutdown Ethernet0") + + def test_autoNegPort(self, dvs, testlog): + self.setup_db(dvs) + + advertised_speeds = '10000,25000,50000' + maximum_advertised_speed = '50000' + if maximum_advertised_speed == self.originalSpeed: + # Let's make sure the configured speed isn't equal to maximum advertised speed + advertised_speeds = '10000,25000' + maximum_advertised_speed = '25000' + + # Startup interfaces + dvs.runcmd('config interface startup Ethernet0') + + # Configure lossless PG 3-4 on the interface + self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) + + # Enable port auto negotiation + dvs.runcmd('config interface autoneg Ethernet0 enabled') + dvs.runcmd('config interface advertised-speeds Ethernet0 {}'.format(advertised_speeds)) + + # Check the buffer profile. The maximum_advertised_speed should be used + expectedProfile = self.make_lossless_profile_name(maximum_advertised_speed, self.originalCableLen) + self.app_db.wait_for_entry("BUFFER_PG_TABLE", "Ethernet0:3-4") + self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) + self.check_new_profile_in_asic_db(dvs, expectedProfile) + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfile)}) + + # Configure another lossless PG on the interface + self.config_db.update_entry('BUFFER_PG', 'Ethernet0|6', {'profile': 'NULL'}) + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfile)}) + + # Disable port auto negotiation + dvs.runcmd('config interface autoneg Ethernet0 disabled') + + # Check the buffer profile. The configured speed should be used + expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) + self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) + self.check_new_profile_in_asic_db(dvs, expectedProfile) + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfile)}) + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": "[BUFFER_PROFILE_TABLE:{}]".format(expectedProfile)}) + + # Remove lossless PGs on the interface + self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') + self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6') + + # Shutdown interface + dvs.runcmd('config interface shutdown Ethernet0') 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 new file mode 100644 index 0000000000..ecdc844572 --- /dev/null +++ b/tests/test_flex_counters.py @@ -0,0 +1,112 @@ +import time +import pytest + +# Counter keys on ConfigDB +PORT_KEY = "PORT" +QUEUE_KEY = "QUEUE" +RIF_KEY = "RIF" +BUFFER_POOL_WATERMARK_KEY = "BUFFER_POOL_WATERMARK" +PORT_BUFFER_DROP_KEY = "PORT_BUFFER_DROP" +PG_WATERMARK_KEY = "PG_WATERMARK" + +# Counter stats on FlexCountersDB +PORT_STAT = "PORT_STAT_COUNTER" +QUEUE_STAT = "QUEUE_STAT_COUNTER" +RIF_STAT = "RIF_STAT_COUNTER" +BUFFER_POOL_WATERMARK_STAT = "BUFFER_POOL_WATERMARK_STAT_COUNTER" +PORT_BUFFER_DROP_STAT = "PORT_BUFFER_DROP_STAT" +PG_WATERMARK_STAT = "PG_WATERMARK_STAT_COUNTER" + +# Counter maps on CountersDB +PORT_MAP = "COUNTERS_PORT_NAME_MAP" +QUEUE_MAP = "COUNTERS_QUEUE_NAME_MAP" +RIF_MAP = "COUNTERS_RIF_NAME_MAP" +BUFFER_POOL_WATERMARK_MAP = "COUNTERS_BUFFER_POOL_NAME_MAP" +PORT_BUFFER_DROP_MAP = "COUNTERS_PORT_NAME_MAP" +PG_WATERMARK_MAP = "COUNTERS_PG_NAME_MAP" + +NUMBER_OF_RETRIES = 10 +CPU_PORT_OID = "0x0" + +counter_type_dict = {"port_counter":[PORT_KEY, PORT_STAT, PORT_MAP], + "queue_counter":[QUEUE_KEY, QUEUE_STAT, QUEUE_MAP], + "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]} + +class TestFlexCounters(object): + + def setup_dbs(self, dvs): + self.config_db = dvs.get_config_db() + self.flex_db = dvs.get_flex_db() + self.counters_db = dvs.get_counters_db() + + def wait_for_table(self, table): + for retry in range(NUMBER_OF_RETRIES): + counters_keys = self.counters_db.db_connection.hgetall(table) + if len(counters_keys) > 0: + return + else: + time.sleep(1) + + assert False, str(table) + " not created in Counters DB" + + def wait_for_id_list(self, stat, name, oid): + for retry in range(NUMBER_OF_RETRIES): + id_list = self.flex_db.db_connection.hgetall("FLEX_COUNTER_TABLE:" + stat + ":" + oid).items() + if len(id_list) > 0: + return + else: + time.sleep(1) + + assert False, "No ID list for counter " + str(name) + + 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_flex_counters_populated(self, map, stat): + counters_keys = self.counters_db.db_connection.hgetall(map) + for counter_entry in counters_keys.items(): + name = counter_entry[0] + oid = counter_entry[1] + self.wait_for_id_list(stat, name, oid) + + 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) + for port_stat in port_counters_stat_keys: + assert port_stat in dict(port_counters_keys.items()).values(), "Non PHY port created on PORT_STAT_COUNTER group: {}".format(port_stat) + + def enable_flex_counter_group(self, group, map): + group_stats_entry = {"FLEX_COUNTER_STATUS": "enable"} + self.config_db.create_entry("FLEX_COUNTER_TABLE", group, group_stats_entry) + self.wait_for_table(map) + + @pytest.mark.parametrize("counter_type", counter_type_dict.keys()) + def test_flex_counters(self, dvs, counter_type): + """ + The test will check there are no flex counters tables on FlexCounter DB when the counters are disabled. + After enabling each counter group, the test will check the flow of creating flex counters tables on FlexCounter DB. + For some counter types the MAPS on COUNTERS DB will be created as well after enabling the counter group, this will be also verified on this test. + """ + self.setup_dbs(dvs) + counter_key = counter_type_dict[counter_type][0] + counter_stat = counter_type_dict[counter_type][1] + counter_map = counter_type_dict[counter_type][2] + + self.verify_no_flex_counters_tables(counter_stat) + + if counter_type == "rif_counter": + 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") + + self.enable_flex_counter_group(counter_key, counter_map) + self.verify_flex_counters_populated(counter_map, counter_stat) + + if counter_type == "port_counter": + self.verify_only_phy_ports_created() + + if counter_type == "rif_counter": + self.config_db.db_connection.hdel('INTERFACE|Ethernet0|192.168.0.1/24', "NULL") diff --git a/tests/test_mirror.py b/tests/test_mirror.py index 472d33fef2..f74ff6fa0d 100644 --- a/tests/test_mirror.py +++ b/tests/test_mirror.py @@ -103,6 +103,9 @@ def get_mirror_session_state(self, name): assert len(fvs) > 0 return { fv[0]: fv[1] for fv in fvs } + def check_syslog(self, dvs, marker, log, expected_cnt): + (ec, out) = dvs.runcmd(['sh', '-c', "awk \'/%s/,ENDFILE {print;}\' /var/log/syslog | grep \'%s\' | wc -l" % (marker, log)]) + assert out.strip() == str(expected_cnt) def test_MirrorAddRemove(self, dvs, testlog): """ @@ -120,9 +123,11 @@ def test_MirrorAddRemove(self, dvs, testlog): session = "TEST_SESSION" + marker = dvs.add_log_marker() # create mirror session self.create_mirror_session(session, "1.1.1.1", "2.2.2.2", "0x6558", "8", "100", "0") assert self.get_mirror_session_state(session)["status"] == "inactive" + self.check_syslog(dvs, marker, "Attached next hop observer .* for destination IP 2.2.2.2", 1) # bring up Ethernet16 self.set_interface_status(dvs, "Ethernet16", "up") @@ -193,8 +198,10 @@ def test_MirrorAddRemove(self, dvs, testlog): self.set_interface_status(dvs, "Ethernet16", "down") assert self.get_mirror_session_state(session)["status"] == "inactive" + marker = dvs.add_log_marker() # remove mirror session self.remove_mirror_session(session) + self.check_syslog(dvs, marker, "Detached next hop observer for destination IP 2.2.2.2", 1) def create_vlan(self, dvs, vlan): #dvs.runcmd("ip link del Bridge") @@ -251,9 +258,11 @@ def test_MirrorToVlanAddRemove(self, dvs, testlog): session = "TEST_SESSION" + marker = dvs.add_log_marker() # create mirror session self.create_mirror_session(session, "5.5.5.5", "6.6.6.6", "0x6558", "8", "100", "0") assert self.get_mirror_session_state(session)["status"] == "inactive" + self.check_syslog(dvs, marker, "Attached next hop observer .* for destination IP 6.6.6.6", 1) # create vlan; create vlan member self.create_vlan(dvs, "6") @@ -339,8 +348,10 @@ def test_MirrorToVlanAddRemove(self, dvs, testlog): self.remove_vlan_member("6", "Ethernet4") self.remove_vlan("6") + marker = dvs.add_log_marker() # remove mirror session self.remove_mirror_session(session) + self.check_syslog(dvs, marker, "Detached next hop observer for destination IP 6.6.6.6", 1) def create_port_channel(self, dvs, channel): tbl = swsscommon.ProducerStateTable(self.pdb, "LAG_TABLE") @@ -388,9 +399,11 @@ def test_MirrorToLagAddRemove(self, dvs, testlog): session = "TEST_SESSION" + marker = dvs.add_log_marker() # create mirror session self.create_mirror_session(session, "10.10.10.10", "11.11.11.11", "0x6558", "8", "100", "0") assert self.get_mirror_session_state(session)["status"] == "inactive" + self.check_syslog(dvs, marker, "Attached next hop observer .* for destination IP 11.11.11.11", 1) # create port channel; create port channel member self.create_port_channel(dvs, "008") @@ -436,8 +449,10 @@ def test_MirrorToLagAddRemove(self, dvs, testlog): self.remove_port_channel_member("008", "Ethernet88") self.remove_port_channel(dvs, "008") + marker = dvs.add_log_marker() # remove mirror session self.remove_mirror_session(session) + self.check_syslog(dvs, marker, "Detached next hop observer for destination IP 11.11.11.11", 1) # Ignore testcase in Debian Jessie @@ -790,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" @@ -805,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"]) @@ -822,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 @@ -873,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 diff --git a/tests/test_pg_drop_counter.py b/tests/test_pg_drop_counter.py index dedee82c1a..1cdd834747 100644 --- a/tests/test_pg_drop_counter.py +++ b/tests/test_pg_drop_counter.py @@ -65,12 +65,14 @@ def set_up_flex_counter(self): fc_status_enable = {"FLEX_COUNTER_STATUS": "enable"} self.config_db.create_entry("FLEX_COUNTER_TABLE", "PG_DROP", fc_status_enable) + self.config_db.create_entry("FLEX_COUNTER_TABLE", "PG_WATERMARK", fc_status_enable) def clear_flex_counter(self): for pg in self.pgs: self.flex_db.delete_entry("FLEX_COUNTER_TABLE", "PG_DROP_STAT_COUNTER:{}".format(pg)) self.config_db.delete_entry("FLEX_COUNTER_TABLE", "PG_DROP") + self.config_db.delete_entry("FLEX_COUNTER_TABLE", "PG_WATERMARK") def test_pg_drop_counters(self, dvs):