diff --git a/.azure-pipelines/build-template.yml b/.azure-pipelines/build-template.yml index e75ffdad8d..632bdb3107 100644 --- a/.azure-pipelines/build-template.yml +++ b/.azure-pipelines/build-template.yml @@ -122,9 +122,9 @@ jobs: pipeline: ${{ parameters.buildimage_pipeline }} artifact: 'sonic-buildimage.marvell-armhf1' runVersion: specific - runId: 63911 + runId: 80637 path: '$(Build.SourcesDirectory)/${{ parameters.buildimage_artifact_name }}' - displayName: "Download sonic buildimage deb packages from 63911" + displayName: "Download sonic buildimage deb packages from 80637" - script: | cd $(Build.SourcesDirectory)/${{ parameters.buildimage_artifact_name }} sudo dpkg -i target/debs/buster/libnl-3-200_*.deb diff --git a/.azure-pipelines/gcov.yml b/.azure-pipelines/gcov.yml index 0940f82cce..1bee32a03e 100644 --- a/.azure-pipelines/gcov.yml +++ b/.azure-pipelines/gcov.yml @@ -47,9 +47,8 @@ jobs: vmImage: 'ubuntu-20.04' variables: - DIFF_COVER_CHECK_THRESHOLD: 0 + DIFF_COVER_CHECK_THRESHOLD: 50 DIFF_COVER_ENABLE: 'true' - DIFF_COVER_WORKING_DIRECTORY: $(System.DefaultWorkingDirectory)/gcov/ container: image: sonicdev-microsoft.azurecr.io:443/${{ parameters.sonic_slave }}:latest @@ -106,8 +105,7 @@ jobs: sudo ./gcov_support.sh generate sudo ./gcov_support.sh merge_container_info $(Build.ArtifactStagingDirectory) sudo cp -rf gcov_output $(Build.ArtifactStagingDirectory) - mkdir -p $(System.DefaultWorkingDirectory)/gcov - sudo cp -rf $(Build.ArtifactStagingDirectory)/gcov_output/AllMergeReport/* $(System.DefaultWorkingDirectory)/gcov/ + sudo cp -rf $(Build.ArtifactStagingDirectory)/gcov_output/AllMergeReport/coverage.xml $(System.DefaultWorkingDirectory)/ ls -lh $(Build.ArtifactStagingDirectory) popd workingDirectory: $(Pipeline.Workspace) diff --git a/.azure-pipelines/test-docker-sonic-vs-template.yml b/.azure-pipelines/test-docker-sonic-vs-template.yml index 002b7749e1..7b1b3c4163 100644 --- a/.azure-pipelines/test-docker-sonic-vs-template.yml +++ b/.azure-pipelines/test-docker-sonic-vs-template.yml @@ -1,7 +1,7 @@ parameters: - name: timeout type: number - default: 360 + default: 480 - name: log_artifact_name type: string diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 38d8187f49..46732aa050 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -9,3 +9,22 @@ # @prsunny will be requested for review when someone opens # a pull request. * @prsunny + +# MACSEC +/gearsyncd/ @Pterosaur +/orchagent/macsecorch* @Pterosaur + +# LAG +/teamsyncd/ @judyjoseph +/tlm_teamd/ @judyjoseph +/cfgmgr/teammgr* @judyjoseph + +# Buffer Management, PFC +/orchagent/bufferorch* @neethajohn +/orchagent/qosorch* @neethajohn +/orchagent/pfc* @neethajohn +/cfgmgr/buffer* @neethajohn + +# Chassis +/orchagent/fabricportsorch* @abdosi @judyjoseph +/tests/test_virtual_chassis.py @abdosi @judyjoseph diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index e11ec4635a..64a57a6e58 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -24,69 +24,69 @@ DBGFLAGS = -g endif vlanmgrd_SOURCES = vlanmgrd.cpp vlanmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -vlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vlanmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +vlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +vlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +vlanmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) teammgrd_SOURCES = teammgrd.cpp teammgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -teammgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -teammgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -teammgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +teammgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +teammgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +teammgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) portmgrd_SOURCES = portmgrd.cpp portmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -portmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -portmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -portmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +portmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +portmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +portmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) intfmgrd_SOURCES = intfmgrd.cpp intfmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/lib/subintf.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -intfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -intfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -intfmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +intfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +intfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +intfmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) buffermgrd_SOURCES = buffermgrd.cpp buffermgr.cpp buffermgrdyn.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -buffermgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -buffermgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -buffermgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +buffermgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +buffermgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +buffermgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) vrfmgrd_SOURCES = vrfmgrd.cpp vrfmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -vrfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vrfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vrfmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +vrfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +vrfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +vrfmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) nbrmgrd_SOURCES = nbrmgrd.cpp nbrmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.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 = $(COMMON_LIBS) $(SAIMETA_LIBS) $(LIBNL_LIBS) +nbrmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(LIBNL_CFLAGS) $(CFLAGS_ASAN) +nbrmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(LIBNL_CPPFLAGS) $(CFLAGS_ASAN) +nbrmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) $(LIBNL_LIBS) vxlanmgrd_SOURCES = vxlanmgrd.cpp vxlanmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -vxlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vxlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -vxlanmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +vxlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +vxlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +vxlanmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) sflowmgrd_SOURCES = sflowmgrd.cpp sflowmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -sflowmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -sflowmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -sflowmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +sflowmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +sflowmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +sflowmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) natmgrd_SOURCES = natmgrd.cpp natmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -natmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -natmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -natmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +natmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +natmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +natmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) coppmgrd_SOURCES = coppmgrd.cpp coppmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -coppmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -coppmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -coppmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +coppmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +coppmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +coppmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) tunnelmgrd_SOURCES = tunnelmgrd.cpp tunnelmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -tunnelmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -tunnelmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -tunnelmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +tunnelmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +tunnelmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +tunnelmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) macsecmgrd_SOURCES = macsecmgrd.cpp macsecmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp $(top_srcdir)/orchagent/response_publisher.cpp shellcmd.h -macsecmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -macsecmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -macsecmgrd_LDADD = $(COMMON_LIBS) $(SAIMETA_LIBS) +macsecmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +macsecmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +macsecmgrd_LDADD = $(LDFLAGS_ASAN) $(COMMON_LIBS) $(SAIMETA_LIBS) if GCOV_ENABLED vlanmgrd_LDADD += -lgcovpreload @@ -104,3 +104,19 @@ tunnelmgrd_LDADD += -lgcovpreload macsecmgrd_LDADD += -lgcovpreload endif +if ASAN_ENABLED +vlanmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +teammgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +portmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +intfmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +buffermgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +vrfmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +nbrmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +vxlanmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +sflowmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +natmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +coppmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +tunnelmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +macsecmgrd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/cfgmgr/buffermgr.cpp b/cfgmgr/buffermgr.cpp index 0fb39a862a..5c7d6ae9e6 100644 --- a/cfgmgr/buffermgr.cpp +++ b/cfgmgr/buffermgr.cpp @@ -133,11 +133,11 @@ Create/update two tables: profile (in m_cfgBufferProfileTable) and port buffer ( } } */ -task_process_status BufferMgr::doSpeedUpdateTask(string port, bool admin_up) +task_process_status BufferMgr::doSpeedUpdateTask(string port) { - vector fvVectorPg, fvVectorProfile; string cable; string speed; + string pfc_enable; if (m_cableLenLookup.count(port) == 0) { @@ -152,32 +152,54 @@ task_process_status BufferMgr::doSpeedUpdateTask(string port, bool admin_up) return task_process_status::task_success; } - speed = m_speedLookup[port]; + if (m_portStatusLookup.count(port) == 0) + { + // admin_statue is not available yet. This can happen when notification of `PORT_QOS_MAP` table + // comes first. + SWSS_LOG_INFO("pfc_enable status is not available for port %s", port.c_str()); + return task_process_status::task_need_retry; + } + + if (m_portPfcStatus.count(port) == 0) + { + // PORT_QOS_MAP is not ready yet. The notification is cleared, and buffer pg + // will be handled when `pfc_enable` in `PORT_QOS_MAP` table is available + SWSS_LOG_INFO("pfc_enable status is not available for port %s", port.c_str()); + return task_process_status::task_success; + } + pfc_enable = m_portPfcStatus[port]; - string buffer_pg_key = port + m_cfgBufferPgTable.getTableNameSeparator() + LOSSLESS_PGS; + speed = m_speedLookup[port]; // key format is pg_lossless___profile string buffer_profile_key = "pg_lossless_" + speed + "_" + cable + "_profile"; string profile_ref = buffer_profile_key; + + vector lossless_pgs = tokenize(pfc_enable, ','); - m_cfgBufferPgTable.get(buffer_pg_key, fvVectorPg); - - if (!admin_up && m_platform == "mellanox") + if (m_portStatusLookup[port] == "down" && m_platform == "mellanox") { - // Remove the entry in BUFFER_PG table if any - if (!fvVectorPg.empty()) + for (auto lossless_pg : lossless_pgs) { - for (auto &prop : fvVectorPg) + // Remove the entry in BUFFER_PG table if any + vector fvVectorPg; + string buffer_pg_key = port + m_cfgBufferPgTable.getTableNameSeparator() + lossless_pg; + + m_cfgBufferPgTable.get(buffer_pg_key, fvVectorPg); + if (!fvVectorPg.empty()) { - if (fvField(prop) == "profile") + for (auto &prop : fvVectorPg) { - if (fvValue(prop) == profile_ref) + if (fvField(prop) == "profile") { - SWSS_LOG_NOTICE("Removing PG %s from port %s which is administrative down", buffer_pg_key.c_str(), port.c_str()); - m_cfgBufferPgTable.del(buffer_pg_key); - } - else - { - SWSS_LOG_NOTICE("Not default profile %s is configured on PG %s, won't reclaim buffer", fvValue(prop).c_str(), buffer_pg_key.c_str()); + if (fvValue(prop) == profile_ref) + { + SWSS_LOG_NOTICE("Removing PG %s from port %s which is administrative down", buffer_pg_key.c_str(), port.c_str()); + m_cfgBufferPgTable.del(buffer_pg_key); + } + else + { + SWSS_LOG_NOTICE("Not default profile %s is configured on PG %s, won't reclaim buffer", fvValue(prop).c_str(), buffer_pg_key.c_str()); + } } } } @@ -185,14 +207,15 @@ task_process_status BufferMgr::doSpeedUpdateTask(string port, bool admin_up) return task_process_status::task_success; } - + 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", - port.c_str(), speed.c_str(), cable.c_str()); - return task_process_status::task_invalid_entry; + SWSS_LOG_ERROR("Unable to create/update PG profile for port %s. No PG profile configured for speed %s and cable length %s", + port.c_str(), speed.c_str(), cable.c_str()); + return task_process_status::task_invalid_entry; } + vector fvVectorProfile; // check if profile already exists - if yes - skip creation m_cfgBufferProfileTable.get(buffer_profile_key, fvVectorProfile); // Create record in BUFFER_PROFILE table @@ -213,9 +236,10 @@ task_process_status BufferMgr::doSpeedUpdateTask(string port, bool admin_up) fvVectorProfile.push_back(make_pair("pool", INGRESS_LOSSLESS_PG_POOL_NAME)); fvVectorProfile.push_back(make_pair("xon", m_pgProfileLookup[speed][cable].xon)); - if (m_pgProfileLookup[speed][cable].xon_offset.length() > 0) { + if (m_pgProfileLookup[speed][cable].xon_offset.length() > 0) + { fvVectorProfile.push_back(make_pair("xon_offset", - m_pgProfileLookup[speed][cable].xon_offset)); + m_pgProfileLookup[speed][cable].xon_offset)); } fvVectorProfile.push_back(make_pair("xoff", m_pgProfileLookup[speed][cable].xoff)); fvVectorProfile.push_back(make_pair("size", m_pgProfileLookup[speed][cable].size)); @@ -227,20 +251,28 @@ task_process_status BufferMgr::doSpeedUpdateTask(string port, bool admin_up) SWSS_LOG_NOTICE("Reusing existing profile '%s'", buffer_profile_key.c_str()); } - /* Check if PG Mapping is already then log message and return. */ - for (auto& prop : fvVectorPg) + for (auto lossless_pg : lossless_pgs) { - if ((fvField(prop) == "profile") && (profile_ref == fvValue(prop))) + vector fvVectorPg; + string buffer_pg_key = port + m_cfgBufferPgTable.getTableNameSeparator() + lossless_pg; + + m_cfgBufferPgTable.get(buffer_pg_key, fvVectorPg); + + /* Check if PG Mapping is already then log message and return. */ + for (auto& prop : fvVectorPg) { - SWSS_LOG_NOTICE("PG to Buffer Profile Mapping %s already present", buffer_pg_key.c_str()); - return task_process_status::task_success; + if ((fvField(prop) == "profile") && (profile_ref == fvValue(prop))) + { + SWSS_LOG_NOTICE("PG to Buffer Profile Mapping %s already present", buffer_pg_key.c_str()); + continue; + } } - } - fvVectorPg.clear(); + fvVectorPg.clear(); - fvVectorPg.push_back(make_pair("profile", profile_ref)); - m_cfgBufferPgTable.set(buffer_pg_key, fvVectorPg); + fvVectorPg.push_back(make_pair("profile", profile_ref)); + m_cfgBufferPgTable.set(buffer_pg_key, fvVectorPg); + } return task_process_status::task_success; } @@ -346,6 +378,47 @@ void BufferMgr::doBufferMetaTask(Consumer &consumer) } } +/* +Parse PORT_QOS_MAP to retrieve on which queue PFC is enable, and +cached in a map +*/ +void BufferMgr::doPortQosTableTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple tuple = it->second; + string port_name = kfvKey(tuple); + string op = kfvOp(tuple); + if (op == SET_COMMAND) + { + bool update_pfc_enable = false; + for (auto itp : kfvFieldsValues(tuple)) + { + if (fvField(itp) == "pfc_enable") + { + if (m_portPfcStatus.count(port_name) == 0 || m_portPfcStatus[port_name] != fvValue(itp)) + { + m_portPfcStatus[port_name] = fvValue(itp); + update_pfc_enable = true; + } + SWSS_LOG_INFO("Got pfc enable status for port %s status %s", port_name.c_str(), fvValue(itp).c_str()); + break; + } + } + if (update_pfc_enable) + { + // The return status is ignored + doSpeedUpdateTask(port_name); + } + } + it = consumer.m_toSync.erase(it); + } + +} + void BufferMgr::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -399,6 +472,12 @@ void BufferMgr::doTask(Consumer &consumer) return; } + if (table_name == CFG_PORT_QOS_MAP_TABLE_NAME) + { + doPortQosTableTask(consumer); + return; + } + auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { @@ -422,7 +501,6 @@ void BufferMgr::doTask(Consumer &consumer) } else if (m_pgfile_processed && table_name == CFG_PORT_TABLE_NAME) { - bool admin_up = false; for (auto i : kfvFieldsValues(t)) { if (fvField(i) == "speed") @@ -431,39 +509,34 @@ void BufferMgr::doTask(Consumer &consumer) } if (fvField(i) == "admin_status") { - admin_up = ("up" == fvValue(i)); + m_portStatusLookup[port] = fvValue(i); } } if (m_speedLookup.count(port) != 0) { // create/update profile for port - task_status = doSpeedUpdateTask(port, admin_up); + task_status = doSpeedUpdateTask(port); } + } - if (task_status != task_process_status::task_success) - { + switch (task_status) + { + case task_process_status::task_failed: + SWSS_LOG_ERROR("Failed to process table update"); + return; + case task_process_status::task_need_retry: + SWSS_LOG_INFO("Unable to process table update. Will retry..."); + ++it; + break; + case task_process_status::task_invalid_entry: + SWSS_LOG_ERROR("Failed to process invalid entry, drop it"); + it = consumer.m_toSync.erase(it); + break; + default: + it = consumer.m_toSync.erase(it); break; - } } } - - switch (task_status) - { - case task_process_status::task_failed: - SWSS_LOG_ERROR("Failed to process table update"); - return; - case task_process_status::task_need_retry: - SWSS_LOG_INFO("Unable to process table update. Will retry..."); - ++it; - break; - case task_process_status::task_invalid_entry: - SWSS_LOG_ERROR("Failed to process invalid entry, drop it"); - it = consumer.m_toSync.erase(it); - break; - default: - it = consumer.m_toSync.erase(it); - break; - } } } diff --git a/cfgmgr/buffermgr.h b/cfgmgr/buffermgr.h index e7f04465ef..b9b3f2c496 100644 --- a/cfgmgr/buffermgr.h +++ b/cfgmgr/buffermgr.h @@ -11,7 +11,6 @@ namespace swss { #define INGRESS_LOSSLESS_PG_POOL_NAME "ingress_lossless_pool" -#define LOSSLESS_PGS "3-4" #define BUFFERMGR_TIMER_PERIOD 10 @@ -28,6 +27,8 @@ typedef std::map pg_profile_lookup_t; typedef std::map port_cable_length_t; typedef std::map port_speed_t; +typedef std::map port_pfc_status_t; +typedef std::map port_admin_status_t; class BufferMgr : public Orch { @@ -56,17 +57,22 @@ class BufferMgr : public Orch pg_profile_lookup_t m_pgProfileLookup; port_cable_length_t m_cableLenLookup; + port_admin_status_t m_portStatusLookup; 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, bool admin_up); + task_process_status doSpeedUpdateTask(std::string port); void doBufferTableTask(Consumer &consumer, ProducerStateTable &applTable); void transformSeperator(std::string &name); void doTask(Consumer &consumer); void doBufferMetaTask(Consumer &consumer); + + port_pfc_status_t m_portPfcStatus; + void doPortQosTableTask(Consumer &consumer); + }; } diff --git a/cfgmgr/buffermgrd.cpp b/cfgmgr/buffermgrd.cpp index 05932a9e3c..eb5de60b65 100644 --- a/cfgmgr/buffermgrd.cpp +++ b/cfgmgr/buffermgrd.cpp @@ -215,7 +215,8 @@ int main(int argc, char **argv) CFG_BUFFER_QUEUE_TABLE_NAME, CFG_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, CFG_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME, - CFG_DEVICE_METADATA_TABLE_NAME + CFG_DEVICE_METADATA_TABLE_NAME, + CFG_PORT_QOS_MAP_TABLE_NAME }; cfgOrchList.emplace_back(new BufferMgr(&cfgDb, &applDb, pg_lookup_file, cfg_buffer_tables)); } diff --git a/cfgmgr/buffermgrdyn.cpp b/cfgmgr/buffermgrdyn.cpp index b4578c2370..b3ce88c6f3 100644 --- a/cfgmgr/buffermgrdyn.cpp +++ b/cfgmgr/buffermgrdyn.cpp @@ -29,9 +29,9 @@ using namespace swss; BufferMgrDynamic::BufferMgrDynamic(DBConnector *cfgDb, DBConnector *stateDb, DBConnector *applDb, const vector &tables, shared_ptr> gearboxInfo, shared_ptr> zeroProfilesInfo) : Orch(tables), m_platform(), - m_bufferDirections({BUFFER_INGRESS, BUFFER_EGRESS}), - m_bufferObjectNames({"priority group", "queue"}), - m_bufferDirectionNames({"ingress", "egress"}), + m_bufferDirections{BUFFER_INGRESS, BUFFER_EGRESS}, + m_bufferObjectNames{"priority group", "queue"}, + m_bufferDirectionNames{"ingress", "egress"}, m_applDb(applDb), m_zeroProfilesLoaded(false), m_supportRemoving(true), @@ -39,8 +39,8 @@ BufferMgrDynamic::BufferMgrDynamic(DBConnector *cfgDb, DBConnector *stateDb, DBC m_cfgDeviceMetaDataTable(cfgDb, CFG_DEVICE_METADATA_TABLE_NAME), m_applBufferPoolTable(applDb, APP_BUFFER_POOL_TABLE_NAME), m_applBufferProfileTable(applDb, APP_BUFFER_PROFILE_TABLE_NAME), - m_applBufferObjectTables({ProducerStateTable(applDb, APP_BUFFER_PG_TABLE_NAME), ProducerStateTable(applDb, APP_BUFFER_QUEUE_TABLE_NAME)}), - m_applBufferProfileListTables({ProducerStateTable(applDb, APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME), ProducerStateTable(applDb, APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME)}), + m_applBufferObjectTables{ProducerStateTable(applDb, APP_BUFFER_PG_TABLE_NAME), ProducerStateTable(applDb, APP_BUFFER_QUEUE_TABLE_NAME)}, + m_applBufferProfileListTables{ProducerStateTable(applDb, APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME), ProducerStateTable(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), @@ -2853,7 +2853,7 @@ task_process_status BufferMgrDynamic::handleSingleBufferPgEntry(const string &ke // For del command: // 1. Removing it from APPL_DB // 2. Update internal caches - string &runningProfileName = bufferPg.running_profile_name; + string runningProfileName = bufferPg.running_profile_name; string &configProfileName = bufferPg.configured_profile_name; if (!m_supportRemoving) diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 93281dbcd9..91ed762ef9 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -115,7 +115,21 @@ void IntfMgr::setIntfIp(const string &alias, const string &opCmd, int ret = swss::exec(cmd.str(), res); if (ret) { - SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + if (!ipPrefix.isV4() && opCmd == "add") + { + SWSS_LOG_NOTICE("Failed to assign IPv6 on interface %s with return code %d, trying to enable IPv6 and retry", alias.c_str(), ret); + if (!enableIpv6Flag(alias)) + { + SWSS_LOG_ERROR("Failed to enable IPv6 on interface %s", alias.c_str()); + return; + } + ret = swss::exec(cmd.str(), res); + } + + if (ret) + { + SWSS_LOG_ERROR("Command '%s' failed with rc %d", cmd.str().c_str(), ret); + } } } @@ -1139,3 +1153,13 @@ void IntfMgr::doPortTableTask(const string& key, vector data, s } } } + +bool IntfMgr::enableIpv6Flag(const string &alias) +{ + stringstream cmd; + string temp_res; + cmd << "sysctl -w net.ipv6.conf." << shellquote(alias) << ".disable_ipv6=0"; + int ret = swss::exec(cmd.str(), temp_res); + SWSS_LOG_INFO("disable_ipv6 flag is set to 0 for iface: %s, cmd: %s, ret: %d", alias.c_str(), cmd.str().c_str(), ret); + return (ret == 0) ? true : false; +} diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index 683e208c0e..65fd051200 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -75,6 +75,7 @@ class IntfMgr : public Orch void updateSubIntfAdminStatus(const std::string &alias, const std::string &admin); void updateSubIntfMtu(const std::string &alias, const std::string &mtu); + bool enableIpv6Flag(const std::string&); bool m_replayDone {false}; }; diff --git a/cfgmgr/macsecmgr.cpp b/cfgmgr/macsecmgr.cpp index 39cf9eae02..0edb86a5af 100644 --- a/cfgmgr/macsecmgr.cpp +++ b/cfgmgr/macsecmgr.cpp @@ -148,7 +148,7 @@ static void wpa_cli_commands( } if (!network_id.empty()) { - wpa_cli_commands(ostream, "set_network", port_name); + wpa_cli_commands(ostream, "set_network", network_id); } wpa_cli_commands(ostream, args...); } diff --git a/cfgmgr/natmgrd.cpp b/cfgmgr/natmgrd.cpp index c2baf7eb87..db5a77f9a6 100644 --- a/cfgmgr/natmgrd.cpp +++ b/cfgmgr/natmgrd.cpp @@ -62,15 +62,30 @@ NatMgr *natmgr = NULL; NotificationConsumer *timeoutNotificationsConsumer = NULL; NotificationConsumer *flushNotificationsConsumer = NULL; +static volatile sig_atomic_t gExit = 0; + std::shared_ptr cleanupNotifier; +static struct sigaction old_sigaction; + void sigterm_handler(int signo) +{ + SWSS_LOG_ENTER(); + + if (old_sigaction.sa_handler != SIG_IGN && old_sigaction.sa_handler != SIG_DFL) { + old_sigaction.sa_handler(signo); + } + + gExit = 1; +} + +void cleanup() { int ret = 0; std::string res; const std::string conntrackFlush = "conntrack -F"; - SWSS_LOG_NOTICE("Got SIGTERM"); + SWSS_LOG_ENTER(); /*If there are any conntrack entries, clean them */ ret = swss::exec(conntrackFlush, res); @@ -129,10 +144,12 @@ int main(int argc, char **argv) cleanupNotifier = std::make_shared(&appDb, "NAT_DB_CLEANUP_NOTIFICATION"); - if (signal(SIGTERM, sigterm_handler) == SIG_ERR) + struct sigaction sigact = {}; + sigact.sa_handler = sigterm_handler; + if (sigaction(SIGTERM, &sigact, &old_sigaction)) { SWSS_LOG_ERROR("failed to setup SIGTERM action handler"); - exit(1); + exit(EXIT_FAILURE); } natmgr = new NatMgr(&cfgDb, &appDb, &stateDb, cfg_tables); @@ -154,7 +171,7 @@ int main(int argc, char **argv) s.addSelectable(flushNotificationsConsumer); SWSS_LOG_NOTICE("starting main loop"); - while (true) + while (!gExit) { Selectable *sel; int ret; @@ -197,10 +214,14 @@ int main(int argc, char **argv) auto *c = (Executor *)sel; c->execute(); } + + cleanup(); } catch(const std::exception &e) { SWSS_LOG_ERROR("Runtime error: %s", e.what()); + return EXIT_FAILURE; } - return -1; + + return 0; } diff --git a/cfgmgr/teammgrd.cpp b/cfgmgr/teammgrd.cpp index 66bfa4b6d2..ff4151c921 100644 --- a/cfgmgr/teammgrd.cpp +++ b/cfgmgr/teammgrd.cpp @@ -23,9 +23,16 @@ ofstream gResponsePublisherRecordOfs; string gResponsePublisherRecordFile; bool received_sigterm = false; +static struct sigaction old_sigaction; void sig_handler(int signo) { + SWSS_LOG_ENTER(); + + if (old_sigaction.sa_handler != SIG_IGN && old_sigaction.sa_handler != SIG_DFL) { + old_sigaction.sa_handler(signo); + } + received_sigterm = true; return; } @@ -38,7 +45,13 @@ int main(int argc, char **argv) SWSS_LOG_NOTICE("--- Starting teammrgd ---"); /* Register the signal handler for SIGTERM */ - signal(SIGTERM, sig_handler); + struct sigaction sigact = {}; + sigact.sa_handler = sig_handler; + if (sigaction(SIGTERM, &sigact, &old_sigaction)) + { + SWSS_LOG_ERROR("failed to setup SIGTERM action handler"); + exit(EXIT_FAILURE); + } try { diff --git a/cfgmgr/tunnelmgr.cpp b/cfgmgr/tunnelmgr.cpp index 7f4dc4dd3d..a81438470f 100644 --- a/cfgmgr/tunnelmgr.cpp +++ b/cfgmgr/tunnelmgr.cpp @@ -9,6 +9,7 @@ #include "tokenize.h" #include "shellcmd.h" #include "exec.h" +#include "warm_restart.h" using namespace std; using namespace swss; @@ -107,7 +108,8 @@ static int cmdIpTunnelRouteDel(const std::string& pfx, std::string & res) TunnelMgr::TunnelMgr(DBConnector *cfgDb, DBConnector *appDb, const std::vector &tableNames) : Orch(cfgDb, tableNames), m_appIpInIpTunnelTable(appDb, APP_TUNNEL_DECAP_TABLE_NAME), - m_cfgPeerTable(cfgDb, CFG_PEER_SWITCH_TABLE_NAME) + m_cfgPeerTable(cfgDb, CFG_PEER_SWITCH_TABLE_NAME), + m_cfgTunnelTable(cfgDb, CFG_TUNNEL_TABLE_NAME) { std::vector peer_keys; m_cfgPeerTable.getKeys(peer_keys); @@ -126,6 +128,23 @@ TunnelMgr::TunnelMgr(DBConnector *cfgDb, DBConnector *appDb, const std::vector tunnel_keys; + m_cfgTunnelTable.getKeys(tunnel_keys); + + for (auto tunnel: tunnel_keys) + { + m_tunnelReplay.insert(tunnel); + } + if (m_tunnelReplay.empty()) + { + finalizeWarmReboot(); + } + + } + auto consumerStateTable = new swss::ConsumerStateTable(appDb, APP_TUNNEL_ROUTE_TABLE_NAME, TableConsumable::DEFAULT_POP_BATCH_SIZE, default_orch_pri); @@ -191,6 +210,11 @@ void TunnelMgr::doTask(Consumer &consumer) ++it; } } + + if (!replayDone && m_tunnelReplay.empty() && WarmStart::isWarmStart()) + { + finalizeWarmReboot(); + } } bool TunnelMgr::doTunnelTask(const KeyOpFieldsValuesTuple & t) @@ -230,8 +254,16 @@ bool TunnelMgr::doTunnelTask(const KeyOpFieldsValuesTuple & t) SWSS_LOG_NOTICE("Peer/Remote IP not configured"); } - m_appIpInIpTunnelTable.set(tunnelName, kfvFieldsValues(t)); + /* If the tunnel is already in hardware (i.e. present in the replay), + * don't try to create it again since it will cause an OA crash + * (warmboot case) + */ + if (m_tunnelReplay.find(tunnelName) == m_tunnelReplay.end()) + { + m_appIpInIpTunnelTable.set(tunnelName, kfvFieldsValues(t)); + } } + m_tunnelReplay.erase(tunnelName); m_tunnelCache[tunnelName] = tunInfo; } else @@ -356,3 +388,13 @@ bool TunnelMgr::configIpTunnel(const TunnelInfo& tunInfo) return true; } + + +void TunnelMgr::finalizeWarmReboot() +{ + replayDone = true; + WarmStart::setWarmStartState("tunnelmgrd", WarmStart::REPLAYED); + SWSS_LOG_NOTICE("tunnelmgrd warmstart state set to REPLAYED"); + WarmStart::setWarmStartState("tunnelmgrd", WarmStart::RECONCILED); + SWSS_LOG_NOTICE("tunnelmgrd warmstart state set to RECONCILED"); +} diff --git a/cfgmgr/tunnelmgr.h b/cfgmgr/tunnelmgr.h index e2b601abe9..53d2f27278 100644 --- a/cfgmgr/tunnelmgr.h +++ b/cfgmgr/tunnelmgr.h @@ -4,6 +4,8 @@ #include "producerstatetable.h" #include "orch.h" +#include + namespace swss { struct TunnelInfo @@ -28,12 +30,18 @@ class TunnelMgr : public Orch bool configIpTunnel(const TunnelInfo& info); + void finalizeWarmReboot(); + ProducerStateTable m_appIpInIpTunnelTable; Table m_cfgPeerTable; + Table m_cfgTunnelTable; std::map m_tunnelCache; std::map m_intfCache; std::string m_peerIp; + + std::set m_tunnelReplay; + bool replayDone = false; }; } diff --git a/cfgmgr/tunnelmgrd.cpp b/cfgmgr/tunnelmgrd.cpp index 0165eb94b5..0a6a84eaeb 100644 --- a/cfgmgr/tunnelmgrd.cpp +++ b/cfgmgr/tunnelmgrd.cpp @@ -11,6 +11,7 @@ #include "exec.h" #include "schema.h" #include "tunnelmgr.h" +#include "warm_restart.h" using namespace std; using namespace swss; @@ -54,6 +55,9 @@ int main(int argc, char **argv) DBConnector cfgDb("CONFIG_DB", 0); DBConnector appDb("APPL_DB", 0); + WarmStart::initialize("tunnelmgrd", "swss"); + WarmStart::checkWarmStart("tunnelmgrd", "swss"); + TunnelMgr tunnelmgr(&cfgDb, &appDb, cfgTunTables); std::vector cfgOrchList = {&tunnelmgr}; diff --git a/cfgmgr/vlanmgr.cpp b/cfgmgr/vlanmgr.cpp index aa02377819..1536349cb0 100644 --- a/cfgmgr/vlanmgr.cpp +++ b/cfgmgr/vlanmgr.cpp @@ -330,10 +330,10 @@ void VlanMgr::doVlanTask(Consumer &consumer) */ if (isVlanStateOk(key) && m_vlans.find(key) == m_vlans.end()) { + SWSS_LOG_DEBUG("%s already created", kfvKey(t).c_str()); m_vlans.insert(key); m_vlanReplay.erase(kfvKey(t)); it = consumer.m_toSync.erase(it); - SWSS_LOG_DEBUG("%s already created", kfvKey(t).c_str()); continue; } diff --git a/cfgmgr/vxlanmgr.cpp b/cfgmgr/vxlanmgr.cpp index e45c593803..4d41819053 100644 --- a/cfgmgr/vxlanmgr.cpp +++ b/cfgmgr/vxlanmgr.cpp @@ -392,8 +392,8 @@ bool VxlanMgr::doVxlanDeleteTask(const KeyOpFieldsValuesTuple & t) SWSS_LOG_WARN("Vxlan %s hasn't been created ", info.m_vxlan.c_str()); } - m_vnetCache.erase(it); SWSS_LOG_INFO("Delete vxlan %s", info.m_vxlan.c_str()); + m_vnetCache.erase(it); return true; } @@ -643,6 +643,7 @@ bool VxlanMgr::doVxlanTunnelMapDeleteTask(const KeyOpFieldsValuesTuple & t) vxlan_dev_name = map_entry.vxlan_dev_name; vlan = map_entry.vlan; vni_id = map_entry.vni_id; + downVxlanNetdevice(vxlan_dev_name); deleteVxlanNetdevice(vxlan_dev_name); m_vxlanTunnelMapCache.erase(vxlanTunnelMapName); @@ -906,11 +907,11 @@ void VxlanMgr::createAppDBTunnelMapTable(const KeyOpFieldsValuesTuple & t) std::replace(vxlanTunnelMapName.begin(), vxlanTunnelMapName.end(), config_db_key_delimiter, delimiter); /* Case 1: Entry exist - Erase from cache & return - * Case 2: Entry does not exist - Write to AppDB + * Case 2: Entry does not exist - Write to AppDB * Case 3: Entry exist but modified - Not taken care. Will address later */ if (m_in_reconcile) - { + { auto it = find(m_appVxlanTunnelMapKeysRecon.begin(), m_appVxlanTunnelMapKeysRecon.end(), vxlanTunnelMapName); if (it != m_appVxlanTunnelMapKeysRecon.end()) { @@ -939,28 +940,28 @@ void VxlanMgr::delAppDBTunnelMapTable(std::string vxlanTunnelMapName) m_appVxlanTunnelMapTable.del(vxlanTunnelMapName); } -int VxlanMgr::createVxlanNetdevice(std::string vxlanTunnelName, std::string vni_id, - std::string src_ip, std::string dst_ip, +int VxlanMgr::createVxlanNetdevice(std::string vxlanTunnelName, std::string vni_id, + std::string src_ip, std::string dst_ip, std::string vlan_id) { std::string res, cmds; - std::string link_add_cmd, link_set_master_cmd, link_up_cmd; + std::string link_add_cmd, link_set_master_cmd, link_up_cmd; std::string bridge_add_cmd, bridge_untagged_add_cmd, bridge_del_vid_cmd; std::string vxlan_dev_name; - vxlan_dev_name = std::string("") + std::string(vxlanTunnelName) + "-" + + vxlan_dev_name = std::string("") + std::string(vxlanTunnelName) + "-" + std::string(vlan_id); SWSS_LOG_INFO("Kernel tnl_name: %s vni_id: %s src_ip: %s dst_ip:%s vlan_id: %s", - vxlanTunnelName.c_str(), vni_id.c_str(), src_ip.c_str(), dst_ip.c_str(), + vxlanTunnelName.c_str(), vni_id.c_str(), src_ip.c_str(), dst_ip.c_str(), vlan_id.c_str()); // Case 1: Entry exist - Erase from cache & return // Case 2: Entry does not exist - Create netDevice in Kernel // Case 3: Entry exist but modified - Not taken care. Will address later - + if (m_in_reconcile) - { + { auto it = m_vxlanNetDevices.find(vxlan_dev_name); if (it != m_vxlanNetDevices.end()) { @@ -1024,6 +1025,15 @@ int VxlanMgr::createVxlanNetdevice(std::string vxlanTunnelName, std::string vni_ return swss::exec(cmds,res); } +int VxlanMgr::downVxlanNetdevice(std::string vxlan_dev_name) +{ + int ret = 0; + std::string res; + const std::string cmd = std::string("") + IP_CMD + " link set dev " + vxlan_dev_name + " down"; + exec(cmd, res); + return ret; +} + int VxlanMgr::deleteVxlanNetdevice(std::string vxlan_dev_name) { std::string res; @@ -1031,29 +1041,59 @@ int VxlanMgr::deleteVxlanNetdevice(std::string vxlan_dev_name) return swss::exec(cmd, res); } +std::vector VxlanMgr::parseNetDev(const string& stdout){ + std::vector netdevs; + std::regex device_name_pattern("^\\d+:\\s+([^:]+)"); + std::smatch match_result; + auto lines = tokenize(stdout, '\n'); + for (const std::string & line : lines) + { + SWSS_LOG_DEBUG("line : %s\n",line.c_str()); + if (!std::regex_search(line, match_result, device_name_pattern)) + { + continue; + } + std::string dev_name = match_result[1]; + netdevs.push_back(dev_name); + } + return netdevs; +} + void VxlanMgr::getAllVxlanNetDevices() { std::string stdout; - const std::string cmd = std::string("") + IP_CMD + " link show type vxlan"; + + // Get VxLan Netdev Interfaces + std::string cmd = std::string("") + IP_CMD + " link show type vxlan"; int ret = swss::exec(cmd, stdout); if (ret != 0) { - SWSS_LOG_ERROR("Cannot get devices by command : %s", cmd.c_str()); - return; + SWSS_LOG_ERROR("Cannot get vxlan devices by command : %s", cmd.c_str()); + stdout.clear(); } - std::regex device_name_pattern("^\\d+:\\s+([^:]+)"); - std::smatch match_result; - auto lines = tokenize(stdout, '\n'); - for (const std::string & line : lines) + std::vector netdevs = parseNetDev(stdout); + for (auto netdev : netdevs) { - SWSS_LOG_INFO("line : %s\n",line.c_str()); - if (!std::regex_search(line, match_result, device_name_pattern)) + m_vxlanNetDevices[netdev] = VXLAN; + } + + // Get VxLanIf Netdev Interfaces + cmd = std::string("") + IP_CMD + " link show type bridge"; + ret = swss::exec(cmd, stdout); + if (ret != 0) + { + SWSS_LOG_ERROR("Cannot get vxlanIf devices by command : %s", cmd.c_str()); + stdout.clear(); + } + netdevs = parseNetDev(stdout); + for (auto netdev : netdevs) + { + if (netdev.find(VXLAN_IF_NAME_PREFIX) == 0) { - continue; + m_vxlanNetDevices[netdev] = VXLAN_IF; } - std::string vxlan_dev_name = match_result[1]; - m_vxlanNetDevices[vxlan_dev_name] = vxlan_dev_name; } + return; } @@ -1150,8 +1190,22 @@ void VxlanMgr::clearAllVxlanDevices() { for (auto it = m_vxlanNetDevices.begin(); it != m_vxlanNetDevices.end();) { - SWSS_LOG_INFO("Deleting Stale NetDevice vxlandevname %s\n", (it->first).c_str()); - deleteVxlanNetdevice(it->first); + std::string netdev_name = it->first; + std::string netdev_type = it->second; + SWSS_LOG_INFO("Deleting Stale NetDevice %s, type: %s\n", netdev_name.c_str(), netdev_type.c_str()); + VxlanInfo info; + std::string res; + if (netdev_type.compare(VXLAN)) + { + info.m_vxlan = netdev_name; + downVxlanNetdevice(netdev_name); + cmdDeleteVxlan(info, res); + } + else if(netdev_type.compare(VXLAN_IF)) + { + info.m_vxlanIf = netdev_name; + cmdDeleteVxlanIf(info, res); + } it = m_vxlanNetDevices.erase(it); } } diff --git a/cfgmgr/vxlanmgr.h b/cfgmgr/vxlanmgr.h index 1988e253ae..68d6250fe5 100644 --- a/cfgmgr/vxlanmgr.h +++ b/cfgmgr/vxlanmgr.h @@ -6,6 +6,7 @@ #include "orch.h" #include +#include #include #include #include @@ -69,7 +70,9 @@ class VxlanMgr : public Orch void delAppDBTunnelMapTable(std::string vxlanTunnelMapName); int createVxlanNetdevice(std::string vxlanTunnelName, std::string vni_id, std::string src_ip, std::string dst_ip, std::string vlan_id); + int downVxlanNetdevice(std::string vxlan_dev_name); int deleteVxlanNetdevice(std::string vxlan_dev_name); + std::vector parseNetDev(const std::string& stdout); void getAllVxlanNetDevices(); /* diff --git a/configure.ac b/configure.ac index 5e5ce44171..5efe0a67bd 100644 --- a/configure.ac +++ b/configure.ac @@ -121,6 +121,26 @@ fi AM_CONDITIONAL(GCOV_ENABLED, test x$enable_gcov = xyes) AC_MSG_RESULT($enable_gcov) +AC_ARG_ENABLE(asan, +[ --enable-asan Compile with address sanitizer], +[case "${enableval}" in + yes) asan_enabled=true ;; + no) asan_enabled=false ;; + *) AC_MSG_ERROR(bad value ${enableval} for --enable-asan) ;; +esac],[asan_enabled=false]) + +if test "x$asan_enabled" = "xtrue"; then + CFLAGS_ASAN+=" -fsanitize=address" + CFLAGS_ASAN+=" -DASAN_ENABLED" + CFLAGS_ASAN+=" -ggdb -fno-omit-frame-pointer -U_FORTIFY_SOURCE" + AC_SUBST(CFLAGS_ASAN) + + LDFLAGS_ASAN+=" -lasan" + AC_SUBST(LDFLAGS_ASAN) +fi + +AM_CONDITIONAL(ASAN_ENABLED, test x$asan_enabled = xtrue) + AC_SUBST(CFLAGS_COMMON) AC_CONFIG_FILES([ diff --git a/debian/rules b/debian/rules index a8a8b835fb..42e82b2f30 100755 --- a/debian/rules +++ b/debian/rules @@ -27,11 +27,18 @@ include /usr/share/dpkg/default.mk # dh_auto_configure -- \ # -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH) +configure_opts = +ifeq ($(ENABLE_ASAN), y) + configure_opts += --enable-asan +endif + ifeq ($(ENABLE_GCOV), y) -override_dh_auto_configure: - dh_auto_configure -- --enable-gcov + configure_opts += --enable-gcov CFLAGS="-g -O0" CXXFLAGS="-g -O0" endif +override_dh_auto_configure: + dh_auto_configure -- $(configure_opts) + override_dh_auto_install: dh_auto_install --destdir=debian/swss ifeq ($(ENABLE_GCOV), y) diff --git a/doc/Configuration.md b/doc/Configuration.md deleted file mode 100644 index d4494283fa..0000000000 --- a/doc/Configuration.md +++ /dev/null @@ -1,1472 +0,0 @@ -# SONiC Configuration Database Manual - -Table of Contents -================= - - * [Introduction](#introduction) - * [Configuration](#configuration) - * [Config Load and Save](#config-load-and-save) - * [Incremental Configuration](#incremental-configuration) - * [Redis and Json Schema](#redis-and-json-schema) - * [ACL and Mirroring](#acl-and-mirroring) - * [BGP Sessions](#bgp-sessions) - * [BUFFER_PG](#buffer_pg) - * [Buffer pool](#buffer-pool) - * [Buffer profile](#buffer-profile) - * [Buffer queue](#buffer-queue) - * [Buffer port ingress profile list](#buffer-port-ingress-profile-list) - * [Buffer port egress profile list](#buffer-port-egress-profile-list) - * [Cable length](#cable-length) - * [COPP_TABLE](#copp_table) - * [CRM](#crm) - * [Data Plane L3 Interfaces](#data-plane-l3-interfaces) - * [DEFAULT_LOSSLESS_BUFFER_PARAMETER](#DEFAULT_LOSSLESS_BUFFER_PARAMETER) - * [Device Metadata](#device-metadata) - * [Device neighbor metada](#device-neighbor-metada) - * [DSCP_TO_TC_MAP](#dscp_to_tc_map) - * [FLEX_COUNTER_TABLE](#flex_counter_table) - * [L2 Neighbors](#l2-neighbors) - * [Loopback Interface](#loopback-interface) - * [LOSSLESS_TRAFFIC_PATTERN](#LOSSLESS_TRAFFIC_PATTERN) - * [Management Interface](#management-interface) - * [Management port](#management-port) - * [Management VRF](#management-vrf) - * [MAP_PFC_PRIORITY_TO_QUEUE](#map_pfc_priority_to_queue) - * [NTP Global Configuration](#ntp-global-configuration) - * [NTP and SYSLOG servers](#ntp-and-syslog-servers) - * [Port](#port) - * [Port Channel](#port-channel) - * [Portchannel member](#portchannel-member) - * [Scheduler](#scheduler) - * [Port QoS Map](#port-qos-map) - * [Queue](#queue) - * [Tacplus Server](#tacplus-server) - * [TC to Priority group map](#tc-to-priority-group-map) - * [TC to Queue map](#tc-to-queue-map) - * [Versions](#versions) - * [VLAN](#vlan) - * [VLAN_MEMBER](#vlan_member) - * [Virtual router](#virtual-router) - * [WRED_PROFILE](#wred_profile) - * [For Developers](#for-developers) - * [Generating Application Config by Jinja2 Template](#generating-application-config-by-jinja2-template) - * [Incremental Configuration by Subscribing to ConfigDB](#incremental-configuration-by-subscribing-to-configdb) - - - -# Introduction -This document lists the configuration commands schema applied in the SONiC eco system. All these commands find relevance in collecting system information, analysis and even for trouble shooting. All the commands are categorized under relevant topics with corresponding examples. - -# Configuration - -SONiC is managing configuration in a single source of truth - a redisDB -instance that we refer as ConfigDB. Applications subscribe to ConfigDB -and generate their running configuration correspondingly. - -(Before Sep 2017, we were using an XML file named minigraph.xml to -configure SONiC devices. For historical documentation, please refer to -[Configuration with -Minigraph](https://github.com/Azure/SONiC/wiki/Configuration-with-Minigraph-(~Sep-2017))) - -# **Config Load and Save** - -In current version of SONiC, ConfigDB is implemented as database 4 of -local redis. When system boots, configurations will be loaded from -/etc/sonic/config_db.json file into redis. Please note that ConfigDB -content won't be written back into /etc/sonic/config_db.json file -automatically. In order to do that, a config save command need to be -manually executed from CLI. Similarly, config load will trigger a force -load of json file into DB. Generally, content in -/etc/sonic/config_db.json can be considered as starting config, and -content in redisDB running config. - -We keep a way to load configuration from minigraph and write into -ConfigDB for backward compatibility. To do that, run `config -load_minigraph`. - -### Incremental Configuration - -The design of ConfigDB supports incremental configuration - application -could subscribe to changes in ConfigDB and response correspondingly. -However, this feature is not implemented by all applications yet. By Sep -2017 now, the only application that supports incremental configuration -is BGP (docker-fpm-quagga). For other applications, a manual restart is -required after configuration changes in ConfigDB. - -# **Redis and Json Schema** - -ConfigDB uses a table-object schema that is similar with -[AppDB](https://github.com/Azure/sonic-swss/blob/4c56d23b9ff4940bdf576cf7c9e5aa77adcbbdcc/doc/swss-schema.md), -and `config_db.json` is a straight-forward serialization of DB. As an -example, the following fragments could be BGP-related configuration in -redis and json, correspondingly: - - -***Redis format*** -``` -127.0.0.1:6379[4]> keys BGP_NEIGHBOR:* - -1) "BGP_NEIGHBOR:10.0.0.31" -2) "BGP_NEIGHBOR:10.0.0.39" -3) "BGP_NEIGHBOR:10.0.0.11" -4) "BGP_NEIGHBOR:10.0.0.7" - -... - -127.0.0.1:6379[4]> hgetall BGP_NEIGHBOR:10.0.0.3 - -1) "admin_status" -2) "up" -3) "peer_addr" -4) "10.0.0.2" -5) "asn" -6) "65200" -7) "name" -8) "ARISTA07T2" -``` - -***Json format*** -``` -"BGP_NEIGHBOR": { - "10.0.0.57": { - "rrclient": "0", - "name": "ARISTA01T1", - "local_addr": "10.0.0.56", - "nhopself": "0", - "holdtime": "10", - "asn": "64600", - "keepalive": "3" - }, - "10.0.0.59": { - "rrclient": "0", - "name": "ARISTA02T1", - "local_addr": "10.0.0.58", - "nhopself": "0", - "holdtime": "10", - "asn": "64600", - "keepalive": "3" - }, -} -``` - -Full sample config_db.json files are availables at -[here](https://github.com/Azure/SONiC/blob/gh-pages/doc/config_db.json) -and -[here](https://github.com/Azure/SONiC/blob/gh-pages/doc/config_db_t0.json). - - -### ACL and Mirroring - -ACL and mirroring related configuration are defined in -**MIRROR_SESSION**, **ACL_TABLE** and **ACL_RULE** tables. Those -tables are in progress of migrating from APPDB. Please refer to their -schema in APPDB -[here](https://github.com/Azure/sonic-swss/blob/4c56d23b9ff4940bdf576cf7c9e5aa77adcbbdcc/doc/swss-schema.md) -and migration plan -[here](https://github.com/Azure/SONiC/wiki/ACL-Configuration-Requirement-Description). - -``` -{ -"MIRROR_SESSION": { - "everflow0": { - "src_ip": "10.1.0.32", - "dst_ip": "2.2.2.2" - } - }, - -"ACL_TABLE": { - "DATAACL": { - "policy_desc" : "data_acl", - "type": "l3", - "ports": [ - "Ethernet0", - "Ethernet4", - "Ethernet8", - "Ethernet12" - ] - } - } -} -``` - -***Below ACL table added as per the mail*** -``` -{ -"ACL_TABLE": { - "aaa": { - "type": "L3", - "ports": "Ethernet0" - } - }, -"ACL_RULE": { - "aaa|rule_0": { - "PRIORITY": "55", - "PACKET_ACTION": "DROP", - "L4_SRC_PORT": "0" - }, - "aaa|rule_1": { - "PRIORITY": "55", - "PACKET_ACTION": "DROP", - "L4_SRC_PORT": "1" - } - } -} -``` - -***Below ACL table added by comparig minigraph.xml & config_db.json*** - -``` -{ -"ACL_TABLE": { - "EVERFLOW": { - "type": "MIRROR", - "policy_desc": "EVERFLOW", - "ports": [ - "PortChannel0001", - "PortChannel0002", - "PortChannel0003", - "PortChannel0004" - ] - }, - "EVERFLOWV6": { - "type": "MIRRORV6", - "policy_desc": "EVERFLOWV6", - "ports": [ - "PortChannel0001", - "PortChannel0002", - "PortChannel0003", - "PortChannel0004" - ] - }, - "SNMP_ACL": { - "services": [ - "SNMP" - ], - "type": "CTRLPLANE", - "policy_desc": "SNMP_ACL" - }, - "SSH_ONLY": { - "services": [ - "SSH" - ], - "type": "CTRLPLANE", - "policy_desc": "SSH_ONLY" - } - }, - -"ACL_RULE": { - "SNMP_ACL|DEFAULT_RULE": { - "PRIORITY": "1", - "PACKET_ACTION": "DROP", - "ETHER_TYPE": "2048" - }, - "SNMP_ACL|RULE_1": { - "PRIORITY": "9999", - "PACKET_ACTION": "ACCEPT", - "SRC_IP": "1.1.1.1/32", - "IP_PROTOCOL": "17" - }, - "SNMP_ACL|RULE_2": { - "PRIORITY": "9998", - "PACKET_ACTION": "ACCEPT", - "SRC_IP": "2.2.2.2/32", - "IP_PROTOCOL": "17" - }, - "SSH_ONLY|DEFAULT_RULE": { - "PRIORITY": "1", - "PACKET_ACTION": "DROP", - "ETHER_TYPE": "2048" - }, - "SSH_ONLY|RULE_1": { - "PRIORITY": "9999", - "PACKET_ACTION": "ACCEPT", - "SRC_IP": "4.4.4.4/8", - "IP_PROTOCOL": "6" - } - } -} - -``` - -***ACL table type configuration example*** -``` -{ - "ACL_TABLE_TYPE": { - "CUSTOM_L3": { - "MATCHES": [ - "IN_PORTS", - "OUT_PORTS", - "SRC_IP" - ], - "ACTIONS": [ - "PACKET_ACTION", - "MIRROR_INGRESS_ACTION" - ], - "BIND_POINTS": [ - "PORT", - "LAG" - ] - } - }, - "ACL_TABLE": { - "DATAACL": { - "STAGE": "INGRESS", - "TYPE": "CUSTOM_L3", - "PORTS": [ - "Ethernet0", - "PortChannel1" - ] - } - }, - "ACL_RULE": { - "DATAACL|RULE0": { - "PRIORITY": "999", - "PACKET_ACTION": "DROP", - "SRC_IP": "1.1.1.1/32", - } - } -} -``` - -### BGP Sessions - -BGP session configuration is defined in **BGP_NEIGHBOR** table. BGP -neighbor address is used as key of bgp neighbor objects. Object -attributes include remote AS number, neighbor router name, and local -peering address. Dynamic neighbor is also supported by defining peer -group name and IP ranges in **BGP_PEER_RANGE** table. - -``` -{ -"BGP_NEIGHBOR": { - "10.0.0.61": { - "local_addr": "10.0.0.60", - "asn": 64015, - "name": "ARISTA15T0" - }, - "10.0.0.49": { - "local_addr": "10.0.0.48", - "asn": 64009, - "name": "ARISTA09T0" - }, - - "10.0.0.63": { - "rrclient": "0", - "name": "ARISTA04T1", - "local_addr": "10.0.0.62", - "nhopself": "0", - "holdtime": "10", - "asn": "64600", - "keepalive": "3" - } - -"BGP_PEER_RANGE": { - "BGPSLBPassive": { - "name": "BGPSLBPassive", - "ip_range": [ - "10.250.0.0/27" - ] - }, - "BGPVac": { - "name": "BGPVac", - "ip_range": [ - "10.2.0.0/16" - ] - } - } -} -``` - -### BUFFER_PG - -When the system is running in traditional buffer model, profiles needs to explicitly configured: - -``` -{ -"BUFFER_PG": { - "Ethernet0|3-4": { - "profile": "pg_lossless_40000_5m_profile" - }, - "Ethernet1|3-4": { - "profile": "pg_lossless_40000_5m_profile" - }, - "Ethernet2|3-4": { - "profile": "pg_lossless_40000_5m_profile" - } - } -} - -``` - -When the system is running in dynamic buffer model, profiles can be: - - - either calculated dynamically according to ports' configuration and just configured as "NULL"; - - or configured explicitly. - -``` -{ -"BUFFER_PG": { - "Ethernet0|3-4": { - "profile": "NULL" - }, - "Ethernet1|3-4": { - "profile": "NULL" - }, - "Ethernet2|3-4": { - "profile": "static_profile" - } - } -} - -``` - -### Buffer pool - -When the system is running in traditional buffer model, the size of all of the buffer pools and xoff of ingress_lossless_pool need to be configured explicitly. - -``` -{ -"BUFFER_POOL": { - "egress_lossless_pool": { - "type": "egress", - "mode": "static", - "size": "15982720" - }, - "egress_lossy_pool": { - "type": "egress", - "mode": "dynamic", - "size": "9243812" - }, - "ingress_lossless_pool": { - "xoff": "4194112", - "type": "ingress", - "mode": "dynamic", - "size": "10875072" - } - } -} - -``` - -When the system is running in dynamic buffer model, the size of some of the buffer pools can be omitted and will be dynamically calculated. - -``` -{ -"BUFFER_POOL": { - "egress_lossless_pool": { - "type": "egress", - "mode": "static", - "size": "15982720" - }, - "egress_lossy_pool": { - "type": "egress", - "mode": "dynamic", - }, - "ingress_lossless_pool": { - "type": "ingress", - "mode": "dynamic", - } - } -} - -``` - - -### Buffer profile - -``` -{ -"BUFFER_PROFILE": { - "egress_lossless_profile": { - "static_th": "3995680", - "pool": "egress_lossless_pool", - "size": "1518" - }, - "egress_lossy_profile": { - "dynamic_th": "3", - "pool": "egress_lossy_pool", - "size": "1518" - }, - "ingress_lossy_profile": { - "dynamic_th": "3", - "pool": "ingress_lossless_pool", - "size": "0" - }, - "pg_lossless_40000_5m_profile": { - "xon_offset": "2288", - "dynamic_th": "-3", - "xon": "2288", - "xoff": "66560", - "pool": "ingress_lossless_pool", - "size": "1248" - }, - "pg_lossless_40000_40m_profile": { - "xon_offset": "2288", - "dynamic_th": "-3", - "xon": "2288", - "xoff": "71552", - "pool": "ingress_lossless_pool", - "size": "1248" - } - } -} - -``` - -When the system is running in dynamic buffer model and the headroom_type is dynamic, only dynamic_th needs to be configured and rest of fields can be omitted. -This kind of profiles will be handled by buffer manager and won't be applied to SAI. - -``` -{ - { - "non_default_dynamic_th_profile": { - "dynamic_th": 1, - "headroom_type": "dynamic" - } - } -} -``` - -### Buffer queue - -``` -{ -"BUFFER_QUEUE": { - "Ethernet50,Ethernet52,Ethernet54,Ethernet56|0-2": { - "profile": "egress_lossy_profile" - }, - "Ethernet50,Ethernet52,Ethernet54,Ethernet56|3-4": { - "profile": "egress_lossless_profile" - }, - "Ethernet50,Ethernet52,Ethernet54,Ethernet56|5-6": { - "profile": "egress_lossy_profile" - } - } -} - -``` - -### Buffer port ingress profile list - -``` -{ -"BUFFER_PORT_INGRESS_PROFILE_LIST": { - "Ethernet50": { - "profile_list": "ingress_lossy_profile,ingress_lossless_profile" - }, - "Ethernet52": { - "profile_list": "ingress_lossy_profile,ingress_lossless_profile" - }, - "Ethernet56": { - "profile_list": "ingress_lossy_profile,ingress_lossless_profile" - } - } -} - -``` - -### Buffer port egress profile list - -``` -{ -"BUFFER_PORT_EGRESS_PROFILE_LIST": { - "Ethernet50": { - "profile_list": "egress_lossy_profile,egress_lossless_profile" - }, - "Ethernet52": { - "profile_list": "egress_lossy_profile,egress_lossless_profile" - }, - "Ethernet56": { - "profile_list": "egress_lossy_profile,egress_lossless_profile" - } - } -} - -``` - -### Cable length - -``` -{ -"CABLE_LENGTH": { - "AZURE": { - "Ethernet8": "5m", - "Ethernet9": "5m", - "Ethernet2": "5m", - "Ethernet58": "5m", - "Ethernet59": "5m", - "Ethernet50": "40m", - "Ethernet51": "5m", - "Ethernet52": "40m", - "Ethernet53": "5m", - "Ethernet54": "40m", - "Ethernet55": "5m", - "Ethernet56": "40m" - } - } -} - -``` - -### COPP_TABLE - -``` -{ -"COPP_TABLE": { - "default": { - "cbs": "600", - "cir": "600", - "meter_type": "packets", - "mode": "sr_tcm", - "queue": "0", - "red_action": "drop" - }, - - "trap.group.arp": { - "cbs": "600", - "cir": "600", - "meter_type": "packets", - "mode": "sr_tcm", - "queue": "4", - "red_action": "drop", - "trap_action": "trap", - "trap_ids": "arp_req,arp_resp,neigh_discovery", - "trap_priority": "4" - }, - - "trap.group.lldp.dhcp.udld": { - "queue": "4", - "trap_action": "trap", - "trap_ids": "lldp,dhcp,udld", - "trap_priority": "4" - }, - - "trap.group.bgp.lacp": { - "queue": "4", - "trap_action": "trap", - "trap_ids": "bgp,bgpv6,lacp", - "trap_priority": "4" - }, - - "trap.group.ip2me": { - "cbs": "600", - "cir": "600", - "meter_type": "packets", - "mode": "sr_tcm", - "queue": "1", - "red_action": "drop", - "trap_action": "trap", - "trap_ids": "ip2me", - "trap_priority": "1" - } - } -} -``` - -### CRM - -``` -{ -"CRM": { - "Config": { - "acl_table_threshold_type": "percentage", - "nexthop_group_threshold_type": "percentage", - "fdb_entry_high_threshold": "85", - "acl_entry_threshold_type": "percentage", - "ipv6_neighbor_low_threshold": "70", - "nexthop_group_member_low_threshold": "70", - "acl_group_high_threshold": "85", - "ipv4_route_high_threshold": "85", - "acl_counter_high_threshold": "85", - "ipv4_route_low_threshold": "70", - "ipv4_route_threshold_type": "percentage", - "ipv4_neighbor_low_threshold": "70", - "acl_group_threshold_type": "percentage", - "ipv4_nexthop_high_threshold": "85", - "ipv6_route_threshold_type": "percentage", - "snat_entry_threshold_type": "percentage", - "snat_entry_high_threshold": "85", - "snat_entry_low_threshold": "70", - "dnat_entry_threshold_type": "percentage", - "dnat_entry_high_threshold": "85", - "dnat_entry_low_threshold": "70", - "ipmc_entry_threshold_type": "percentage", - "ipmc_entry_high_threshold": "85", - "ipmc_entry_low_threshold": "70" - } - } -} - -``` - -### Data Plane L3 Interfaces - -IP configuration for data plane are defined in **INTERFACE**, -**PORTCHANNEL_INTERFACE**, and **VLAN_INTERFACE** table. The objects -in all three tables have the interface (could be physical port, port -channel, or vlan) that IP address is attached to as first-level key, and -IP prefix as second-level key. IP interface objects don't have any -attributes. - -``` -{ -"INTERFACE": { - "Ethernet0|10.0.0.0/31": {}, - "Ethernet4|10.0.0.2/31": {}, - "Ethernet8|10.0.0.4/31": {} - ... - }, - -"PORTCHANNEL_INTERFACE": { - "PortChannel01|10.0.0.56/31": {}, - "PortChannel01|FC00::71/126": {}, - "PortChannel02|10.0.0.58/31": {}, - "PortChannel02|FC00::75/126": {} - ... - }, -"VLAN_INTERFACE": { - "Vlan1000|192.168.0.1/27": {} - } -} - -``` - - -### DEFAULT_LOSSLESS_BUFFER_PARAMETER - -This table stores the default lossless buffer parameters for dynamic buffer calculation. - -``` -{ - "DEFAULT_LOSSLESS_BUFFER_PARAMETER": { - "AZURE": { - "default_dynamic_th": "0", - "over_subscribe_ratio": "2" - } - } -} -``` - -### Device Metadata - -The **DEVICE_METADATA** table contains only one object named -*localhost*. In this table the device metadata such as hostname, hwsku, -deployment envionment id and deployment type are specified. BGP local AS -number is also specified in this table as current only single BGP -instance is supported in SONiC. - -``` -{ -"DEVICE_METADATA": { - "localhost": { - "hwsku": "Force10-S6100", - "default_bgp_status": "up", - "docker_routing_config_mode": "unified", - "hostname": "sonic-s6100-01", - "platform": "x86_64-dell_s6100_c2538-r0", - "mac": "4c:76:25:f4:70:82", - "default_pfcwd_status": "disable", - "bgp_asn": "65100", - "deployment_id": "1", - "type": "ToRRouter", - "buffer_model": "traditional" - } - } -} - -``` - - -### Device neighbor metada - -``` -{ -"DEVICE_NEIGHBOR_METADATA": { - "ARISTA01T1": { - "lo_addr": "None", - "mgmt_addr": "10.11.150.45", - "hwsku": "Arista-VM", - "type": "LeafRouter" - }, - "ARISTA02T1": { - "lo_addr": "None", - "mgmt_addr": "10.11.150.46", - "hwsku": "Arista-VM", - "type": "LeafRouter" - } - } -} - -``` - - -### DSCP_TO_TC_MAP -``` -{ -"DSCP_TO_TC_MAP": { - "AZURE": { - "1": "1", - "0": "1", - "3": "3", - "2": "1", - "5": "2", - "4": "4", - "7": "1", - "6": "1", - "9": "1", - "8": "0" - } - } -} - -``` - - -### MPLS_TC_TO_TC_MAP -``` -{ -"MPLS_TC_TO_TC_MAP": { - "AZURE": { - "0": "0", - "1": "1", - "2": "1", - "3": "2", - "4": "2", - "5": "3", - "6": "3", - "7": "4" - } - } -} - -``` - -### FLEX_COUNTER_TABLE - -``` -{ -"FLEX_COUNTER_TABLE": { - "PFCWD": { - "FLEX_COUNTER_STATUS": "enable" - }, - "PORT": { - "FLEX_COUNTER_STATUS": "enable" - }, - "QUEUE": { - "FLEX_COUNTER_STATUS": "enable" - } - } -} - -``` - - -### L2 Neighbors - -The L2 neighbor and connection information can be configured in -**DEVICE_NEIGHBOR** table. Those information are used mainly for LLDP. -While mandatory fields include neighbor name acting as object key and -remote port / local port information in attributes, optional information -about neighbor device such as device type, hwsku, management address and -loopback address can also be defined. - -``` -{ -"DEVICE_NEIGHBOR": { - "ARISTA04T1": { - "mgmt_addr": "10.20.0.163", - "hwsku": "Arista", - "lo_addr": null, - "local_port": "Ethernet124", - "type": "LeafRouter", - "port": "Ethernet1" - }, - "ARISTA03T1": { - "mgmt_addr": "10.20.0.162", - "hwsku": "Arista", - "lo_addr": null, - "local_port": "Ethernet120", - "type": "LeafRouter", - "port": "Ethernet1" - }, - "ARISTA02T1": { - "mgmt_addr": "10.20.0.161", - "hwsku": "Arista", - "lo_addr": null, - "local_port": "Ethernet116", - "type": "LeafRouter", - "port": "Ethernet1" - }, - "ARISTA01T1": { - "mgmt_addr": "10.20.0.160", - "hwsku": "Arista", - "lo_addr": null, - "local_port": "Ethernet112", - "type": "LeafRouter", - "port": "Ethernet1" - } - } -} -``` - -### Loopback Interface - -Loopback interface configuration lies in **LOOPBACK_INTERFACE** table -and has similar schema with data plane interfaces. The loopback device -name and loopback IP prefix act as multi-level key for loopback -interface objects. - -``` -{ -"LOOPBACK_INTERFACE": { - "Loopback0|10.1.0.32/32": {}, - "Loopback0|FC00:1::32/128": {} - } -} - -``` - -### LOSSLESS_TRAFFIC_PATTERN - -The LOSSLESS_TRAFFIC_PATTERN table stores parameters related to -lossless traffic for dynamic buffer calculation - -``` -{ - "LOSSLESS_TRAFFIC_PATTERN": { - "AZURE": { - "mtu": "1024", - "small_packet_percentage": "100" - } - } -} -``` - -### Management Interface - -Management interfaces are defined in **MGMT_INTERFACE** table. Object -key is composed of management interface name and IP prefix. Attribute -***gwaddr*** specify the gateway address of the prefix. -***forced_mgmt_routes*** attribute can be used to specify addresses / -prefixes traffic to which are forced to go through management network -instead of data network. - -``` -{ -"MGMT_INTERFACE": { - "eth0|10.11.150.11/16": { - "gwaddr": "10.11.0.1" - }, - "eth0|FC00:2::32/64": { - "forced_mgmt_routes": [ - "10.0.0.100/31", - "10.250.0.8", - "10.255.0.0/28" - ], - "gwaddr": "fc00:2::1" - } - } -} - -``` - -### Management port - -``` -{ -"MGMT_PORT": { - "eth0": { - "alias": "eth0", - "admin_status": "up" - } - } -} - -``` - - -### Management VRF - -``` -{ -"MGMT_VRF_CONFIG": { - "vrf_global": { - "mgmtVrfEnabled": "true" - } - } -} -``` - -### MAP_PFC_PRIORITY_TO_QUEUE - -``` -{ -"MAP_PFC_PRIORITY_TO_QUEUE": { - "AZURE": { - "1": "1", - "0": "0", - "3": "3", - "2": "2", - "5": "5", - "4": "4", - "7": "7", - "6": "6" - } - } -} -``` -### NTP Global Configuration - -These configuration options are used to modify the way that -ntp binds to the ports on the switch and which port it uses to -make ntp update requests from. - -***NTP VRF*** - -If this option is set to `default` then ntp will run within the default vrf -**when the management vrf is enabled**. If the mgmt vrf is enabled and this value is -not set to default then ntp will run within the mgmt vrf. - -This option **has no effect** if the mgmt vrf is not enabled. - -``` -{ -"NTP": { - "global": { - "vrf": "default" - } - } -} -``` - - -***NTP Source Port*** - -This option sets the port which ntp will choose to send time update requests from by. - -NOTE: If a Loopback interface is defined on the switch ntp will choose this by default, so this setting -is **required** if the switch has a Loopback interface and the ntp peer does not have defined routes -for that address. - -``` -{ -"NTP": { - "global": { - "src_intf": "Ethernet1" - } - } -} -``` - -### NTP and SYSLOG servers - -These information are configured in individual tables. Domain name or IP -address of the server is used as object key. Currently there are no -attributes in those objects. - -***NTP server*** -``` -{ -"NTP_SERVER": { - "2.debian.pool.ntp.org": {}, - "1.debian.pool.ntp.org": {}, - "3.debian.pool.ntp.org": {}, - "0.debian.pool.ntp.org": {} - }, - -"NTP_SERVER": { - "23.92.29.245": {}, - "204.2.134.164": {} - } -} -``` - -***Syslogserver*** -``` -{ -"SYSLOG_SERVER": { - "10.0.0.5": {}, - "10.0.0.6": {}, - "10.11.150.5": {} - } -} -``` - -### Port - -In this table the physical port configurations are defined. Each object -will have port name as its key, and port name alias and port speed as -optional attributes. - -``` -{ -"PORT": { - "Ethernet0": { - "index": "0", - "lanes": "101,102", - "description": "fortyGigE1/1/1", - "mtu": "9100", - "alias": "fortyGigE1/1/1", - "speed": "40000" - }, - "Ethernet1": { - "index": "1", - "lanes": "103,104", - "description": "fortyGigE1/1/2", - "mtu": "9100", - "alias": "fortyGigE1/1/2", - "admin_status": "up", - "speed": "40000" - }, - "Ethernet63": { - "index": "63", - "lanes": "87,88", - "description": "fortyGigE1/4/16", - "mtu": "9100", - "alias": "fortyGigE1/4/16", - "speed": "40000" - } - } -} - -``` - -### Port Channel - -Port channels are defined in **PORTCHANNEL** table with port channel -name as object key and member list as attribute. - -``` -{ -"PORTCHANNEL": { - "PortChannel0003": { - "admin_status": "up", - "min_links": "1", - "members": [ - "Ethernet54" - ], - "mtu": "9100" - }, - "PortChannel0004": { - "admin_status": "up", - "min_links": "1", - "members": [ - "Ethernet56" - ], - "mtu": "9100" - } - } -} -``` - - -### Portchannel member - -``` -{ -"PORTCHANNEL_MEMBER": { - "PortChannel0001|Ethernet50": {}, - "PortChannel0002|Ethernet52": {}, - "PortChannel0003|Ethernet54": {}, - "PortChannel0004|Ethernet56": {} - } -} - -``` -### Scheduler - -``` -{ -"SCHEDULER": { - "scheduler.0": { - "type": "STRICT" - }, - "scheduler.1": { - "type": "WRR" - "weight": "1", - "meter_type": "bytes", - "pir": "1250000000", - "pbs": "8192" - }, - "scheduler.port": { - "meter_type": "bytes", - "pir": "1000000000", - "pbs": "8192" - } - } -} -``` - -### Port QoS Map - -``` -{ -"PORT_QOS_MAP": { - "Ethernet50,Ethernet52,Ethernet54,Ethernet56": { - "tc_to_pg_map": "AZURE", - "tc_to_queue_map": "AZURE", - "pfc_enable": "3,4", - "pfc_to_queue_map": "AZURE", - "dscp_to_tc_map": "AZURE", - "dscp_to_fc_map": "AZURE", - "exp_to_fc_map": "AZURE", - "scheduler": "scheduler.port" - } - } -} -``` - -### Queue -``` -{ -"QUEUE": { - "Ethernet56|4": { - "wred_profile": "AZURE_LOSSLESS", - "scheduler": "scheduler.1" - }, - "Ethernet56|5": { - "scheduler": "scheduler.0" - }, - "Ethernet56|6": { - "scheduler": "scheduler.0" - } - } -} -``` - - -### Tacplus Server - -``` -{ -"TACPLUS_SERVER": { - "10.0.0.8": { - "priority": "1", - "tcp_port": "49" - }, - "10.0.0.9": { - "priority": "1", - "tcp_port": "49" - } - } -} -``` - - -### TC to Priority group map - -``` -{ -"TC_TO_PRIORITY_GROUP_MAP": { - "AZURE": { - "1": "1", - "0": "0", - "3": "3", - "2": "2", - "5": "5", - "4": "4", - "7": "7", - "6": "6" - } - } -} -``` - -### TC to Queue map - -``` -{ -"TC_TO_QUEUE_MAP": { - "AZURE": { - "1": "1", - "0": "0", - "3": "3", - "2": "2", - "5": "5", - "4": "4", - "7": "7", - "6": "6" - } - } -} -``` - -### Versions - -This table is where the curret version of the software is recorded. -``` -{ - "VERSIONS": { - "DATABASE": { - "VERSION": "version_1_0_1" - } - } -} -``` - -### VLAN - -This table is where VLANs are defined. VLAN name is used as object key, -and member list as well as an integer id are defined as attributes. If a -DHCP relay is required for this VLAN, a dhcp_servers attribute must be -specified for that VLAN, the value of which is a list that must contain -the domain name or IP address of one or more DHCP servers. - -``` -{ -"VLAN": { - "Vlan1000": { - "dhcp_servers": [ - "192.0.0.1", - "192.0.0.2", - "192.0.0.3", - "192.0.0.4" - ], - "members": [ - "Ethernet0", - "Ethernet4", - "Ethernet8", - "Ethernet12" - ], - "vlanid": "1000" - } - } -} -``` - -### VLAN_MEMBER - -VLAN member table has Vlan name together with physical port or port -channel name as object key, and tagging mode as attributes. - -``` -{ -"VLAN_MEMBER": { - "Vlan1000|PortChannel47": { - "tagging_mode": "untagged" - }, - "Vlan1000|Ethernet8": { - "tagging_mode": "untagged" - }, - "Vlan2000|PortChannel47": { - "tagging_mode": "tagged" - } - } -} -``` - -### Virtual router - -The virtual router table allows to insert or update a new virtual router -instance. The key of the instance is its name. The attributes in the -table allow to change properties of a virtual router. Attributes: - -- 'v4' contains boolean value 'true' or 'false'. Enable or - disable IPv4 in the virtual router -- 'v6' contains boolean value 'true' or 'false'. Enable or - disable IPv6 in the virtual router -- 'src_mac' contains MAC address. What source MAC address will be - used for packets egressing from the virtual router -- 'ttl_action' contains packet action. Defines the action for - packets with TTL == 0 or TTL == 1 -- 'ip_opt_action' contains packet action. Defines the action for - packets with IP options -- 'l3_mc_action' contains packet action. Defines the action for - unknown L3 multicast packets - -The packet action could be: - -- 'drop' -- 'forward' -- 'copy' -- 'copy_cancel' -- 'trap' -- 'log' -- 'deny' -- 'transit' - - -***TBD*** -``` -'VRF:rid1': { - 'v4': 'true', - 'v6': 'false', - 'src_mac': '02:04:05:06:07:08', - 'ttl_action': 'copy', - 'ip_opt_action': 'deny', - 'l3_mc_action': 'drop' -} -``` - - -### WRED_PROFILE - -``` -{ -"WRED_PROFILE": { - "AZURE_LOSSLESS": { - "red_max_threshold": "2097152", - "wred_green_enable": "true", - "ecn": "ecn_all", - "green_min_threshold": "1048576", - "red_min_threshold": "1048576", - "wred_yellow_enable": "true", - "yellow_min_threshold": "1048576", - "green_max_threshold": "2097152", - "green_drop_probability": "5", - "yellow_max_threshold": "2097152", - "wred_red_enable": "true", - "yellow_drop_probability": "5", - "red_drop_probability": "5" - } - } -} -``` - -### BREAKOUT_CFG - -This table is introduced as part of Dynamic Port Breakout(DPB) feature. -It shows the current breakout mode of all ports(root ports). -The list of root ports, all possible breakout modes, and default breakout modes - are obtained/derived from platform.json and hwsku.json files. - -``` -"BREAKOUT_CFG": { - "Ethernet0": { - "brkout_mode": "4x25G[10G]" - }, - "Ethernet4": { - "brkout_mode": "4x25G[10G]" - }, - "Ethernet8": { - "brkout_mode": "4x25G[10G]" - }, - - ...... - - "Ethernet116": { - "brkout_mode": "2x50G" - }, - "Ethernet120": { - "brkout_mode": "2x50G" - }, - "Ethernet124": { - "brkout_mode": "2x50G" - } -} -``` - -For Developers -============== - -Generating Application Config by Jinja2 Template ------------------------------------------------- - -To be added. - -Incremental Configuration by Subscribing to ConfigDB ----------------------------------------------------- - -Detail instruction to be added. A sample could be found in this -[PR](https://github.com/Azure/sonic-buildimage/pull/861) that -implemented dynamic configuration for BGP. diff --git a/fdbsyncd/Makefile.am b/fdbsyncd/Makefile.am index 4ab2f5dddd..b35ee5f309 100644 --- a/fdbsyncd/Makefile.am +++ b/fdbsyncd/Makefile.am @@ -10,10 +10,15 @@ 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) +fdbsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) $(CFLAGS_ASAN) +fdbsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) $(CFLAGS_ASAN) +fdbsyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lswsscommon $(COV_LDFLAGS) if GCOV_ENABLED fdbsyncd_LDADD += -lgcovpreload endif + +if ASAN_ENABLED +fdbsyncd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/fpmsyncd/Makefile.am b/fpmsyncd/Makefile.am index ef709db876..29b81d7381 100644 --- a/fpmsyncd/Makefile.am +++ b/fpmsyncd/Makefile.am @@ -10,10 +10,15 @@ endif fpmsyncd_SOURCES = fpmsyncd.cpp fpmlink.cpp routesync.cpp $(top_srcdir)/warmrestart/warmRestartHelper.cpp -fpmsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -fpmsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -fpmsyncd_LDADD = -lnl-3 -lnl-route-3 -lswsscommon +fpmsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +fpmsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +fpmsyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lswsscommon if GCOV_ENABLED fpmsyncd_LDADD += -lgcovpreload endif + +if ASAN_ENABLED +fpmsyncd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/gearsyncd/Makefile.am b/gearsyncd/Makefile.am index c9df85853a..1a1d9983a2 100644 --- a/gearsyncd/Makefile.am +++ b/gearsyncd/Makefile.am @@ -1,6 +1,6 @@ INCLUDES = -I $(top_srcdir)/lib -I $(top_srcdir) -I $(top_srcdir)/warmrestart -I $(top_srcdir)/cfgmgr -bin_PROGRAMS = gearsyncd +bin_PROGRAMS = gearsyncd if DEBUG DBGFLAGS = -ggdb -DDEBUG @@ -8,12 +8,17 @@ else DBGFLAGS = -g endif -gearsyncd_SOURCES = $(top_srcdir)/lib/gearboxutils.cpp gearsyncd.cpp gearparserbase.cpp gearboxparser.cpp phyparser.cpp $(top_srcdir)/cfgmgr/shellcmd.h +gearsyncd_SOURCES = $(top_srcdir)/lib/gearboxutils.cpp gearsyncd.cpp gearparserbase.cpp gearboxparser.cpp phyparser.cpp $(top_srcdir)/cfgmgr/shellcmd.h -gearsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) $(ASAN_CFLAGS) +gearsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(COV_CFLAGS) $(CFLAGS_ASAN) -gearsyncd_LDADD = -lnl-3 -lnl-route-3 -lswsscommon $(COV_LDFLAGS) $(ASAN_LDFLAGS) +gearsyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lswsscommon $(COV_LDFLAGS) if GCOV_ENABLED gearsyncd_LDADD += -lgcovpreload endif + +if ASAN_ENABLED +gearsyncd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/lib/asan.cpp b/lib/asan.cpp new file mode 100644 index 0000000000..053d1bd3c3 --- /dev/null +++ b/lib/asan.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +#include + +static void swss_asan_sigterm_handler(int signo) +{ + SWSS_LOG_ENTER(); + + __lsan_do_leak_check(); + + struct sigaction sigact; + if (sigaction(SIGTERM, NULL, &sigact)) + { + SWSS_LOG_ERROR("failed to get current SIGTERM action handler"); + _exit(EXIT_FAILURE); + } + + // Check the currently set signal handler. + // If it is ASAN's signal handler this means that the application didn't set its own handler. + // To preserve default behavior set the default signal handler and raise the signal to trigger its execution. + // Otherwise, the application installed its own signal handler. + // In this case, just trigger a leak check and do nothing else. + if (sigact.sa_handler == swss_asan_sigterm_handler) { + sigemptyset(&sigact.sa_mask); + sigact.sa_flags = 0; + sigact.sa_handler = SIG_DFL; + if (sigaction(SIGTERM, &sigact, NULL)) + { + SWSS_LOG_ERROR("failed to setup SIGTERM action handler"); + _exit(EXIT_FAILURE); + } + + raise(signo); + } +} + +__attribute__((constructor)) +static void swss_asan_init() +{ + SWSS_LOG_ENTER(); + + struct sigaction sigact = {}; + sigact.sa_handler = swss_asan_sigterm_handler; + if (sigaction(SIGTERM, &sigact, NULL)) + { + SWSS_LOG_ERROR("failed to setup SIGTERM action handler"); + exit(EXIT_FAILURE); + } +} diff --git a/lib/subintf.cpp b/lib/subintf.cpp index ab26ee62de..4f934ff659 100644 --- a/lib/subintf.cpp +++ b/lib/subintf.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include "subintf.h" using namespace swss; diff --git a/lib/subintf.h b/lib/subintf.h index ec5d0c4656..263faf9f3a 100644 --- a/lib/subintf.h +++ b/lib/subintf.h @@ -1,5 +1,7 @@ #pragma once +#include + #define VLAN_SUB_INTERFACE_SEPARATOR "." namespace swss { class subIntf diff --git a/mclagsyncd/Makefile.am b/mclagsyncd/Makefile.am index e7bed8de7d..d4b4b03c40 100644 --- a/mclagsyncd/Makefile.am +++ b/mclagsyncd/Makefile.am @@ -10,10 +10,15 @@ endif mclagsyncd_SOURCES = mclagsyncd.cpp mclaglink.cpp -mclagsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -mclagsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -mclagsyncd_LDADD = -lnl-3 -lnl-route-3 -lswsscommon +mclagsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +mclagsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +mclagsyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lswsscommon if GCOV_ENABLED mclagsyncd_LDADD += -lgcovpreload endif + +if ASAN_ENABLED +mclagsyncd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/natsyncd/Makefile.am b/natsyncd/Makefile.am index d8212ee4b4..cdee9d52ae 100644 --- a/natsyncd/Makefile.am +++ b/natsyncd/Makefile.am @@ -10,11 +10,15 @@ endif natsyncd_SOURCES = natsyncd.cpp natsync.cpp $(top_srcdir)/warmrestart/warmRestartAssist.cpp -natsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -natsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -natsyncd_LDADD = -lnl-3 -lnl-route-3 -lnl-nf-3 -lswsscommon +natsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +natsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +natsyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lnl-nf-3 -lswsscommon if GCOV_ENABLED natsyncd_LDADD += -lgcovpreload endif +if ASAN_ENABLED +natsyncd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/neighsyncd/Makefile.am b/neighsyncd/Makefile.am index 23e76b6cd2..cb61a83bbc 100644 --- a/neighsyncd/Makefile.am +++ b/neighsyncd/Makefile.am @@ -10,10 +10,15 @@ endif neighsyncd_SOURCES = neighsyncd.cpp neighsync.cpp $(top_srcdir)/warmrestart/warmRestartAssist.cpp -neighsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -neighsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -neighsyncd_LDADD = -lnl-3 -lnl-route-3 -lswsscommon +neighsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +neighsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +neighsyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lswsscommon if GCOV_ENABLED neighsyncd_LDADD += -lgcovpreload endif + +if ASAN_ENABLED +neighsyncd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/neighsyncd/neighsync.cpp b/neighsyncd/neighsync.cpp index 5492a2cf87..8864746cb5 100644 --- a/neighsyncd/neighsync.cpp +++ b/neighsyncd/neighsync.cpp @@ -80,6 +80,16 @@ void NeighSync::onMsg(int nlmsg_type, struct nl_object *obj) key+= ":"; nl_addr2str(rtnl_neigh_get_dst(neigh), ipStr, MAX_ADDR_SIZE); + + /* Ignore IPv4 link-local addresses as neighbors */ + IpAddress ipAddress(ipStr); + if (family == IPV4_NAME && ipAddress.getAddrScope() == IpAddress::AddrScope::LINK_SCOPE) + { + SWSS_LOG_INFO("Link Local address received, ignoring for %s", ipStr); + return; + } + + /* Ignore IPv6 link-local addresses as neighbors, if ipv6 link local mode is disabled */ if (family == IPV6_NAME && IN6_IS_ADDR_LINKLOCAL(nl_addr_get_binary_addr(rtnl_neigh_get_dst(neigh)))) { diff --git a/neighsyncd/neighsync.h b/neighsyncd/neighsync.h index d179a6ba07..8f25ee16c8 100644 --- a/neighsyncd/neighsync.h +++ b/neighsyncd/neighsync.h @@ -14,7 +14,7 @@ * service to finish, should be longer than the restore_neighbors timeout value (110) * This should not happen, if happens, system is in a unknown state, we should exit. */ -#define RESTORE_NEIGH_WAIT_TIME_OUT 120 +#define RESTORE_NEIGH_WAIT_TIME_OUT 180 namespace swss { diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 68aa474552..9524a61a19 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -62,6 +62,7 @@ orchagent_SOURCES = \ mirrororch.cpp \ fdborch.cpp \ aclorch.cpp \ + pbh/pbhcap.cpp \ pbh/pbhcnt.cpp \ pbh/pbhmgr.cpp \ pbh/pbhrule.cpp \ @@ -92,9 +93,10 @@ orchagent_SOURCES = \ lagid.cpp \ bfdorch.cpp \ srv6orch.cpp \ - response_publisher.cpp + response_publisher.cpp \ + nvgreorch.cpp -orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp flex_counter/flow_counter_handler.cpp +orchagent_SOURCES += flex_counter/flex_counter_manager.cpp flex_counter/flex_counter_stat_manager.cpp flex_counter/flow_counter_handler.cpp flex_counter/flowcounterrouteorch.cpp orchagent_SOURCES += debug_counter/debug_counter.cpp debug_counter/drop_counter.cpp orchagent_SOURCES += p4orch/p4orch.cpp \ p4orch/p4orch_util.cpp \ @@ -109,21 +111,28 @@ orchagent_SOURCES += p4orch/p4orch.cpp \ p4orch/wcmp_manager.cpp \ p4orch/mirror_session_manager.cpp -orchagent_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -orchagent_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -orchagent_LDADD = -lnl-3 -lnl-route-3 -lpthread -lsairedis -lsaimeta -lsaimetadata -lswsscommon -lzmq +orchagent_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +orchagent_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) $(CFLAGS_ASAN) +orchagent_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lpthread -lsairedis -lsaimeta -lsaimetadata -lswsscommon -lzmq routeresync_SOURCES = routeresync.cpp -routeresync_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -routeresync_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -routeresync_LDADD = -lswsscommon +routeresync_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +routeresync_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +routeresync_LDADD = $(LDFLAGS_ASAN) -lswsscommon orchagent_restart_check_SOURCES = orchagent_restart_check.cpp -orchagent_restart_check_CPPFLAGS = $(DBGFLAGS) $(AM_CPPFLAGS) $(CFLAGS_COMMON) -orchagent_restart_check_LDADD = -lhiredis -lswsscommon -lpthread +orchagent_restart_check_CPPFLAGS = $(DBGFLAGS) $(AM_CPPFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +orchagent_restart_check_LDADD = $(LDFLAGS_ASAN) -lhiredis -lswsscommon -lpthread if GCOV_ENABLED orchagent_LDADD += -lgcovpreload routeresync_LDADD += -lgcovpreload orchagent_restart_check_LDADD += -lgcovpreload endif + +if ASAN_ENABLED +orchagent_SOURCES += $(top_srcdir)/lib/asan.cpp +routeresync_SOURCES += $(top_srcdir)/lib/asan.cpp +orchagent_restart_check_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index c3a9f23ec7..e371ecd980 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -848,7 +848,10 @@ bool AclRule::createRule() decreaseNextHopRefCount(); } - gCrmOrch->incCrmAclTableUsedCounter(CrmResourceType::CRM_ACL_ENTRY, m_pTable->getOid()); + if (status == SAI_STATUS_SUCCESS) + { + gCrmOrch->incCrmAclTableUsedCounter(CrmResourceType::CRM_ACL_ENTRY, m_pTable->getOid()); + } return (status == SAI_STATUS_SUCCESS); } @@ -984,9 +987,13 @@ bool AclRule::updateCounter(const AclRule& updatedRule) { return false; } + + m_pAclOrch->registerFlexCounter(*this); } else { + m_pAclOrch->deregisterFlexCounter(*this); + if (!disableCounter()) { return false; @@ -2645,6 +2652,7 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr // Broadcom and Mellanox. Virtual switch is also supported for testing // purposes. string platform = getenv("platform") ? getenv("platform") : ""; + string sub_platform = getenv("sub_platform") ? getenv("sub_platform") : ""; if (platform == BRCM_PLATFORM_SUBSTRING || platform == CISCO_8000_PLATFORM_SUBSTRING || platform == MLNX_PLATFORM_SUBSTRING || @@ -2676,9 +2684,11 @@ void AclOrch::init(vector& connectors, PortsOrch *portOrch, Mirr m_mirrorTableCapabilities[TABLE_TYPE_MIRRORV6] ? "yes" : "no"); // In Mellanox platform, V4 and V6 rules are stored in different tables + // In Broadcom DNX platform also, V4 and V6 rules are stored in different tables if (platform == MLNX_PLATFORM_SUBSTRING || platform == CISCO_8000_PLATFORM_SUBSTRING || - platform == MRVL_PLATFORM_SUBSTRING) + platform == MRVL_PLATFORM_SUBSTRING || + (platform == BRCM_PLATFORM_SUBSTRING && sub_platform == BRCM_DNX_PLATFORM_SUBSTRING)) { m_isCombinedMirrorV6Table = false; } @@ -3617,7 +3627,9 @@ bool AclOrch::removeAclRule(string table_id, string rule_id) auto rule = getAclRule(table_id, rule_id); if (!rule) { - return false; + SWSS_LOG_NOTICE("ACL rule [%s] in table [%s] already deleted", + rule_id.c_str(), table_id.c_str()); + return true; } if (rule->hasCounter()) @@ -4462,7 +4474,7 @@ void AclOrch::registerFlexCounter(const AclRule& rule) void AclOrch::deregisterFlexCounter(const AclRule& rule) { auto ruleIdentifier = generateAclRuleIdentifierInCountersDb(rule); - m_countersDb.hdel(COUNTERS_ACL_COUNTER_RULE_MAP, rule.getId()); + m_countersDb.hdel(COUNTERS_ACL_COUNTER_RULE_MAP, ruleIdentifier); m_flex_counter_manager.clearCounterIdList(rule.getCounterOid()); } diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index 9e6db3919c..710720a5c1 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -489,6 +489,9 @@ class AclOrch : public Orch, public Observer bool m_isCombinedMirrorV6Table = true; map m_mirrorTableCapabilities; + void registerFlexCounter(const AclRule& rule); + void deregisterFlexCounter(const AclRule& rule); + // Get the OID for the ACL bind point for a given port static bool getAclBindPortId(Port& port, sai_object_id_t& port_id); @@ -537,8 +540,6 @@ class AclOrch : public Orch, public Observer void createDTelWatchListTables(); void deleteDTelWatchListTables(); - void registerFlexCounter(const AclRule& rule); - void deregisterFlexCounter(const AclRule& rule); string generateAclRuleIdentifierInCountersDb(const AclRule& rule) const; map m_AclTables; diff --git a/orchagent/bfdorch.cpp b/orchagent/bfdorch.cpp index 68295842b3..f40782ba2b 100644 --- a/orchagent/bfdorch.cpp +++ b/orchagent/bfdorch.cpp @@ -6,6 +6,7 @@ #include "notifier.h" #include "sai_serialize.h" #include "directory.h" +#include "notifications.h" using namespace std; using namespace swss; @@ -16,11 +17,13 @@ using namespace swss; #define BFD_SESSION_MILLISECOND_TO_MICROSECOND 1000 #define BFD_SRCPORTINIT 49152 #define BFD_SRCPORTMAX 65536 +#define NUM_BFD_SRCPORT_RETRIES 3 extern sai_bfd_api_t* sai_bfd_api; extern sai_object_id_t gSwitchId; extern sai_object_id_t gVirtualRouterId; extern PortsOrch* gPortsOrch; +extern sai_switch_api_t* sai_switch_api; extern Directory gDirectory; const map session_type_map = @@ -57,6 +60,7 @@ BfdOrch::BfdOrch(DBConnector *db, string tableName, TableConnector stateDbBfdSes m_bfdStateNotificationConsumer = new swss::NotificationConsumer(notificationsDb, "NOTIFICATIONS"); auto bfdStateNotificatier = new Notifier(m_bfdStateNotificationConsumer, this, "BFD_STATE_NOTIFICATIONS"); Orch::addExecutor(bfdStateNotificatier); + register_state_change_notif = false; } BfdOrch::~BfdOrch(void) @@ -152,8 +156,52 @@ void BfdOrch::doTask(NotificationConsumer &consumer) } } +bool BfdOrch::register_bfd_state_change_notification(void) +{ + sai_attribute_t attr; + sai_status_t status; + sai_attr_capability_t capability; + + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_SWITCH, + SAI_SWITCH_ATTR_BFD_SESSION_STATE_CHANGE_NOTIFY, + &capability); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Unable to query the BFD change notification capability"); + return false; + } + + if (!capability.set_implemented) + { + SWSS_LOG_ERROR("BFD register change notification not supported"); + return false; + } + + attr.id = SAI_SWITCH_ATTR_BFD_SESSION_STATE_CHANGE_NOTIFY; + attr.value.ptr = (void *)on_bfd_session_state_change; + + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to register BFD notification handler"); + return false; + } + return true; +} + bool BfdOrch::create_bfd_session(const string& key, const vector& data) { + if (!register_state_change_notif) + { + if (!register_bfd_state_change_notification()) + { + SWSS_LOG_ERROR("BFD session for %s cannot be created", key.c_str()); + return false; + } + register_state_change_notif = true; + } if (bfd_session_map.find(key) != bfd_session_map.end()) { SWSS_LOG_ERROR("BFD session for %s already exists", key.c_str()); @@ -369,6 +417,12 @@ bool BfdOrch::create_bfd_session(const string& key, const vectorcreate_bfd_session(&bfd_session_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); + + if (status != SAI_STATUS_SUCCESS) + { + status = retry_create_bfd_session(bfd_session_id, attrs); + } + if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to create bfd session %s, rv:%d", key.c_str(), status); @@ -392,6 +446,38 @@ bool BfdOrch::create_bfd_session(const string& key, const vector &attrs) +{ + for (uint32_t attr_idx = 0; attr_idx < (uint32_t)attrs.size(); attr_idx++) + { + if (attrs[attr_idx].id == SAI_BFD_SESSION_ATTR_UDP_SRC_PORT) + { + auto old_num = attrs[attr_idx].value.u32; + attrs[attr_idx].value.u32 = bfd_src_port(); + SWSS_LOG_WARN("BFD create using port number %d failed. Retrying with port number %d", + old_num, attrs[attr_idx].value.u32); + return; + } + } +} + +sai_status_t BfdOrch::retry_create_bfd_session(sai_object_id_t &bfd_session_id, vector attrs) +{ + sai_status_t status = SAI_STATUS_FAILURE; + + for (int retry = 0; retry < NUM_BFD_SRCPORT_RETRIES; retry++) + { + update_port_number(attrs); + status = sai_bfd_api->create_bfd_session(&bfd_session_id, gSwitchId, + (uint32_t)attrs.size(), attrs.data()); + if (status == SAI_STATUS_SUCCESS) + { + return status; + } + } + return status; +} + bool BfdOrch::remove_bfd_session(const string& key) { if (bfd_session_map.find(key) == bfd_session_map.end()) @@ -440,3 +526,4 @@ uint32_t BfdOrch::bfd_src_port(void) return (port++); } + diff --git a/orchagent/bfdorch.h b/orchagent/bfdorch.h index 6be1f8deae..4a0cb9edfb 100644 --- a/orchagent/bfdorch.h +++ b/orchagent/bfdorch.h @@ -26,12 +26,17 @@ class BfdOrch: public Orch, public Subject uint32_t bfd_gen_id(void); uint32_t bfd_src_port(void); + bool register_bfd_state_change_notification(void); + void update_port_number(std::vector &attrs); + sai_status_t retry_create_bfd_session(sai_object_id_t &bfd_session_id, vector attrs); + std::map bfd_session_map; std::map bfd_session_lookup; swss::Table m_stateBfdSessionTable; swss::NotificationConsumer* m_bfdStateNotificationConsumer; + bool register_state_change_notif; }; #endif /* SWSS_BFDORCH_H */ diff --git a/orchagent/bufferorch.cpp b/orchagent/bufferorch.cpp index e7204344d5..f9b91e7a16 100644 --- a/orchagent/bufferorch.cpp +++ b/orchagent/bufferorch.cpp @@ -48,7 +48,11 @@ BufferOrch::BufferOrch(DBConnector *applDb, DBConnector *confDb, DBConnector *st m_flexCounterTable(new ProducerTable(m_flexCounterDb.get(), FLEX_COUNTER_TABLE)), m_flexCounterGroupTable(new ProducerTable(m_flexCounterDb.get(), FLEX_COUNTER_GROUP_TABLE)), m_countersDb(new DBConnector("COUNTERS_DB", 0)), - m_stateBufferMaximumValueTable(stateDb, STATE_BUFFER_MAXIMUM_VALUE_TABLE) + m_stateBufferMaximumValueTable(stateDb, STATE_BUFFER_MAXIMUM_VALUE_TABLE), + m_ingressZeroBufferPool(SAI_NULL_OBJECT_ID), + m_egressZeroBufferPool(SAI_NULL_OBJECT_ID), + m_ingressZeroPoolRefCount(0), + m_egressZeroPoolRefCount(0) { SWSS_LOG_ENTER(); initTableHandlers(); @@ -310,6 +314,65 @@ const object_reference_map &BufferOrch::getBufferPoolNameOidMap(void) return *m_buffer_type_maps[APP_BUFFER_POOL_TABLE_NAME]; } +void BufferOrch::lockZeroBufferPool(bool ingress) +{ + if (ingress) + m_ingressZeroPoolRefCount++; + else + m_egressZeroPoolRefCount++; +} + +void BufferOrch::unlockZeroBufferPool(bool ingress) +{ + sai_object_id_t pool = SAI_NULL_OBJECT_ID; + if (ingress) + { + if (--m_ingressZeroPoolRefCount <= 0) + { + pool = m_ingressZeroBufferPool; + m_ingressZeroBufferPool = SAI_NULL_OBJECT_ID; + } + } + else + { + if (--m_egressZeroPoolRefCount <= 0) + { + pool = m_egressZeroBufferPool; + m_egressZeroBufferPool = SAI_NULL_OBJECT_ID; + } + } + + if (pool != SAI_NULL_OBJECT_ID) + { + auto sai_status = sai_buffer_api->remove_buffer_pool(pool); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to remove buffer pool, rv:%d", sai_status); + task_process_status handle_status = handleSaiRemoveStatus(SAI_API_BUFFER, sai_status); + if (handle_status != task_process_status::task_success) + { + return; + } + } + else + { + SWSS_LOG_NOTICE("Zero buffer pool has been successfully removed"); + } + } +} + +void BufferOrch::setZeroBufferPool(bool ingress, sai_object_id_t pool) +{ + if (ingress) + { + m_ingressZeroBufferPool = pool; + } + else + { + m_egressZeroBufferPool = pool; + } +} + task_process_status BufferOrch::processBufferPool(KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); @@ -318,14 +381,32 @@ task_process_status BufferOrch::processBufferPool(KeyOpFieldsValuesTuple &tuple) string map_type_name = APP_BUFFER_POOL_TABLE_NAME; string object_name = kfvKey(tuple); string op = kfvOp(tuple); + sai_buffer_pool_type_t pool_direction = SAI_BUFFER_POOL_TYPE_INGRESS; + bool creating_zero_pool = false; SWSS_LOG_DEBUG("object name:%s", object_name.c_str()); if (m_buffer_type_maps[map_type_name]->find(object_name) != m_buffer_type_maps[map_type_name]->end()) { sai_object = (*(m_buffer_type_maps[map_type_name]))[object_name].m_saiObjectId; SWSS_LOG_DEBUG("found existing object:%s of type:%s", object_name.c_str(), map_type_name.c_str()); + if ((*(m_buffer_type_maps[map_type_name]))[object_name].m_pendingRemove && op == SET_COMMAND) + { + SWSS_LOG_NOTICE("Entry %s %s is pending remove, need retry", map_type_name.c_str(), object_name.c_str()); + return task_process_status::task_need_retry; + } } SWSS_LOG_DEBUG("processing command:%s", op.c_str()); + if (object_name == "ingress_zero_pool") + { + creating_zero_pool = true; + pool_direction = SAI_BUFFER_POOL_TYPE_INGRESS; + } + else if (object_name == "egress_zero_pool") + { + creating_zero_pool = true; + pool_direction = SAI_BUFFER_POOL_TYPE_EGRESS; + } + if (op == SET_COMMAND) { vector attribs; @@ -372,6 +453,11 @@ task_process_status BufferOrch::processBufferPool(KeyOpFieldsValuesTuple &tuple) return task_process_status::task_invalid_entry; } attr.id = SAI_BUFFER_POOL_ATTR_TYPE; + if (creating_zero_pool && pool_direction != static_cast(attr.value.u32)) + { + SWSS_LOG_ERROR("Wrong pool direction for pool %s", object_name.c_str()); + return task_process_status::task_invalid_entry; + } attribs.push_back(attr); } else if (field == buffer_pool_mode_field_name) @@ -437,18 +523,54 @@ task_process_status BufferOrch::processBufferPool(KeyOpFieldsValuesTuple &tuple) } else { - sai_status = sai_buffer_api->create_buffer_pool(&sai_object, gSwitchId, (uint32_t)attribs.size(), attribs.data()); - if (SAI_STATUS_SUCCESS != sai_status) + if (creating_zero_pool) { - SWSS_LOG_ERROR("Failed to create buffer pool %s with type %s, rv:%d", object_name.c_str(), map_type_name.c_str(), sai_status); - task_process_status handle_status = handleSaiCreateStatus(SAI_API_BUFFER, sai_status); - if (handle_status != task_process_status::task_success) + if (pool_direction == SAI_BUFFER_POOL_TYPE_INGRESS) { - return handle_status; + sai_object = m_ingressZeroBufferPool; + } + else if (pool_direction == SAI_BUFFER_POOL_TYPE_EGRESS) + { + sai_object = m_egressZeroBufferPool; } } + + if (SAI_NULL_OBJECT_ID == sai_object) + { + sai_status = sai_buffer_api->create_buffer_pool(&sai_object, gSwitchId, (uint32_t)attribs.size(), attribs.data()); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to create buffer pool %s with type %s, rv:%d", object_name.c_str(), map_type_name.c_str(), sai_status); + task_process_status handle_status = handleSaiCreateStatus(SAI_API_BUFFER, sai_status); + if (handle_status != task_process_status::task_success) + { + return handle_status; + } + } + + SWSS_LOG_NOTICE("Created buffer pool %s with type %s", object_name.c_str(), map_type_name.c_str()); + } + else + { + SWSS_LOG_NOTICE("No need to create buffer pool %s since it has been created", object_name.c_str()); + } + + if (creating_zero_pool) + { + if (pool_direction == SAI_BUFFER_POOL_TYPE_INGRESS) + { + m_ingressZeroPoolRefCount++; + m_ingressZeroBufferPool = sai_object; + } + else if (pool_direction == SAI_BUFFER_POOL_TYPE_EGRESS) + { + m_egressZeroPoolRefCount++; + m_egressZeroBufferPool = sai_object; + } + } + (*(m_buffer_type_maps[map_type_name]))[object_name].m_saiObjectId = sai_object; - SWSS_LOG_NOTICE("Created buffer pool %s with type %s", object_name.c_str(), map_type_name.c_str()); + (*(m_buffer_type_maps[map_type_name]))[object_name].m_pendingRemove = false; // Here we take the PFC watchdog approach to update the COUNTERS_DB metadata (e.g., PFC_WD_DETECTION_TIME per queue) // at initialization (creation and registration phase) // Specifically, we push the buffer pool name to oid mapping upon the creation of the oid @@ -463,6 +585,7 @@ task_process_status BufferOrch::processBufferPool(KeyOpFieldsValuesTuple &tuple) { auto hint = objectReferenceInfo(m_buffer_type_maps, map_type_name, object_name); SWSS_LOG_NOTICE("Can't remove object %s due to being referenced (%s)", object_name.c_str(), hint.c_str()); + (*(m_buffer_type_maps[map_type_name]))[object_name].m_pendingRemove = true; return task_process_status::task_need_retry; } @@ -470,18 +593,40 @@ task_process_status BufferOrch::processBufferPool(KeyOpFieldsValuesTuple &tuple) if (SAI_NULL_OBJECT_ID != sai_object) { clearBufferPoolWatermarkCounterIdList(sai_object); - sai_status = sai_buffer_api->remove_buffer_pool(sai_object); - if (SAI_STATUS_SUCCESS != sai_status) + bool remove = true; + if (sai_object == m_ingressZeroBufferPool) { - SWSS_LOG_ERROR("Failed to remove buffer pool %s with type %s, rv:%d", object_name.c_str(), map_type_name.c_str(), sai_status); - task_process_status handle_status = handleSaiRemoveStatus(SAI_API_BUFFER, sai_status); - if (handle_status != task_process_status::task_success) + if (--m_ingressZeroPoolRefCount > 0) + remove = false; + else + m_ingressZeroBufferPool = SAI_NULL_OBJECT_ID; + } + else if (sai_object == m_egressZeroBufferPool) + { + if (--m_egressZeroPoolRefCount > 0) + remove = false; + else + m_egressZeroBufferPool = SAI_NULL_OBJECT_ID; + } + if (remove) + { + sai_status = sai_buffer_api->remove_buffer_pool(sai_object); + if (SAI_STATUS_SUCCESS != sai_status) { - return handle_status; + SWSS_LOG_ERROR("Failed to remove buffer pool %s with type %s, rv:%d", object_name.c_str(), map_type_name.c_str(), sai_status); + task_process_status handle_status = handleSaiRemoveStatus(SAI_API_BUFFER, sai_status); + if (handle_status != task_process_status::task_success) + { + return handle_status; + } } + SWSS_LOG_NOTICE("Removed buffer pool %s with type %s", object_name.c_str(), map_type_name.c_str()); + } + else + { + SWSS_LOG_NOTICE("Will not remove buffer pool %s since it is still referenced", object_name.c_str()); } } - SWSS_LOG_NOTICE("Removed buffer pool %s with type %s", object_name.c_str(), map_type_name.c_str()); auto it_to_delete = (m_buffer_type_maps[map_type_name])->find(object_name); (m_buffer_type_maps[map_type_name])->erase(it_to_delete); m_countersDb->hdel(COUNTERS_BUFFER_POOL_NAME_MAP, object_name); @@ -509,6 +654,11 @@ task_process_status BufferOrch::processBufferProfile(KeyOpFieldsValuesTuple &tup { sai_object = (*(m_buffer_type_maps[map_type_name]))[object_name].m_saiObjectId; SWSS_LOG_DEBUG("found existing object:%s of type:%s", object_name.c_str(), map_type_name.c_str()); + if ((*(m_buffer_type_maps[map_type_name]))[object_name].m_pendingRemove && op == SET_COMMAND) + { + SWSS_LOG_NOTICE("Entry %s %s is pending remove, need retry", map_type_name.c_str(), object_name.c_str()); + return task_process_status::task_need_retry; + } } SWSS_LOG_DEBUG("processing command:%s", op.c_str()); if (op == SET_COMMAND) @@ -523,13 +673,6 @@ task_process_status BufferOrch::processBufferProfile(KeyOpFieldsValuesTuple &tup sai_attribute_t attr; if (field == buffer_pool_field_name) { - if (SAI_NULL_OBJECT_ID != sai_object) - { - // We should skip the profile's pool name because it's create only when setting a profile's attribute. - SWSS_LOG_INFO("Skip setting buffer profile's pool %s for profile %s", value.c_str(), object_name.c_str()); - continue; - } - sai_object_id_t sai_pool; ref_resolve_status resolve_result = resolveFieldRefValue(m_buffer_type_maps, buffer_pool_field_name, buffer_to_ref_table_map.at(buffer_pool_field_name), @@ -656,6 +799,7 @@ task_process_status BufferOrch::processBufferProfile(KeyOpFieldsValuesTuple &tup } } (*(m_buffer_type_maps[map_type_name]))[object_name].m_saiObjectId = sai_object; + (*(m_buffer_type_maps[map_type_name]))[object_name].m_pendingRemove = false; SWSS_LOG_NOTICE("Created buffer profile %s with type %s", object_name.c_str(), map_type_name.c_str()); } @@ -668,6 +812,7 @@ task_process_status BufferOrch::processBufferProfile(KeyOpFieldsValuesTuple &tup { auto hint = objectReferenceInfo(m_buffer_type_maps, map_type_name, object_name); SWSS_LOG_NOTICE("Can't remove object %s due to being referenced (%s)", object_name.c_str(), hint.c_str()); + (*(m_buffer_type_maps[map_type_name]))[object_name].m_pendingRemove = true; return task_process_status::task_need_retry; } diff --git a/orchagent/bufferorch.h b/orchagent/bufferorch.h index 05fdd7917f..24af140b4a 100644 --- a/orchagent/bufferorch.h +++ b/orchagent/bufferorch.h @@ -37,6 +37,14 @@ class BufferOrch : public Orch static type_map m_buffer_type_maps; void generateBufferPoolWatermarkCounterIdList(void); const object_reference_map &getBufferPoolNameOidMap(void); + sai_object_id_t getZeroBufferPool(bool ingress) + { + return ingress ? m_ingressZeroBufferPool : m_egressZeroBufferPool; + } + + void lockZeroBufferPool(bool ingress); + void unlockZeroBufferPool(bool ingress); + void setZeroBufferPool(bool direction, sai_object_id_t pool); private: typedef task_process_status (BufferOrch::*buffer_table_handler)(KeyOpFieldsValuesTuple &tuple); @@ -71,6 +79,11 @@ class BufferOrch : public Orch unique_ptr m_countersDb; bool m_isBufferPoolWatermarkCounterIdListGenerated = false; + + sai_object_id_t m_ingressZeroBufferPool; + sai_object_id_t m_egressZeroBufferPool; + int m_ingressZeroPoolRefCount; + int m_egressZeroPoolRefCount; }; #endif /* SWSS_BUFFORCH_H */ diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index 7895bc38a4..65dad8e98c 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -66,6 +66,26 @@ const map crmResSaiAvailAttrMap = { CrmResourceType::CRM_IPMC_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_IPMC_ENTRY}, { CrmResourceType::CRM_SNAT_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_SNAT_ENTRY }, { CrmResourceType::CRM_DNAT_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_DNAT_ENTRY }, +}; + +const map crmResSaiObjAttrMap = +{ + { CrmResourceType::CRM_IPV4_ROUTE, SAI_OBJECT_TYPE_ROUTE_ENTRY }, + { CrmResourceType::CRM_IPV6_ROUTE, SAI_OBJECT_TYPE_ROUTE_ENTRY }, + { CrmResourceType::CRM_IPV4_NEXTHOP, SAI_OBJECT_TYPE_NULL }, + { CrmResourceType::CRM_IPV6_NEXTHOP, SAI_OBJECT_TYPE_NULL }, + { CrmResourceType::CRM_IPV4_NEIGHBOR, SAI_OBJECT_TYPE_NEIGHBOR_ENTRY }, + { CrmResourceType::CRM_IPV6_NEIGHBOR, SAI_OBJECT_TYPE_NEIGHBOR_ENTRY }, + { CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER, SAI_OBJECT_TYPE_NULL }, + { CrmResourceType::CRM_NEXTHOP_GROUP, SAI_OBJECT_TYPE_NEXT_HOP_GROUP }, + { CrmResourceType::CRM_ACL_TABLE, SAI_OBJECT_TYPE_NULL }, + { CrmResourceType::CRM_ACL_GROUP, SAI_OBJECT_TYPE_NULL }, + { CrmResourceType::CRM_ACL_ENTRY, SAI_OBJECT_TYPE_NULL }, + { CrmResourceType::CRM_ACL_COUNTER, SAI_OBJECT_TYPE_NULL }, + { CrmResourceType::CRM_FDB_ENTRY, SAI_OBJECT_TYPE_FDB_ENTRY }, + { CrmResourceType::CRM_IPMC_ENTRY, SAI_OBJECT_TYPE_NULL}, + { CrmResourceType::CRM_SNAT_ENTRY, SAI_OBJECT_TYPE_NULL }, + { CrmResourceType::CRM_DNAT_ENTRY, SAI_OBJECT_TYPE_NULL }, { CrmResourceType::CRM_MPLS_INSEG, SAI_OBJECT_TYPE_INSEG_ENTRY }, { CrmResourceType::CRM_MPLS_NEXTHOP, SAI_OBJECT_TYPE_NEXT_HOP }, { CrmResourceType::CRM_SRV6_MY_SID_ENTRY, SAI_OBJECT_TYPE_MY_SID_ENTRY }, @@ -73,6 +93,22 @@ const map crmResSaiAvailAttrMap = { CrmResourceType::CRM_NEXTHOP_GROUP_MAP, SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MAP }, }; +const map crmResAddrFamilyAttrMap = +{ + { CrmResourceType::CRM_IPV4_ROUTE, SAI_ROUTE_ENTRY_ATTR_IP_ADDR_FAMILY }, + { CrmResourceType::CRM_IPV6_ROUTE, SAI_ROUTE_ENTRY_ATTR_IP_ADDR_FAMILY }, + { CrmResourceType::CRM_IPV4_NEIGHBOR, SAI_NEIGHBOR_ENTRY_ATTR_IP_ADDR_FAMILY }, + { CrmResourceType::CRM_IPV6_NEIGHBOR, SAI_NEIGHBOR_ENTRY_ATTR_IP_ADDR_FAMILY }, +}; + +const map crmResAddrFamilyValMap = +{ + { CrmResourceType::CRM_IPV4_ROUTE, SAI_IP_ADDR_FAMILY_IPV4 }, + { CrmResourceType::CRM_IPV6_ROUTE, SAI_IP_ADDR_FAMILY_IPV6 }, + { CrmResourceType::CRM_IPV4_NEIGHBOR, SAI_IP_ADDR_FAMILY_IPV4 }, + { CrmResourceType::CRM_IPV6_NEIGHBOR, SAI_IP_ADDR_FAMILY_IPV6 }, +}; + const map crmThreshTypeResMap = { { "ipv4_route_threshold_type", CrmResourceType::CRM_IPV4_ROUTE }, @@ -306,7 +342,11 @@ void CrmOrch::handleSetCommand(const string& key, const vector& auto resourceType = crmThreshTypeResMap.at(field); auto thresholdType = crmThreshTypeMap.at(value); - m_resourcesMap.at(resourceType).thresholdType = thresholdType; + if (m_resourcesMap.at(resourceType).thresholdType != thresholdType) + { + m_resourcesMap.at(resourceType).thresholdType = thresholdType; + m_resourcesMap.at(resourceType).exceededLogCounter = 0; + } } else if (crmThreshLowResMap.find(field) != crmThreshLowResMap.end()) { @@ -464,6 +504,74 @@ void CrmOrch::doTask(SelectableTimer &timer) checkCrmThresholds(); } +bool CrmOrch::getResAvailability(CrmResourceType type, CrmResourceEntry &res) +{ + sai_attribute_t attr; + uint64_t availCount = 0; + sai_status_t status = SAI_STATUS_SUCCESS; + + sai_object_type_t objType = crmResSaiObjAttrMap.at(type); + + if (objType != SAI_OBJECT_TYPE_NULL) + { + uint32_t attrCount = 0; + + if ((type == CrmResourceType::CRM_IPV4_ROUTE) || (type == CrmResourceType::CRM_IPV6_ROUTE) || + (type == CrmResourceType::CRM_IPV4_NEIGHBOR) || (type == CrmResourceType::CRM_IPV6_NEIGHBOR)) + { + attr.id = crmResAddrFamilyAttrMap.at(type); + attr.value.s32 = crmResAddrFamilyValMap.at(type); + attrCount = 1; + } + else if (type == CrmResourceType::CRM_MPLS_NEXTHOP) + { + attr.id = SAI_NEXT_HOP_ATTR_TYPE; + attr.value.s32 = SAI_NEXT_HOP_TYPE_MPLS; + attrCount = 1; + } + else if (type == CrmResourceType::CRM_SRV6_NEXTHOP) + { + attr.id = SAI_NEXT_HOP_ATTR_TYPE; + attr.value.s32 = SAI_NEXT_HOP_TYPE_SRV6_SIDLIST; + attrCount = 1; + } + + status = sai_object_type_get_availability(gSwitchId, objType, attrCount, &attr, &availCount); + } + + if ((status != SAI_STATUS_SUCCESS) || (objType == SAI_OBJECT_TYPE_NULL)) + { + if (crmResSaiAvailAttrMap.find(type) != crmResSaiAvailAttrMap.end()) + { + attr.id = crmResSaiAvailAttrMap.at(type); + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + } + + if ((status == SAI_STATUS_NOT_SUPPORTED) || + (status == SAI_STATUS_NOT_IMPLEMENTED) || + SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || + SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) + { + // mark unsupported resources + res.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; + SWSS_LOG_NOTICE("CRM resource %s not supported", crmResTypeNameMap.at(type).c_str()); + return false; + } + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get availability counter for %s CRM resourse", crmResTypeNameMap.at(type).c_str()); + return false; + } + + availCount = attr.value.u32; + } + + res.countersMap[CRM_COUNTERS_TABLE_KEY].availableCounter = static_cast(availCount); + + return true; +} + void CrmOrch::getResAvailableCounters() { SWSS_LOG_ENTER(); @@ -490,33 +598,13 @@ void CrmOrch::getResAvailableCounters() case CrmResourceType::CRM_IPMC_ENTRY: case CrmResourceType::CRM_SNAT_ENTRY: case CrmResourceType::CRM_DNAT_ENTRY: + case CrmResourceType::CRM_MPLS_INSEG: + case CrmResourceType::CRM_NEXTHOP_GROUP_MAP: + case CrmResourceType::CRM_SRV6_MY_SID_ENTRY: + case CrmResourceType::CRM_MPLS_NEXTHOP: + case CrmResourceType::CRM_SRV6_NEXTHOP: { - sai_attribute_t attr; - attr.id = crmResSaiAvailAttrMap.at(res.first); - - sai_status_t status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); - if (status != SAI_STATUS_SUCCESS) - { - if ((status == SAI_STATUS_NOT_SUPPORTED) || - (status == SAI_STATUS_NOT_IMPLEMENTED) || - SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || - SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) - { - // mark unsupported resources - res.second.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; - SWSS_LOG_NOTICE("Switch attribute %u not supported", attr.id); - break; - } - SWSS_LOG_ERROR("Failed to get switch attribute %u , rv:%d", attr.id, status); - 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; - + getResAvailability(res.first, res.second); break; } @@ -578,119 +666,6 @@ void CrmOrch::getResAvailableCounters() break; } - case CrmResourceType::CRM_MPLS_INSEG: - case CrmResourceType::CRM_NEXTHOP_GROUP_MAP: - { - sai_object_type_t objType = static_cast(crmResSaiAvailAttrMap.at(res.first)); - uint64_t availCount = 0; - sai_status_t status = sai_object_type_get_availability(gSwitchId, objType, 0, nullptr, &availCount); - if (status != SAI_STATUS_SUCCESS) - { - if ((status == SAI_STATUS_NOT_SUPPORTED) || - (status == SAI_STATUS_NOT_IMPLEMENTED) || - SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || - SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) - { - // mark unsupported resources - res.second.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; - SWSS_LOG_NOTICE("CRM Resource %s not supported", crmResTypeNameMap.at(res.first).c_str()); - break; - } - SWSS_LOG_ERROR("Failed to get availability for object_type %u , rv:%d", objType, status); - break; - } - - res.second.countersMap[CRM_COUNTERS_TABLE_KEY].availableCounter = static_cast(availCount); - - break; - } - - case CrmResourceType::CRM_MPLS_NEXTHOP: - { - sai_object_type_t objType = static_cast(crmResSaiAvailAttrMap.at(res.first)); - sai_attribute_t attr; - uint64_t availCount = 0; - - attr.id = SAI_NEXT_HOP_ATTR_TYPE; - attr.value.s32 = SAI_NEXT_HOP_TYPE_MPLS; - sai_status_t status = sai_object_type_get_availability(gSwitchId, objType, 1, &attr, &availCount); - if (status != SAI_STATUS_SUCCESS) - { - if ((status == SAI_STATUS_NOT_SUPPORTED) || - (status == SAI_STATUS_NOT_IMPLEMENTED) || - SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || - SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) - { - // mark unsupported resources - res.second.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; - SWSS_LOG_NOTICE("CRM Resource %s not supported", crmResTypeNameMap.at(res.first).c_str()); - break; - } - SWSS_LOG_ERROR("Failed to get availability for object_type %u , rv:%d", objType, status); - break; - } - - res.second.countersMap[CRM_COUNTERS_TABLE_KEY].availableCounter = static_cast(availCount); - - break; - } - - case CrmResourceType::CRM_SRV6_MY_SID_ENTRY: - { - sai_object_type_t objType = static_cast(crmResSaiAvailAttrMap.at(res.first)); - uint64_t availCount = 0; - sai_status_t status = sai_object_type_get_availability(gSwitchId, objType, 0, nullptr, &availCount); - if (status != SAI_STATUS_SUCCESS) - { - if ((status == SAI_STATUS_NOT_SUPPORTED) || - (status == SAI_STATUS_NOT_IMPLEMENTED) || - SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || - SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) - { - // mark unsupported resources - res.second.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; - SWSS_LOG_NOTICE("CRM Resource %s not supported", crmResTypeNameMap.at(res.first).c_str()); - break; - } - SWSS_LOG_ERROR("Failed to get availability for object_type %u , rv:%d", objType, status); - break; - } - - res.second.countersMap[CRM_COUNTERS_TABLE_KEY].availableCounter = static_cast(availCount); - - break; - } - - case CrmResourceType::CRM_SRV6_NEXTHOP: - { - sai_object_type_t objType = static_cast(crmResSaiAvailAttrMap.at(res.first)); - sai_attribute_t attr; - uint64_t availCount = 0; - - attr.id = SAI_NEXT_HOP_ATTR_TYPE; - attr.value.s32 = SAI_NEXT_HOP_TYPE_SRV6_SIDLIST; - sai_status_t status = sai_object_type_get_availability(gSwitchId, objType, 1, &attr, &availCount); - if (status != SAI_STATUS_SUCCESS) - { - if ((status == SAI_STATUS_NOT_SUPPORTED) || - (status == SAI_STATUS_NOT_IMPLEMENTED) || - SAI_STATUS_IS_ATTR_NOT_SUPPORTED(status) || - SAI_STATUS_IS_ATTR_NOT_IMPLEMENTED(status)) - { - // mark unsupported resources - res.second.resStatus = CrmResourceStatus::CRM_RES_NOT_SUPPORTED; - SWSS_LOG_NOTICE("CRM Resource %s not supported", crmResTypeNameMap.at(res.first).c_str()); - break; - } - SWSS_LOG_ERROR("Failed to get availability for object_type %u , rv:%d", objType, status); - break; - } - - res.second.countersMap[CRM_COUNTERS_TABLE_KEY].availableCounter = static_cast(availCount); - - break; - } - default: SWSS_LOG_ERROR("Failed to get CRM resource type %u. Unknown resource type.\n", static_cast(res.first)); return; diff --git a/orchagent/crmorch.h b/orchagent/crmorch.h index 345caa2cf6..f63e2a31c2 100644 --- a/orchagent/crmorch.h +++ b/orchagent/crmorch.h @@ -98,6 +98,7 @@ class CrmOrch : public Orch void doTask(Consumer &consumer); void handleSetCommand(const std::string& key, const std::vector& data); void doTask(swss::SelectableTimer &timer); + bool getResAvailability(CrmResourceType type, CrmResourceEntry &res); void getResAvailableCounters(); void updateCrmCountersTable(); void checkCrmThresholds(); diff --git a/orchagent/debugcounterorch.cpp b/orchagent/debugcounterorch.cpp index 25d0b94589..ed27f400d4 100644 --- a/orchagent/debugcounterorch.cpp +++ b/orchagent/debugcounterorch.cpp @@ -5,6 +5,7 @@ #include "schema.h" #include "drop_counter.h" #include +#include "observer.h" using std::string; using std::unordered_map; @@ -34,6 +35,8 @@ DebugCounterOrch::DebugCounterOrch(DBConnector *db, const vector& table_ { SWSS_LOG_ENTER(); publishDropCounterCapabilities(); + + gPortsOrch->attach(this); } DebugCounterOrch::~DebugCounterOrch(void) @@ -41,6 +44,52 @@ DebugCounterOrch::~DebugCounterOrch(void) SWSS_LOG_ENTER(); } +void DebugCounterOrch::update(SubjectType type, void *cntx) +{ + SWSS_LOG_ENTER(); + + if (type == SUBJECT_TYPE_PORT_CHANGE) + { + if (!cntx) + { + SWSS_LOG_ERROR("cntx is NULL"); + return; + } + + PortUpdate *update = static_cast(cntx); + Port &port = update->port; + + if (update->add) + { + for (const auto& debug_counter: debug_counters) + { + DebugCounter *counter = debug_counter.second.get(); + auto counter_type = counter->getCounterType(); + auto counter_stat = counter->getDebugCounterSAIStat(); + auto flex_counter_type = getFlexCounterType(counter_type); + if (flex_counter_type == CounterType::PORT_DEBUG) + { + installDebugFlexCounters(counter_type, counter_stat, port.m_port_id); + } + } + } + else + { + for (const auto& debug_counter: debug_counters) + { + DebugCounter *counter = debug_counter.second.get(); + auto counter_type = counter->getCounterType(); + auto counter_stat = counter->getDebugCounterSAIStat(); + auto flex_counter_type = getFlexCounterType(counter_type); + if (flex_counter_type == CounterType::PORT_DEBUG) + { + uninstallDebugFlexCounters(counter_type, counter_stat, port.m_port_id); + } + } + } + } +} + // doTask processes updates from the consumer and modifies the state of the // following components: // 1) The ASIC, by creating, modifying, and deleting debug counters @@ -476,7 +525,8 @@ CounterType DebugCounterOrch::getFlexCounterType(const string& counter_type) } void DebugCounterOrch::installDebugFlexCounters(const string& counter_type, - const string& counter_stat) + const string& counter_stat, + sai_object_id_t port_id) { SWSS_LOG_ENTER(); CounterType flex_counter_type = getFlexCounterType(counter_type); @@ -489,6 +539,14 @@ void DebugCounterOrch::installDebugFlexCounters(const string& counter_type, { for (auto const &curr : gPortsOrch->getAllPorts()) { + if (port_id != SAI_NULL_OBJECT_ID) + { + if (curr.second.m_port_id != port_id) + { + continue; + } + } + if (curr.second.m_type != Port::Type::PHY) { continue; @@ -503,7 +561,8 @@ void DebugCounterOrch::installDebugFlexCounters(const string& counter_type, } void DebugCounterOrch::uninstallDebugFlexCounters(const string& counter_type, - const string& counter_stat) + const string& counter_stat, + sai_object_id_t port_id) { SWSS_LOG_ENTER(); CounterType flex_counter_type = getFlexCounterType(counter_type); @@ -516,6 +575,14 @@ void DebugCounterOrch::uninstallDebugFlexCounters(const string& counter_type, { for (auto const &curr : gPortsOrch->getAllPorts()) { + if (port_id != SAI_NULL_OBJECT_ID) + { + if (curr.second.m_port_id != port_id) + { + continue; + } + } + if (curr.second.m_type != Port::Type::PHY) { continue; @@ -616,3 +683,6 @@ bool DebugCounterOrch::isDropReasonValid(const string& drop_reason) const return true; } + + + diff --git a/orchagent/debugcounterorch.h b/orchagent/debugcounterorch.h index e5b512c8e4..edfb5d98e0 100644 --- a/orchagent/debugcounterorch.h +++ b/orchagent/debugcounterorch.h @@ -10,6 +10,7 @@ #include "flex_counter_stat_manager.h" #include "debug_counter.h" #include "drop_counter.h" +#include "observer.h" extern "C" { #include "sai.h" @@ -17,9 +18,11 @@ extern "C" { #define DEBUG_COUNTER_FLEX_COUNTER_GROUP "DEBUG_COUNTER" +using DebugCounterMap = std::unordered_map>; + // DebugCounterOrch is an orchestrator for managing debug counters. It handles // the creation, deletion, and modification of debug counters. -class DebugCounterOrch: public Orch +class DebugCounterOrch: public Orch, public Observer { public: DebugCounterOrch(swss::DBConnector *db, const std::vector& table_names, int poll_interval); @@ -27,6 +30,7 @@ class DebugCounterOrch: public Orch void doTask(Consumer& consumer); + void update(SubjectType, void *cntx); private: // Debug Capability Reporting Functions void publishDropCounterCapabilities(); @@ -48,10 +52,12 @@ class DebugCounterOrch: public Orch CounterType getFlexCounterType(const std::string& counter_type) noexcept(false); void installDebugFlexCounters( const std::string& counter_type, - const std::string& counter_stat); + const std::string& counter_stat, + sai_object_id_t port_id = SAI_NULL_OBJECT_ID); void uninstallDebugFlexCounters( const std::string& counter_type, - const std::string& counter_stat); + const std::string& counter_stat, + sai_object_id_t port_id = SAI_NULL_OBJECT_ID); // Debug Counter Initialization Helper Functions std::string getDebugCounterType( @@ -83,7 +89,7 @@ class DebugCounterOrch: public Orch FlexCounterStatManager flex_counter_manager; - std::unordered_map> debug_counters; + DebugCounterMap debug_counters; // free_drop_counters are drop counters that have been created by a user // that do not have any drop reasons associated with them yet. Because diff --git a/orchagent/directory.h b/orchagent/directory.h index ecae1564a2..4d5857b18e 100644 --- a/orchagent/directory.h +++ b/orchagent/directory.h @@ -6,6 +6,7 @@ #include #include +#include #include #include diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index daab3ad52e..d4e0d8ffad 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -18,7 +18,6 @@ extern sai_fdb_api_t *sai_fdb_api; extern sai_object_id_t gSwitchId; -extern PortsOrch* gPortsOrch; extern CrmOrch * gCrmOrch; extern MlagOrch* gMlagOrch; extern Directory gDirectory; @@ -76,6 +75,7 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) string portName = port.m_alias; Port vlan; + oldFdbData.origin = FDB_ORIGIN_INVALID; if (!m_portsOrch->getPort(entry.bv_id, vlan)) { SWSS_LOG_NOTICE("FdbOrch notification: Failed to locate \ @@ -174,6 +174,107 @@ bool FdbOrch::storeFdbEntryState(const FdbUpdate& update) } } +/* +clears stateDb and decrements corresponding internal fdb counters +*/ +void FdbOrch::clearFdbEntry(const MacAddress& mac, + const sai_object_id_t& bv_id, + const string& port_alias) +{ + FdbUpdate update; + update.entry.mac = mac; + update.entry.bv_id = bv_id; + update.add = false; + + /* Fetch Vlan and decrement the counter */ + Port temp_vlan; + if (m_portsOrch->getPort(bv_id, temp_vlan)) + { + m_portsOrch->decrFdbCount(temp_vlan.m_alias, 1); + } + + /* Decrement port fdb_counter */ + m_portsOrch->decrFdbCount(port_alias, 1); + + /* Remove the FdbEntry from the internal cache, update state DB and CRM counter */ + storeFdbEntryState(update); + notify(SUBJECT_TYPE_FDB_CHANGE, &update); + + SWSS_LOG_INFO("FdbEntry removed from internal cache, MAC: %s , port: %s, BVID: 0x%" PRIx64, + mac.to_string().c_str(), port_alias.c_str(), bv_id); +} + +/* +Handles the SAI_FDB_EVENT_FLUSHED notification recieved from syncd +*/ +void FdbOrch::handleSyncdFlushNotif(const sai_object_id_t& bv_id, + const sai_object_id_t& bridge_port_id, + const MacAddress& mac) +{ + // Consolidated flush will have a zero mac + MacAddress flush_mac("00:00:00:00:00:00"); + + /* TODO: Read the SAI_FDB_FLUSH_ATTR_ENTRY_TYPE attr from the flush notif + and clear the entries accordingly, currently only non-static entries are flushed + */ + if (bridge_port_id == SAI_NULL_OBJECT_ID && bv_id == SAI_NULL_OBJECT_ID) + { + for (auto itr = m_entries.begin(); itr != m_entries.end();) + { + auto curr = itr++; + if (curr->second.type != "static" && (curr->first.mac == mac || mac == flush_mac)) + { + clearFdbEntry(curr->first.mac, curr->first.bv_id, curr->first.port_name); + } + } + } + else if (bv_id == SAI_NULL_OBJECT_ID) + { + /* FLUSH based on PORT */ + for (auto itr = m_entries.begin(); itr != m_entries.end();) + { + auto curr = itr++; + if (curr->second.bridge_port_id == bridge_port_id) + { + if (curr->second.type != "static" && (curr->first.mac == mac || mac == flush_mac)) + { + clearFdbEntry(curr->first.mac, curr->first.bv_id, curr->first.port_name); + } + } + } + } + else if (bridge_port_id == SAI_NULL_OBJECT_ID) + { + /* FLUSH based on BV_ID */ + for (auto itr = m_entries.begin(); itr != m_entries.end();) + { + auto curr = itr++; + if (curr->first.bv_id == bv_id) + { + if (curr->second.type != "static" && (curr->first.mac == mac || mac == flush_mac)) + { + clearFdbEntry(curr->first.mac, curr->first.bv_id, curr->first.port_name); + } + } + } + } + else + { + /* FLUSH based on port and VLAN */ + for (auto itr = m_entries.begin(); itr != m_entries.end();) + { + auto curr = itr++; + if (curr->first.bv_id == bv_id && curr->second.bridge_port_id == bridge_port_id) + { + if (curr->second.type != "static" && (curr->first.mac == mac || mac == flush_mac)) + { + clearFdbEntry(curr->first.mac, curr->first.bv_id, curr->first.port_name); + } + } + } + } +} + void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_object_id_t bridge_port_id) @@ -191,24 +292,29 @@ void FdbOrch::update(sai_fdb_event_t type, type, update.entry.mac.to_string().c_str(), entry->bv_id, bridge_port_id); - if (bridge_port_id && !m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) { if (type == SAI_FDB_EVENT_FLUSHED) { - /* In case of flush - can be ignored due to a race. - There are notifications about FDB FLUSH (syncd/sai_redis) on port, - which was already removed by orchagent as a result of - removeVlanMember action (removeBridgePort) */ + /* There are notifications about FDB FLUSH (syncd/sai_redis) on port, + which was already removed by orchagent as a result of removeVlanMember + action (removeBridgePort). But the internal cleanup of statedb and + internal counters is yet to be performed, thus continue + */ SWSS_LOG_INFO("Flush event: Failed to get port by bridge port ID 0x%" PRIx64 ".", bridge_port_id); - } else { SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%" PRIx64 ".", bridge_port_id); - + return; } + } + + if (entry->bv_id && + !m_portsOrch->getPort(entry->bv_id, vlan)) + { + SWSS_LOG_NOTICE("FdbOrch notification type %d: Failed to locate vlan port from bv_id 0x%" PRIx64, type, entry->bv_id); return; } @@ -218,12 +324,6 @@ void FdbOrch::update(sai_fdb_event_t type, { SWSS_LOG_INFO("Received LEARN event for bvid=0x%" PRIx64 "mac=%s port=0x%" PRIx64, entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); - if (!m_portsOrch->getPort(entry->bv_id, vlan)) - { - SWSS_LOG_ERROR("FdbOrch LEARN notification: Failed to locate vlan port from bv_id 0x%" PRIx64, entry->bv_id); - return; - } - // we already have such entries auto existing_entry = m_entries.find(update.entry); if (existing_entry != m_entries.end()) @@ -318,11 +418,6 @@ void FdbOrch::update(sai_fdb_event_t type, SWSS_LOG_INFO("Received AGE event for bvid=0x%" PRIx64 " mac=%s port=0x%" PRIx64, entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); - if (!m_portsOrch->getPort(entry->bv_id, vlan)) - { - SWSS_LOG_NOTICE("FdbOrch AGE notification: Failed to locate vlan port from bv_id 0x%" PRIx64, entry->bv_id); - } - auto existing_entry = m_entries.find(update.entry); // we don't have such entries if (existing_entry == m_entries.end()) @@ -456,12 +551,6 @@ void FdbOrch::update(sai_fdb_event_t type, SWSS_LOG_INFO("Received MOVE event for bvid=0x%" PRIx64 " mac=%s port=0x%" PRIx64, entry->bv_id, update.entry.mac.to_string().c_str(), bridge_port_id); - if (!m_portsOrch->getPort(entry->bv_id, vlan)) - { - SWSS_LOG_ERROR("FdbOrch MOVE notification: Failed to locate vlan port from bv_id 0x%" PRIx64, entry->bv_id); - return; - } - // We should already have such entry if (existing_entry == m_entries.end()) { @@ -475,6 +564,7 @@ void FdbOrch::update(sai_fdb_event_t type, } update.add = true; + update.entry.port_name = update.port.m_alias; if (!port_old.m_alias.empty()) { port_old.m_fdb_count--; @@ -498,80 +588,15 @@ void FdbOrch::update(sai_fdb_event_t type, bridge_port_id); string vlanName = "-"; - if (entry->bv_id) { - Port vlan; - - if (!m_portsOrch->getPort(entry->bv_id, vlan)) - { - SWSS_LOG_NOTICE("FdbOrch notification: Failed to locate vlan\ - port from bv_id 0x%" PRIx64, entry->bv_id); - return; - } + if (!vlan.m_alias.empty()) { vlanName = "Vlan" + to_string(vlan.m_vlan_info.vlan_id); } + SWSS_LOG_INFO("FDB Flush: [ %s , %s ] = { port: %s }", update.entry.mac.to_string().c_str(), + vlanName.c_str(), update.port.m_alias.c_str()); - if (bridge_port_id == SAI_NULL_OBJECT_ID && - entry->bv_id == SAI_NULL_OBJECT_ID) - { - SWSS_LOG_INFO("FDB Flush: [ %s , %s ] = { port: - }", - update.entry.mac.to_string().c_str(), vlanName.c_str()); - for (auto itr = m_entries.begin(); itr != m_entries.end();) - { - /* - TODO: here should only delete the dynamic fdb entries, - but unfortunately in structure FdbEntry currently have - no member to indicate the fdb entry type, - if there is static mac added, here will have issue. - */ - update.entry.mac = itr->first.mac; - update.entry.bv_id = itr->first.bv_id; - update.add = false; - itr++; - - storeFdbEntryState(update); - - notify(SUBJECT_TYPE_FDB_CHANGE, &update); - - } - } - else if (entry->bv_id == SAI_NULL_OBJECT_ID) - { - /* FLUSH based on port */ - SWSS_LOG_INFO("FDB Flush: [ %s , %s ] = { port: %s }", - update.entry.mac.to_string().c_str(), - vlanName.c_str(), update.port.m_alias.c_str()); - - for (auto itr = m_entries.begin(); itr != m_entries.end();) - { - auto next_item = std::next(itr); - if (itr->first.port_name == update.port.m_alias) - { - update.entry.mac = itr->first.mac; - update.entry.bv_id = itr->first.bv_id; - update.add = false; + handleSyncdFlushNotif(entry->bv_id, bridge_port_id, update.entry.mac); - storeFdbEntryState(update); - notify(SUBJECT_TYPE_FDB_CHANGE, &update); - } - itr = next_item; - } - } - else if (bridge_port_id == SAI_NULL_OBJECT_ID) - { - /* FLUSH based on VLAN - unsupported */ - SWSS_LOG_ERROR("Unsupported FDB Flush: [ %s , %s ] = { port: - }", - update.entry.mac.to_string().c_str(), - vlanName.c_str()); - - } - else - { - /* FLUSH based on port and VLAN - unsupported */ - SWSS_LOG_ERROR("Unsupported FDB Flush: [ %s , %s ] = { port: %s }", - update.entry.mac.to_string().c_str(), - vlanName.c_str(), update.port.m_alias.c_str()); - } break; } @@ -647,7 +672,7 @@ void FdbOrch::doTask(Consumer& consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->allPortsReady()) + if (!m_portsOrch->allPortsReady()) { return; } @@ -854,7 +879,7 @@ void FdbOrch::doTask(NotificationConsumer& consumer) { SWSS_LOG_ENTER(); - if (!gPortsOrch->allPortsReady()) + if (!m_portsOrch->allPortsReady()) { return; } @@ -890,7 +915,7 @@ void FdbOrch::doTask(NotificationConsumer& consumer) SWSS_LOG_ERROR("Receive wrong port to flush fdb!"); return; } - if (!gPortsOrch->getPort(alias, port)) + if (!m_portsOrch->getPort(alias, port)) { SWSS_LOG_ERROR("Get Port from port(%s) failed!", alias.c_str()); return; @@ -911,7 +936,7 @@ void FdbOrch::doTask(NotificationConsumer& consumer) SWSS_LOG_ERROR("Receive wrong vlan to flush fdb!"); return; } - if (!gPortsOrch->getPort(vlan, vlanPort)) + if (!m_portsOrch->getPort(vlan, vlanPort)) { SWSS_LOG_ERROR("Get Port from vlan(%s) failed!", vlan.c_str()); return; @@ -937,12 +962,12 @@ void FdbOrch::doTask(NotificationConsumer& consumer) SWSS_LOG_ERROR("Receive wrong port or vlan to flush fdb!"); return; } - if (!gPortsOrch->getPort(alias, port)) + if (!m_portsOrch->getPort(alias, port)) { SWSS_LOG_ERROR("Get Port from port(%s) failed!", alias.c_str()); return; } - if (!gPortsOrch->getPort(vlan, vlanPort)) + if (!m_portsOrch->getPort(vlan, vlanPort)) { SWSS_LOG_ERROR("Get Port from vlan(%s) failed!", vlan.c_str()); return; diff --git a/orchagent/fdborch.h b/orchagent/fdborch.h index 82611e686f..3e53dcd394 100644 --- a/orchagent/fdborch.h +++ b/orchagent/fdborch.h @@ -122,6 +122,9 @@ class FdbOrch: public Orch, public Subject, public Observer bool storeFdbEntryState(const FdbUpdate& update); void notifyTunnelOrch(Port& port); + + void clearFdbEntry(const MacAddress&, const sai_object_id_t&, const string&); + void handleSyncdFlushNotif(const sai_object_id_t&, const sai_object_id_t&, const MacAddress& ); }; #endif /* SWSS_FDBORCH_H */ diff --git a/orchagent/flex_counter/flex_counter_manager.cpp b/orchagent/flex_counter/flex_counter_manager.cpp index 71731e84d3..3e61289acd 100644 --- a/orchagent/flex_counter/flex_counter_manager.cpp +++ b/orchagent/flex_counter/flex_counter_manager.cpp @@ -44,6 +44,7 @@ const unordered_map FlexCounterManager::counter_id_field_lo { CounterType::ACL_COUNTER, ACL_COUNTER_ATTR_ID_LIST }, { CounterType::TUNNEL, TUNNEL_COUNTER_ID_LIST }, { CounterType::HOSTIF_TRAP, FLOW_COUNTER_ID_LIST }, + { CounterType::ROUTE, FLOW_COUNTER_ID_LIST }, }; FlexManagerDirectory g_FlexManagerDirectory; diff --git a/orchagent/flex_counter/flex_counter_manager.h b/orchagent/flex_counter/flex_counter_manager.h index 6e80feb8fb..6a997f28f7 100644 --- a/orchagent/flex_counter/flex_counter_manager.h +++ b/orchagent/flex_counter/flex_counter_manager.h @@ -31,6 +31,7 @@ enum class CounterType ACL_COUNTER, TUNNEL, HOSTIF_TRAP, + ROUTE, }; // FlexCounterManager allows users to manage a group of flex counters. diff --git a/orchagent/flex_counter/flow_counter_handler.cpp b/orchagent/flex_counter/flow_counter_handler.cpp index 89f621fe7b..27ba357a8e 100644 --- a/orchagent/flex_counter/flow_counter_handler.cpp +++ b/orchagent/flex_counter/flow_counter_handler.cpp @@ -47,3 +47,16 @@ void FlowCounterHandler::getGenericCounterStatIdList(std::unordered_set& counter_stats); + static bool queryRouteFlowCounterCapability(); }; diff --git a/orchagent/flex_counter/flowcounterrouteorch.cpp b/orchagent/flex_counter/flowcounterrouteorch.cpp new file mode 100644 index 0000000000..b82d66e27a --- /dev/null +++ b/orchagent/flex_counter/flowcounterrouteorch.cpp @@ -0,0 +1,991 @@ +#include "dbconnector.h" +#include "directory.h" +#include "flow_counter_handler.h" +#include "logger.h" +#include "routeorch.h" +#include "flowcounterrouteorch.h" +#include "schema.h" +#include "swssnet.h" +#include "table.h" +#include "vnetorch.h" + +#include + +extern Directory gDirectory; +extern RouteOrch* gRouteOrch; +extern size_t gMaxBulkSize; +extern sai_route_api_t* sai_route_api; +extern sai_object_id_t gVirtualRouterId; +extern sai_object_id_t gSwitchId; + +#define FLEX_COUNTER_UPD_INTERVAL 1 +#define FLOW_COUNTER_ROUTE_KEY "route" +#define FLOW_COUNTER_SUPPORT_FIELD "support" +#define ROUTE_PATTERN_MAX_MATCH_COUNT_FIELD "max_match_count" +#define ROUTE_PATTERN_DEFAULT_MAX_MATCH_COUNT 30 +#define ROUTE_FLOW_COUNTER_POLLING_INTERVAL_MS 10000 + +FlowCounterRouteOrch::FlowCounterRouteOrch(swss::DBConnector *db, const std::vector &tableNames): +Orch(db, tableNames), +mAsicDb(std::shared_ptr(new DBConnector("ASIC_DB", 0))), +mCounterDb(std::shared_ptr(new DBConnector("COUNTERS_DB", 0))), +mVidToRidTable(std::unique_ptr(new Table(mAsicDb.get(), "VIDTORID"))), +mPrefixToCounterTable(std::unique_ptr
(new Table(mCounterDb.get(), COUNTERS_ROUTE_NAME_MAP))), +mPrefixToPatternTable(std::unique_ptr
(new Table(mCounterDb.get(), COUNTERS_ROUTE_TO_PATTERN_MAP))), +mRouteFlowCounterMgr(ROUTE_FLOW_COUNTER_FLEX_COUNTER_GROUP, StatsMode::READ, ROUTE_FLOW_COUNTER_POLLING_INTERVAL_MS, false), +gRouteBulker(sai_route_api, gMaxBulkSize) +{ + SWSS_LOG_ENTER(); + initRouteFlowCounterCapability(); + + if (mRouteFlowCounterSupported) + { + auto intervT = timespec { .tv_sec = FLEX_COUNTER_UPD_INTERVAL , .tv_nsec = 0 }; + mFlexCounterUpdTimer = new SelectableTimer(intervT); + auto executorT = new ExecutableTimer(mFlexCounterUpdTimer, this, "FLEX_COUNTER_UPD_TIMER"); + Orch::addExecutor(executorT); + } +} + +FlowCounterRouteOrch::~FlowCounterRouteOrch() +{ + SWSS_LOG_ENTER(); +} + +void FlowCounterRouteOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + if (!gRouteOrch || !mRouteFlowCounterSupported) + { + return; + } + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + const auto &key = kfvKey(t); + const auto &op = kfvOp(t); + const auto &data = kfvFieldsValues(t); + if (op == SET_COMMAND) + { + size_t maxMatchCount = ROUTE_PATTERN_DEFAULT_MAX_MATCH_COUNT; + for (auto valuePair : data) + { + const auto &field = fvField(valuePair); + const auto &value = fvValue(valuePair); + if (field == ROUTE_PATTERN_MAX_MATCH_COUNT_FIELD) + { + maxMatchCount = (size_t)std::stoul(value); + if (maxMatchCount == 0) + { + SWSS_LOG_WARN("Max match count for route pattern cannot be 0, set it to default value 30"); + maxMatchCount = ROUTE_PATTERN_DEFAULT_MAX_MATCH_COUNT; + } + } + } + + addRoutePattern(key, maxMatchCount); + } + else if (op == DEL_COMMAND) + { + removeRoutePattern(key); + } + consumer.m_toSync.erase(it++); + } +} + +void FlowCounterRouteOrch::doTask(SelectableTimer &timer) +{ + SWSS_LOG_ENTER(); + SWSS_LOG_NOTICE("Add flex counters, pending in queue: %zu", mPendingAddToFlexCntr.size()); + string value; + std::string nameMapKey; + std::string pattern; + vector prefixToCounterMap; + vector prefixToPatternMap; + for (auto it = mPendingAddToFlexCntr.begin(); it != mPendingAddToFlexCntr.end(); ) + { + const auto& route_pattern = it->first; + auto vrf_id = route_pattern.vrf_id; + + for(auto inner_iter = it->second.begin(); inner_iter != it->second.end(); ) + { + const auto id = sai_serialize_object_id(inner_iter->second); + if (mVidToRidTable->hget("", id, value)) + { + auto ip_prefix = inner_iter->first; + SWSS_LOG_INFO("Registering %s, id %s", ip_prefix.to_string().c_str(), id.c_str()); + + std::unordered_set counter_stats; + FlowCounterHandler::getGenericCounterStatIdList(counter_stats); + mRouteFlowCounterMgr.setCounterIdList(inner_iter->second, CounterType::ROUTE, counter_stats); + + getRouteFlowCounterNameMapKey(vrf_id, ip_prefix, nameMapKey); + prefixToCounterMap.emplace_back(nameMapKey, id); + + getRouteFlowCounterNameMapKey(vrf_id, route_pattern.ip_prefix, pattern); + prefixToPatternMap.emplace_back(nameMapKey, pattern); + + updateRouterFlowCounterCache(route_pattern, ip_prefix, inner_iter->second, mBoundRouteCounters); + inner_iter = it->second.erase(inner_iter); + } + else + { + ++inner_iter; + } + } + + if (it->second.empty()) + { + it = mPendingAddToFlexCntr.erase(it); + } + else + { + ++it; + } + } + + if (!prefixToCounterMap.empty()) + { + mPrefixToCounterTable->set("", prefixToCounterMap); + } + + if (!prefixToPatternMap.empty()) + { + mPrefixToPatternTable->set("", prefixToPatternMap); + } + + if (mPendingAddToFlexCntr.empty()) + { + mFlexCounterUpdTimer->stop(); + } +} + +void FlowCounterRouteOrch::initRouteFlowCounterCapability() +{ + SWSS_LOG_ENTER(); + mRouteFlowCounterSupported = FlowCounterHandler::queryRouteFlowCounterCapability(); + if (!mRouteFlowCounterSupported) + { + SWSS_LOG_NOTICE("Route flow counter is not supported on this platform"); + } + swss::DBConnector state_db("STATE_DB", 0); + swss::Table capability_table(&state_db, STATE_FLOW_COUNTER_CAPABILITY_TABLE_NAME); + std::vector fvs; + fvs.emplace_back(FLOW_COUNTER_SUPPORT_FIELD, mRouteFlowCounterSupported ? "true" : "false"); + capability_table.set(FLOW_COUNTER_ROUTE_KEY, fvs); +} + +void FlowCounterRouteOrch::generateRouteFlowStats() +{ + SWSS_LOG_ENTER(); + if (!mRouteFlowCounterSupported) + { + return; + } + + for (const auto &route_pattern : mRoutePatternSet) + { + createRouteFlowCounterByPattern(route_pattern, 0); + } +} + +void FlowCounterRouteOrch::clearRouteFlowStats() +{ + SWSS_LOG_ENTER(); + if (!mBoundRouteCounters.empty() || !mPendingAddToFlexCntr.empty()) + { + for (auto &entry : mBoundRouteCounters) + { + const auto& route_pattern = entry.first; + for (auto &inner_entry : entry.second) + { + removeRouteFlowCounterFromDB(route_pattern.vrf_id, inner_entry.first, inner_entry.second); + unbindFlowCounter(route_pattern, route_pattern.vrf_id, inner_entry.first, inner_entry.second); + } + } + + for (auto &entry : mPendingAddToFlexCntr) + { + const auto& route_pattern = entry.first; + for (auto &inner_entry : entry.second) + { + unbindFlowCounter(route_pattern, route_pattern.vrf_id, inner_entry.first, inner_entry.second); + } + } + + mBoundRouteCounters.clear(); + mPendingAddToFlexCntr.clear(); + } +} + +void FlowCounterRouteOrch::addRoutePattern(const std::string &pattern, size_t max_match_count) +{ + SWSS_LOG_ENTER(); + sai_object_id_t vrf_id; + IpPrefix ip_prefix; + std::string vrf_name; + if (!parseRouteKeyForRoutePattern(pattern, '|', vrf_id, ip_prefix, vrf_name)) + { + vrf_id = SAI_NULL_OBJECT_ID; + } + + auto insert_result = mRoutePatternSet.emplace(vrf_name, vrf_id, ip_prefix, max_match_count); + if (insert_result.second) + { + SWSS_LOG_NOTICE("Inserting route pattern %s, max match count is %zu", pattern.c_str(), max_match_count); + if (!validateRoutePattern(*insert_result.first)) + { + mRoutePatternSet.erase(insert_result.first); + return; + } + + createRouteFlowCounterByPattern(*insert_result.first, 0); + } + else + { + SWSS_LOG_NOTICE("Updating route pattern %s max match count to %zu", pattern.c_str(), max_match_count); + RoutePattern &existing = const_cast(*insert_result.first); + onRoutePatternMaxMatchCountChange(existing, max_match_count); + } +} + +void FlowCounterRouteOrch::removeRoutePattern(const std::string& pattern) +{ + SWSS_LOG_ENTER(); + sai_object_id_t vrf_id; + IpPrefix ip_prefix; + std::string vrf_name; + if (!parseRouteKeyForRoutePattern(pattern, '|', vrf_id, ip_prefix, vrf_name)) + { + vrf_id = SAI_NULL_OBJECT_ID; + } + + SWSS_LOG_NOTICE("Removing route pattern %s", pattern.c_str()); + RoutePattern route_pattern(vrf_name, vrf_id, ip_prefix, 0); + auto iter = mRoutePatternSet.find(route_pattern); + if (iter == mRoutePatternSet.end()) + { + // Should not go to this branch, just in case + SWSS_LOG_ERROR("Trying to remove route pattern %s, but it does not exist", pattern.c_str()); + return; + } + mRoutePatternSet.erase(iter); + + removeRoutePattern(route_pattern); +} + +void FlowCounterRouteOrch::removeRoutePattern(const RoutePattern &route_pattern) +{ + SWSS_LOG_ENTER(); + auto cache_iter = mBoundRouteCounters.find(route_pattern); + if (cache_iter != mBoundRouteCounters.end()) + { + for (auto &entry : cache_iter->second) + { + removeRouteFlowCounterFromDB(route_pattern.vrf_id, entry.first, entry.second); + unbindFlowCounter(route_pattern, route_pattern.vrf_id, entry.first, entry.second); + } + mBoundRouteCounters.erase(cache_iter); + } + + auto pending_iter = mPendingAddToFlexCntr.find(route_pattern); + if (pending_iter != mPendingAddToFlexCntr.end()) + { + for (auto &entry : pending_iter->second) + { + unbindFlowCounter(route_pattern, route_pattern.vrf_id, entry.first, entry.second); + } + mPendingAddToFlexCntr.erase(pending_iter); + } +} + +void FlowCounterRouteOrch::onAddMiscRouteEntry(sai_object_id_t vrf_id, const sai_ip_prefix_t& ip_pfx, bool add_to_cache) +{ + SWSS_LOG_ENTER(); + if (!mRouteFlowCounterSupported) + { + return; + } + + IpPrefix ip_prefix = getIpPrefixFromSaiPrefix(ip_pfx); + onAddMiscRouteEntry(vrf_id, ip_prefix, add_to_cache); +} + +void FlowCounterRouteOrch::onAddMiscRouteEntry(sai_object_id_t vrf_id, const IpPrefix& ip_prefix, bool add_to_cache) +{ + SWSS_LOG_ENTER(); + if (!mRouteFlowCounterSupported) + { + return; + } + + if (add_to_cache) + { + auto iter = mMiscRoutes.find(vrf_id); + if (iter == mMiscRoutes.end()) + { + mMiscRoutes.emplace(vrf_id, std::set({ip_prefix})); + } + else + { + iter->second.insert(ip_prefix); + } + } + + if (!isRouteFlowCounterEnabled()) + { + return; + } + + if (mRoutePatternSet.empty()) + { + return; + } + + handleRouteAdd(vrf_id, ip_prefix); +} + +void FlowCounterRouteOrch::onRemoveMiscRouteEntry(sai_object_id_t vrf_id, const sai_ip_prefix_t& ip_pfx, bool remove_from_cache) +{ + SWSS_LOG_ENTER(); + if (!mRouteFlowCounterSupported) + { + return; + } + + IpPrefix ip_prefix = getIpPrefixFromSaiPrefix(ip_pfx); + onRemoveMiscRouteEntry(vrf_id, ip_prefix, remove_from_cache); +} + +void FlowCounterRouteOrch::onRemoveMiscRouteEntry(sai_object_id_t vrf_id, const IpPrefix& ip_prefix, bool remove_from_cache) +{ + SWSS_LOG_ENTER(); + if (!mRouteFlowCounterSupported) + { + return; + } + + if (remove_from_cache) + { + auto iter = mMiscRoutes.find(vrf_id); + if (iter != mMiscRoutes.end()) + { + auto prefix_iter = iter->second.find(ip_prefix); + if (prefix_iter != iter->second.end()) + { + iter->second.erase(prefix_iter); + if (iter->second.empty()) + { + mMiscRoutes.erase(iter); + } + } + } + } + + if (!isRouteFlowCounterEnabled()) + { + return; + } + + if (mRoutePatternSet.empty()) + { + return; + } + + handleRouteRemove(vrf_id, ip_prefix); +} + +void FlowCounterRouteOrch::onAddVR(sai_object_id_t vrf_id) +{ + SWSS_LOG_ENTER(); + if (!mRouteFlowCounterSupported) + { + return; + } + + assert(vrf_id != gVirtualRouterId); + auto *vrf_orch = gDirectory.get(); + std::string vrf_name = vrf_orch->getVRFname(vrf_id); + if (vrf_name == "") + { + getVnetNameByVrfId(vrf_id, vrf_name); + } + + if (vrf_name == "") + { + SWSS_LOG_WARN("Failed to get VRF name for vrf id %s", sai_serialize_object_id(vrf_id).c_str()); + } + + for (auto &route_pattern : mRoutePatternSet) + { + if (route_pattern.vrf_name == vrf_name) + { + RoutePattern &existing = const_cast(route_pattern); + existing.vrf_id = vrf_id; + createRouteFlowCounterByPattern(existing, 0); + break; + } + } +} + +void FlowCounterRouteOrch::onRemoveVR(sai_object_id_t vrf_id) +{ + SWSS_LOG_ENTER(); + if (!mRouteFlowCounterSupported) + { + return; + } + + for (auto &route_pattern : mRoutePatternSet) + { + if (route_pattern.vrf_id == vrf_id) + { + SWSS_LOG_NOTICE("Removing route pattern %s and all related counters due to VRF %s has been removed", route_pattern.to_string().c_str(), route_pattern.vrf_name.c_str()); + removeRoutePattern(route_pattern); + RoutePattern &existing = const_cast(route_pattern); + existing.vrf_id = SAI_NULL_OBJECT_ID; + } + } +} + +bool FlowCounterRouteOrch::bindFlowCounter(const RoutePattern &route_pattern, sai_object_id_t vrf_id, const IpPrefix& ip_prefix) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("Binding route entry vrf=%s prefix=%s to flow counter", route_pattern.vrf_name.c_str(), ip_prefix.to_string().c_str()); + + sai_object_id_t counter_oid; + if (!FlowCounterHandler::createGenericCounter(counter_oid)) + { + SWSS_LOG_ERROR("Failed to create generic counter"); + return false; + } + + sai_route_entry_t route_entry; + route_entry.switch_id = gSwitchId; + route_entry.vr_id = route_pattern.vrf_id; + copy(route_entry.destination, ip_prefix); + sai_attribute_t attr; + attr.id = SAI_ROUTE_ENTRY_ATTR_COUNTER_ID; + attr.value.oid = counter_oid; + + auto status = sai_route_api->set_route_entry_attribute(&route_entry, &attr); + if (status != SAI_STATUS_SUCCESS) + { + FlowCounterHandler::removeGenericCounter(counter_oid); + SWSS_LOG_WARN("Failed to bind route entry vrf=%s prefix=%s to flow counter", route_pattern.vrf_name.c_str(), ip_prefix.to_string().c_str()); + return false; + } + + pendingUpdateFlexDb(route_pattern, ip_prefix, counter_oid); + return true; +} + +void FlowCounterRouteOrch::unbindFlowCounter(const RoutePattern &route_pattern, sai_object_id_t vrf_id, const IpPrefix& ip_prefix, sai_object_id_t counter_oid) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("Unbinding route entry vrf=%s prefix=%s to flow counter", route_pattern.vrf_name.c_str(), ip_prefix.to_string().c_str()); + + sai_route_entry_t route_entry; + route_entry.switch_id = gSwitchId; + route_entry.vr_id = route_pattern.vrf_id; + copy(route_entry.destination, ip_prefix); + sai_attribute_t attr; + attr.id = SAI_ROUTE_ENTRY_ATTR_COUNTER_ID; + attr.value.oid = SAI_NULL_OBJECT_ID; + + auto status = sai_route_api->set_route_entry_attribute(&route_entry, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Failed to unbind route entry vrf=%s prefix=%s from flow counter", route_pattern.vrf_name.c_str(), ip_prefix.to_string().c_str()); + } + + FlowCounterHandler::removeGenericCounter(counter_oid); +} + +bool FlowCounterRouteOrch::removeRouteFlowCounter(const RoutePattern &route_pattern, sai_object_id_t vrf_id, const IpPrefix& ip_prefix) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("Removing route entry vrf=%s prefix=%s from flow counter", route_pattern.vrf_name.c_str(), ip_prefix.to_string().c_str()); + + // Check if the entry is in mPendingAddToFlexCntr + sai_object_id_t counter_oid = SAI_NULL_OBJECT_ID; + auto pending_iter = mPendingAddToFlexCntr.find(route_pattern); + if (pending_iter != mPendingAddToFlexCntr.end()) + { + auto iter_prefix = pending_iter->second.find(ip_prefix); + if (iter_prefix != pending_iter->second.end()) + { + counter_oid = iter_prefix->second; + pending_iter->second.erase(iter_prefix); + if (pending_iter->second.empty()) + { + mPendingAddToFlexCntr.erase(pending_iter); + } + } + } + + if (counter_oid == SAI_NULL_OBJECT_ID) + { + // Check if the entry is in mBoundRouteCounters + auto cache_iter = mBoundRouteCounters.find(route_pattern); + if (cache_iter != mBoundRouteCounters.end()) + { + auto iter_prefix = cache_iter->second.find(ip_prefix); + if (iter_prefix != cache_iter->second.end()) + { + counter_oid = iter_prefix->second; + removeRouteFlowCounterFromDB(vrf_id, ip_prefix, counter_oid); + cache_iter->second.erase(iter_prefix); + if (cache_iter->second.empty()) + { + mBoundRouteCounters.erase(cache_iter); + } + } + } + } + + // No need unbind because the route entry has been removed, just remove the generic counter + if (counter_oid != SAI_NULL_OBJECT_ID) + { + FlowCounterHandler::removeGenericCounter(counter_oid); + return true; + } + + return false; +} + +void FlowCounterRouteOrch::pendingUpdateFlexDb(const RoutePattern &route_pattern, const IpPrefix& ip_prefix, sai_object_id_t counter_oid) +{ + SWSS_LOG_ENTER(); + bool was_empty = mPendingAddToFlexCntr.empty(); + updateRouterFlowCounterCache(route_pattern, ip_prefix, counter_oid, mPendingAddToFlexCntr); + if (was_empty) + { + mFlexCounterUpdTimer->start(); + } +} + +bool FlowCounterRouteOrch::validateRoutePattern(const RoutePattern &route_pattern) const +{ + SWSS_LOG_ENTER(); + + for (const auto& existing : mRoutePatternSet) + { + if (existing.is_overlap_with(route_pattern)) + { + SWSS_LOG_ERROR("Configured route pattern %s is conflict with existing one %s", route_pattern.to_string().c_str(), existing.to_string().c_str()); + return false; + } + } + + return true; +} + +size_t FlowCounterRouteOrch::getRouteFlowCounterSizeByPattern(const RoutePattern &route_pattern) const +{ + SWSS_LOG_ENTER(); + + auto cache_iter = mBoundRouteCounters.find(route_pattern); + auto cache_count = cache_iter == mBoundRouteCounters.end() ? 0 : cache_iter->second.size(); + auto pending_iter = mPendingAddToFlexCntr.find(route_pattern); + auto pending_count = pending_iter == mPendingAddToFlexCntr.end() ? 0 : pending_iter->second.size(); + return cache_count + pending_count; +} + +bool FlowCounterRouteOrch::isRouteAlreadyBound(const RoutePattern &route_pattern, const IpPrefix &ip_prefix) const +{ + SWSS_LOG_ENTER(); + + auto iter = mBoundRouteCounters.find(route_pattern); + if (iter == mBoundRouteCounters.end()) + { + auto pending_iter = mPendingAddToFlexCntr.find(route_pattern); + if (pending_iter != mPendingAddToFlexCntr.end()) + { + return pending_iter->second.find(ip_prefix) != pending_iter->second.end(); + } + return false; + } + + return iter->second.find(ip_prefix) != iter->second.end(); +} + +void FlowCounterRouteOrch::createRouteFlowCounterByPattern(const RoutePattern &route_pattern, size_t current_bound_count) +{ + SWSS_LOG_ENTER(); + if (!isRouteFlowCounterEnabled()) + { + return; + } + + auto &syncdRoutes = gRouteOrch->getSyncdRoutes(); + auto iter = syncdRoutes.find(route_pattern.vrf_id); + if (iter != syncdRoutes.end()) + { + SWSS_LOG_NOTICE("Creating route flow counter for pattern %s", route_pattern.to_string().c_str()); + + for (auto &entry : iter->second) + { + if (current_bound_count == route_pattern.max_match_count) + { + return; + } + + if (route_pattern.is_match(route_pattern.vrf_id, entry.first)) + { + if (isRouteAlreadyBound(route_pattern, entry.first)) + { + continue; + } + + if (bindFlowCounter(route_pattern, route_pattern.vrf_id, entry.first)) + { + ++current_bound_count; + } + } + } + } + + createRouteFlowCounterFromVnetRoutes(route_pattern, current_bound_count); + + auto misc_iter = mMiscRoutes.find(route_pattern.vrf_id); + if (misc_iter != mMiscRoutes.end()) + { + SWSS_LOG_NOTICE("Creating route flow counter for pattern %s for other type route entries", route_pattern.to_string().c_str()); + + for (auto ip_prefix : misc_iter->second) + { + if (current_bound_count == route_pattern.max_match_count) + { + return; + } + + if (route_pattern.is_match(route_pattern.vrf_id, ip_prefix)) + { + if (isRouteAlreadyBound(route_pattern, ip_prefix)) + { + continue; + } + + if (bindFlowCounter(route_pattern, route_pattern.vrf_id, ip_prefix)) + { + ++current_bound_count; + } + } + } + } +} + +void FlowCounterRouteOrch::createRouteFlowCounterFromVnetRoutes(const RoutePattern &route_pattern, size_t& current_bound_count) +{ + SWSS_LOG_ENTER(); + + auto *vnet_orch = gDirectory.get(); + assert(vnet_orch); // VnetOrch instance is created before RouteOrch + + if (!vnet_orch->isVnetExists(route_pattern.vrf_name)) + { + return; + } + + SWSS_LOG_NOTICE("Creating route flow counter for pattern %s for VNET route entries", route_pattern.to_string().c_str()); + + auto *vrf_obj = vnet_orch->getTypePtr(route_pattern.vrf_name); + const auto &route_map = vrf_obj->getRouteMap(); + for (const auto &entry : route_map) + { + if (current_bound_count == route_pattern.max_match_count) + { + return; + } + + if (route_pattern.is_match(route_pattern.vrf_id, entry.first)) + { + if (isRouteAlreadyBound(route_pattern, entry.first)) + { + continue; + } + + if (bindFlowCounter(route_pattern, route_pattern.vrf_id, entry.first)) + { + ++current_bound_count; + } + } + } + + const auto &tunnel_routes = vrf_obj->getTunnelRoutes(); + for (const auto &entry : tunnel_routes) + { + if (current_bound_count == route_pattern.max_match_count) + { + return; + } + if (route_pattern.is_match(route_pattern.vrf_id, entry.first)) + { + if (isRouteAlreadyBound(route_pattern, entry.first)) + { + continue; + } + + if (bindFlowCounter(route_pattern, route_pattern.vrf_id, entry.first)) + { + ++current_bound_count; + } + } + } +} + +void FlowCounterRouteOrch::reapRouteFlowCounterByPattern(const RoutePattern &route_pattern, size_t current_bound_count) +{ + SWSS_LOG_ENTER(); + + auto pending_iter = mPendingAddToFlexCntr.find(route_pattern); + auto iter = mBoundRouteCounters.find(route_pattern); + if (iter == mBoundRouteCounters.end() && pending_iter == mPendingAddToFlexCntr.end()) + { + return; + } + + // Remove from pending cache first + if (pending_iter != mPendingAddToFlexCntr.end()) + { + while(current_bound_count > route_pattern.max_match_count) + { + auto bound_iter = pending_iter->second.begin(); + if (bound_iter == pending_iter->second.end()) + { + break; + } + unbindFlowCounter(route_pattern, route_pattern.vrf_id, bound_iter->first, bound_iter->second); + pending_iter->second.erase(bound_iter); + --current_bound_count; + } + } + + // Remove from bound cache + if (iter != mBoundRouteCounters.end()) + { + while(current_bound_count > route_pattern.max_match_count) + { + auto bound_iter = iter->second.begin(); + if (bound_iter == iter->second.end()) + { + break; + } + + removeRouteFlowCounterFromDB(route_pattern.vrf_id, bound_iter->first, bound_iter->second); + unbindFlowCounter(route_pattern, route_pattern.vrf_id, bound_iter->first, bound_iter->second); + iter->second.erase(bound_iter); + --current_bound_count; + } + } +} + +void FlowCounterRouteOrch::onRoutePatternMaxMatchCountChange(RoutePattern &route_pattern, size_t new_max_match_count) +{ + SWSS_LOG_ENTER(); + + if (route_pattern.max_match_count != new_max_match_count) + { + auto old_max_match_count = route_pattern.max_match_count; + route_pattern.max_match_count = new_max_match_count; + + if (!isRouteFlowCounterEnabled()) + { + return; + } + + auto current_bound_count = getRouteFlowCounterSizeByPattern(route_pattern); + SWSS_LOG_NOTICE("Current bound route flow counter count is %zu, new limit is %zu, old limit is %zu", current_bound_count, new_max_match_count, old_max_match_count); + if (new_max_match_count > old_max_match_count) + { + if (current_bound_count == old_max_match_count) + { + createRouteFlowCounterByPattern(route_pattern, current_bound_count); + } + } + else + { + if (current_bound_count > new_max_match_count) + { + reapRouteFlowCounterByPattern(route_pattern, current_bound_count); + } + } + } +} + +void FlowCounterRouteOrch::getRouteFlowCounterNameMapKey(sai_object_id_t vrf_id, const IpPrefix& ip_prefix, std::string &key) +{ + SWSS_LOG_ENTER(); + std::ostringstream oss; + if (gVirtualRouterId != vrf_id) + { + auto *vrf_orch = gDirectory.get(); + auto vrf_name = vrf_orch->getVRFname(vrf_id); + if (vrf_name == "") + { + getVnetNameByVrfId(vrf_id, vrf_name); + } + + if (vrf_name != "") + { + oss << vrf_name; + oss << "|"; + } + else + { + // Should not happen, just in case + SWSS_LOG_ERROR("Failed to get VRF/VNET name for vrf id %s", sai_serialize_object_id(vrf_id).c_str()); + } + } + oss << ip_prefix.to_string(); + key = oss.str(); +} + +void FlowCounterRouteOrch::handleRouteAdd(sai_object_id_t vrf_id, const IpPrefix& ip_prefix) +{ + if (!mRouteFlowCounterSupported) + { + return; + } + + if (!isRouteFlowCounterEnabled()) + { + return; + } + + for (const auto &route_pattern : mRoutePatternSet) + { + if (route_pattern.is_match(vrf_id, ip_prefix)) + { + auto current_bound_count = getRouteFlowCounterSizeByPattern(route_pattern); + if (current_bound_count < route_pattern.max_match_count) + { + bindFlowCounter(route_pattern, vrf_id, ip_prefix); + } + break; + } + } +} + +void FlowCounterRouteOrch::handleRouteRemove(sai_object_id_t vrf_id, const IpPrefix& ip_prefix) +{ + if (!mRouteFlowCounterSupported) + { + return; + } + + if (!isRouteFlowCounterEnabled()) + { + return; + } + + for (const auto &route_pattern : mRoutePatternSet) + { + if (route_pattern.is_match(vrf_id, ip_prefix)) + { + if (isRouteAlreadyBound(route_pattern, ip_prefix)) + { + if (removeRouteFlowCounter(route_pattern, vrf_id, ip_prefix)) + { + auto current_bound_count = getRouteFlowCounterSizeByPattern(route_pattern); + if (current_bound_count == route_pattern.max_match_count - 1) + { + createRouteFlowCounterByPattern(route_pattern, current_bound_count); + } + } + } + break; + } + } +} + +void FlowCounterRouteOrch::removeRouteFlowCounterFromDB(sai_object_id_t vrf_id, const IpPrefix& ip_prefix, sai_object_id_t counter_oid) +{ + SWSS_LOG_ENTER(); + std::string nameMapKey; + getRouteFlowCounterNameMapKey(vrf_id, ip_prefix, nameMapKey); + mPrefixToPatternTable->hdel("", nameMapKey); + mPrefixToCounterTable->hdel("", nameMapKey); + mRouteFlowCounterMgr.clearCounterIdList(counter_oid); +} + +void FlowCounterRouteOrch::updateRouterFlowCounterCache( + const RoutePattern &route_pattern, + const IpPrefix &ip_prefix, + sai_object_id_t counter_oid, + RouterFlowCounterCache &cache) +{ + SWSS_LOG_ENTER(); + auto iter = cache.find(route_pattern); + if (iter == cache.end()) + { + cache.emplace(route_pattern, std::map({{ip_prefix, counter_oid}})); + } + else + { + iter->second.emplace(ip_prefix, counter_oid); + } +} + +bool FlowCounterRouteOrch::isRouteFlowCounterEnabled() const +{ + SWSS_LOG_ENTER(); + FlexCounterOrch *flexCounterOrch = gDirectory.get(); + return flexCounterOrch && flexCounterOrch->getRouteFlowCountersState(); +} + +bool FlowCounterRouteOrch::parseRouteKeyForRoutePattern(const std::string &key, char sep, sai_object_id_t &vrf_id, IpPrefix &ip_prefix, std::string &vrf_name) +{ + size_t found = key.find(sep); + if (found == std::string::npos) + { + vrf_id = gVirtualRouterId; + ip_prefix = IpPrefix(key); + vrf_name = ""; + } + else + { + vrf_name = key.substr(0, found); + auto *vrf_orch = gDirectory.get(); + if (!key.compare(0, strlen(VRF_PREFIX), VRF_PREFIX) && vrf_orch->isVRFexists(vrf_name)) + { + vrf_id = vrf_orch->getVRFid(vrf_name); + } + else + { + if (!getVrfIdByVnetName(vrf_name, vrf_id)) + { + SWSS_LOG_NOTICE("VRF/VNET name %s is not resolved", vrf_name.c_str()); + return false; + } + } + + ip_prefix = IpPrefix(key.substr(found+1)); + } + + return true; +} + +bool FlowCounterRouteOrch::getVrfIdByVnetName(const std::string& vnet_name, sai_object_id_t &vrf_id) +{ + auto *vnet_orch = gDirectory.get(); + assert(vnet_orch); // VnetOrch instance is created before RouteOrch + + return vnet_orch->getVrfIdByVnetName(vnet_name, vrf_id); +} + +bool FlowCounterRouteOrch::getVnetNameByVrfId(sai_object_id_t vrf_id, std::string& vnet_name) +{ + auto *vnet_orch = gDirectory.get(); + assert(vnet_orch); // VnetOrch instance is created before RouteOrch + + return vnet_orch->getVnetNameByVrfId(vrf_id, vnet_name); +} + diff --git a/orchagent/flex_counter/flowcounterrouteorch.h b/orchagent/flex_counter/flowcounterrouteorch.h new file mode 100644 index 0000000000..1ef0452d4a --- /dev/null +++ b/orchagent/flex_counter/flowcounterrouteorch.h @@ -0,0 +1,178 @@ +#pragma once + +#include "bulker.h" +#include "dbconnector.h" +#include "ipprefix.h" +#include "orch.h" +#include +#include +#include +#include +#include + +#define ROUTE_FLOW_COUNTER_FLEX_COUNTER_GROUP "ROUTE_FLOW_COUNTER" + +struct RoutePattern +{ + RoutePattern(const std::string& input_vrf_name, sai_object_id_t vrf, IpPrefix prefix, size_t max_match_count) + :vrf_name(input_vrf_name), vrf_id(vrf), ip_prefix(prefix), max_match_count(max_match_count), exact_match(prefix.isDefaultRoute()) + { + } + + std::string vrf_name; + sai_object_id_t vrf_id; + IpPrefix ip_prefix; + size_t max_match_count; + bool exact_match; + + bool operator < (const RoutePattern &other) const + { + // We don't compare the vrf id here because: + // 1. vrf id could be SAI_NULL_OBJECT_ID if the VRF name is not resolved, two pattern with different VRF name and vrf_id=SAI_NULL_OBJECT_ID + // and same prefix will be treat as same route pattern, which is not expected + // 2. vrf name must be different + auto vrf_name_compare = vrf_name.compare(other.vrf_name); + if (vrf_name_compare < 0) + { + return true; + } + else if (vrf_name_compare == 0 && ip_prefix < other.ip_prefix) + { + return true; + } + else + { + return false; + } + } + + bool is_match(sai_object_id_t vrf, IpPrefix prefix) const + { + // No need compare VRF name here because: + // 1. If the VRF is not resolved, the vrf_id shall be SAI_NULL_OBJECT_ID, it cannot match any input vrf_id + // 2. If the VRF is resolved, different vrf must have different vrf id + if (vrf_id != vrf) + { + return false; + } + + if (!exact_match) + { + return (ip_prefix.getMaskLength() <= prefix.getMaskLength() && ip_prefix.isAddressInSubnet(prefix.getIp())); + } + else + { + return prefix == ip_prefix; + } + } + + bool is_overlap_with(const RoutePattern &other) const + { + if (this == &other) + { + return false; + } + + if (vrf_name != other.vrf_name) + { + return false; + } + + if (vrf_name != other.vrf_name) + { + return false; + } + + return is_match(other.vrf_id, other.ip_prefix) || other.is_match(vrf_id, ip_prefix); + } + + std::string to_string() const + { + std::ostringstream oss; + oss << "RoutePattern(vrf_id=" << vrf_id << ",ip_prefix=" << ip_prefix.to_string() << ")"; + return oss.str(); + } +}; + + + +typedef std::set RoutePatternSet; +/* RoutePattern to */ +typedef std::map> RouterFlowCounterCache; +/* IP2ME, MUX, VNET route entries */ +typedef std::map> MiscRouteEntryMap; + +class FlowCounterRouteOrch : public Orch +{ +public: + FlowCounterRouteOrch(swss::DBConnector *db, const std::vector &tableNames); + virtual ~FlowCounterRouteOrch(void); + + bool getRouteFlowCounterSupported() const { return mRouteFlowCounterSupported; } + void generateRouteFlowStats(); + void clearRouteFlowStats(); + void addRoutePattern(const std::string &pattern, size_t); + void removeRoutePattern(const std::string &pattern); + void onAddMiscRouteEntry(sai_object_id_t vrf_id, const IpPrefix& ip_prefix, bool add_to_cache = true); + void onAddMiscRouteEntry(sai_object_id_t vrf_id, const sai_ip_prefix_t& ip_pfx, bool add_to_cache = true); + void onRemoveMiscRouteEntry(sai_object_id_t vrf_id, const IpPrefix& ip_prefix, bool remove_from_cache = true); + void onRemoveMiscRouteEntry(sai_object_id_t vrf_id, const sai_ip_prefix_t& ip_pfx, bool remove_from_cache = true); + void onAddVR(sai_object_id_t vrf_id); + void onRemoveVR(sai_object_id_t vrf_id); + void handleRouteAdd(sai_object_id_t vrf_id, const IpPrefix& ip_prefix); + void handleRouteRemove(sai_object_id_t vrf_id, const IpPrefix& ip_prefix); + void processRouteFlowCounterBinding(); + +protected: + void doTask(Consumer &consumer) override; + void doTask(SelectableTimer &timer) override; + +private: + std::shared_ptr mAsicDb; + std::shared_ptr mCounterDb; + std::unique_ptr
mVidToRidTable; + std::unique_ptr
mPrefixToCounterTable; + std::unique_ptr
mPrefixToPatternTable; + + bool mRouteFlowCounterSupported = false; + /* Route pattern set, store configured route patterns */ + RoutePatternSet mRoutePatternSet; + /* Cache for those bound route flow counters*/ + RouterFlowCounterCache mBoundRouteCounters; + /* Cache for those route flow counters pending update to FLEX DB */ + RouterFlowCounterCache mPendingAddToFlexCntr; + /* IP2ME, MUX */ // TODO: remove MUX support + MiscRouteEntryMap mMiscRoutes; // Save here for route flow counter + /* Flex counter manager for route flow counter */ + FlexCounterManager mRouteFlowCounterMgr; + /* Timer to create flex counter and update counters DB */ + SelectableTimer *mFlexCounterUpdTimer = nullptr; + + EntityBulker gRouteBulker; + + void initRouteFlowCounterCapability(); + void removeRoutePattern(const RoutePattern &route_pattern); + void removeRouteFlowCounterFromDB(sai_object_id_t vrf_id, const IpPrefix& ip_prefix, sai_object_id_t counter_oid); + bool bindFlowCounter(const RoutePattern &route_pattern, sai_object_id_t vrf_id, const IpPrefix& ip_prefix); + void unbindFlowCounter(const RoutePattern &route_pattern, sai_object_id_t vrf_id, const IpPrefix& ip_prefix, sai_object_id_t counter_oid); + void pendingUpdateFlexDb(const RoutePattern &route_pattern, const IpPrefix &ip_prefix, sai_object_id_t counter_oid); + void updateRouterFlowCounterCache( + const RoutePattern &route_pattern, + const IpPrefix& ip_prefix, + sai_object_id_t counter_oid, + RouterFlowCounterCache &cache); + bool validateRoutePattern(const RoutePattern &route_pattern) const; + void onRoutePatternMaxMatchCountChange(RoutePattern &route_pattern, size_t new_max_match_count); + bool isRouteAlreadyBound(const RoutePattern &route_pattern, const IpPrefix &ip_prefix) const; + void createRouteFlowCounterByPattern(const RoutePattern &route_pattern, size_t currentBoundCount); + /* Return true if it actaully removed a counter so that caller need to fill the hole if possible*/ + bool removeRouteFlowCounter(const RoutePattern &route_pattern, sai_object_id_t vrf_id, const IpPrefix& ip_prefix); + void createRouteFlowCounterFromVnetRoutes(const RoutePattern &route_pattern, size_t& current_bound_count); + void reapRouteFlowCounterByPattern(const RoutePattern &route_pattern, size_t currentBoundCount); + bool isRouteFlowCounterEnabled() const; + void getRouteFlowCounterNameMapKey(sai_object_id_t vrf_id, const IpPrefix &ip_prefix, std::string &key); + size_t getRouteFlowCounterSizeByPattern(const RoutePattern &route_pattern) const; + bool parseRouteKeyForRoutePattern(const std::string &key, char sep, sai_object_id_t &vrf_id, IpPrefix &ip_prefix, std::string& vrf_name); + bool getVrfIdByVnetName(const std::string& vnet_name, sai_object_id_t &vrf_id); + bool getVnetNameByVrfId(sai_object_id_t vrf_id, std::string& vnet_name); +}; diff --git a/orchagent/flexcounterorch.cpp b/orchagent/flexcounterorch.cpp index dc14998774..a3770b76cb 100644 --- a/orchagent/flexcounterorch.cpp +++ b/orchagent/flexcounterorch.cpp @@ -10,6 +10,8 @@ #include "debugcounterorch.h" #include "directory.h" #include "copporch.h" +#include "routeorch.h" +#include "flowcounterrouteorch.h" extern sai_port_api_t *sai_port_api; @@ -19,6 +21,7 @@ extern IntfsOrch *gIntfsOrch; extern BufferOrch *gBufferOrch; extern Directory gDirectory; extern CoppOrch *gCoppOrch; +extern FlowCounterRouteOrch *gFlowCounterRouteOrch; #define BUFFER_POOL_WATERMARK_KEY "BUFFER_POOL_WATERMARK" #define PORT_KEY "PORT" @@ -29,6 +32,7 @@ extern CoppOrch *gCoppOrch; #define ACL_KEY "ACL" #define TUNNEL_KEY "TUNNEL" #define FLOW_CNT_TRAP_KEY "FLOW_CNT_TRAP" +#define FLOW_CNT_ROUTE_KEY "FLOW_CNT_ROUTE" unordered_map flexCounterGroupMap = { @@ -47,6 +51,7 @@ unordered_map flexCounterGroupMap = {"ACL", ACL_COUNTER_FLEX_COUNTER_GROUP}, {"TUNNEL", TUNNEL_STAT_COUNTER_FLEX_COUNTER_GROUP}, {FLOW_CNT_TRAP_KEY, HOSTIF_TRAP_COUNTER_FLEX_COUNTER_GROUP}, + {FLOW_CNT_ROUTE_KEY, ROUTE_FLOW_COUNTER_FLEX_COUNTER_GROUP}, }; @@ -175,6 +180,19 @@ void FlexCounterOrch::doTask(Consumer &consumer) m_hostif_trap_counter_enabled = false; } } + if (gFlowCounterRouteOrch && gFlowCounterRouteOrch->getRouteFlowCounterSupported() && key == FLOW_CNT_ROUTE_KEY) + { + if (value == "enable" && !m_route_flow_counter_enabled) + { + m_route_flow_counter_enabled = true; + gFlowCounterRouteOrch->generateRouteFlowStats(); + } + else if (value == "disable" && m_route_flow_counter_enabled) + { + gFlowCounterRouteOrch->clearRouteFlowStats(); + m_route_flow_counter_enabled = false; + } + } vector fieldValues; fieldValues.emplace_back(FLEX_COUNTER_STATUS_FIELD, value); m_flexCounterGroupTable->set(flexCounterGroupMap[key], fieldValues); diff --git a/orchagent/flexcounterorch.h b/orchagent/flexcounterorch.h index ceb8187506..4f9734c0e2 100644 --- a/orchagent/flexcounterorch.h +++ b/orchagent/flexcounterorch.h @@ -19,15 +19,16 @@ class FlexCounterOrch: public Orch bool getPortCountersState() const; bool getPortBufferDropCountersState() const; bool getHostIfTrapCounterState() const {return m_hostif_trap_counter_enabled;} + bool getRouteFlowCountersState() const {return m_route_flow_counter_enabled;} bool bake() override; - 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; bool m_hostif_trap_counter_enabled = false; + bool m_route_flow_counter_enabled = false; Table m_flexCounterConfigTable; }; diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 1feebb4d75..9bc36f7bb6 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -12,6 +12,7 @@ #include "swssnet.h" #include "tokenize.h" #include "routeorch.h" +#include "flowcounterrouteorch.h" #include "crmorch.h" #include "bufferorch.h" #include "directory.h" @@ -29,7 +30,7 @@ extern sai_vlan_api_t* sai_vlan_api; extern sai_object_id_t gSwitchId; extern PortsOrch *gPortsOrch; -extern RouteOrch *gRouteOrch; +extern FlowCounterRouteOrch *gFlowCounterRouteOrch; extern CrmOrch *gCrmOrch; extern BufferOrch *gBufferOrch; extern bool gIsNatSupported; @@ -1203,6 +1204,7 @@ bool IntfsOrch::removeRouterIntfs(Port &port) const auto id = sai_serialize_object_id(port.m_rif_id); removeRifFromFlexCounter(id, port.m_alias); + cleanUpRifFromCounterDb(id, port.m_alias); sai_status_t status = sai_router_intfs_api->remove_router_interface(port.m_rif_id); if (status != SAI_STATUS_SUCCESS) @@ -1272,6 +1274,8 @@ void IntfsOrch::addIp2MeRoute(sai_object_id_t vrf_id, const IpPrefix &ip_prefix) { gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); } + + gFlowCounterRouteOrch->onAddMiscRouteEntry(vrf_id, IpPrefix(ip_prefix.getIp().to_string())); } void IntfsOrch::removeIp2MeRoute(sai_object_id_t vrf_id, const IpPrefix &ip_prefix) @@ -1301,6 +1305,8 @@ void IntfsOrch::removeIp2MeRoute(sai_object_id_t vrf_id, const IpPrefix &ip_pref { gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); } + + gFlowCounterRouteOrch->onRemoveMiscRouteEntry(vrf_id, IpPrefix(ip_prefix.getIp().to_string())); } void IntfsOrch::addDirectedBroadcast(const Port &port, const IpPrefix &ip_prefix) @@ -1421,11 +1427,45 @@ void IntfsOrch::removeRifFromFlexCounter(const string &id, const string &name) SWSS_LOG_DEBUG("Unregistered interface %s from Flex counter", name.c_str()); } +/* + TODO A race condition can exist when swss removes the counter from COUNTERS DB + and at the same time syncd is inserting a new entry in COUNTERS DB. Therefore + all the rif counters cleanup code should move to syncd +*/ +void IntfsOrch::cleanUpRifFromCounterDb(const string &id, const string &name) +{ + SWSS_LOG_ENTER(); + string counter_key = getRifCounterTableKey(id); + string rate_key = getRifRateTableKey(id); + string rate_init_key = getRifRateInitTableKey(id); + m_counter_db->del(counter_key); + m_counter_db->del(rate_key); + m_counter_db->del(rate_init_key); + SWSS_LOG_NOTICE("CleanUp interface %s oid %s from counter db", name.c_str(),id.c_str()); +} + string IntfsOrch::getRifFlexCounterTableKey(string key) { return string(RIF_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; } +string IntfsOrch::getRifCounterTableKey(string key) +{ + return "COUNTERS:" + key; +} + +string IntfsOrch::getRifRateTableKey(string key) +{ + return "RATES:" + key; +} + +string IntfsOrch::getRifRateInitTableKey(string key) +{ + return "RATES:" + key + ":RIF"; +} + + + void IntfsOrch::generateInterfaceMap() { m_updateMapsTimer->start(); diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 5605abf133..341675bac1 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -90,6 +90,10 @@ class IntfsOrch : public Orch unique_ptr m_flexCounterGroupTable; std::string getRifFlexCounterTableKey(std::string s); + std::string getRifCounterTableKey(std::string s); + std::string getRifRateTableKey(std::string s); + std::string getRifRateInitTableKey(std::string s); + void cleanUpRifFromCounterDb(const string &id, const string &name); bool addRouterIntfs(sai_object_id_t vrf_id, Port &port); bool removeRouterIntfs(Port &port); diff --git a/orchagent/macsecorch.cpp b/orchagent/macsecorch.cpp index cb11fb35e5..20b6057733 100644 --- a/orchagent/macsecorch.cpp +++ b/orchagent/macsecorch.cpp @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -13,15 +14,22 @@ #include #include #include +#include +#include /* Global Variables*/ #define AVAILABLE_ACL_PRIORITIES_LIMITATION (32) #define EAPOL_ETHER_TYPE (0x888e) +#define PAUSE_ETHER_TYPE (0x8808) #define MACSEC_STAT_FLEX_COUNTER_POLLING_INTERVAL_MS (1000) #define COUNTERS_MACSEC_SA_ATTR_GROUP "COUNTERS_MACSEC_SA_ATTR" #define COUNTERS_MACSEC_SA_GROUP "COUNTERS_MACSEC_SA" #define COUNTERS_MACSEC_FLOW_GROUP "COUNTERS_MACSEC_FLOW" +#define PFC_MODE_BYPASS "bypass" +#define PFC_MODE_ENCRYPT "encrypt" +#define PFC_MODE_STRICT_ENCRYPT "strict_encrypt" +#define PFC_MODE_DEFAULT PFC_MODE_BYPASS extern sai_object_id_t gSwitchId; extern sai_macsec_api_t *sai_macsec_api; @@ -213,6 +221,68 @@ static void lexical_convert(const std::string &buffer, MACsecAuthKey &auth_key) } } +class MACsecSCI +{ +public: + operator sai_uint64_t () const + { + SWSS_LOG_ENTER(); + + return m_sci; + } + + std::string str() const + { + SWSS_LOG_ENTER(); + + return boost::algorithm::to_lower_copy(swss::binary_to_hex(&m_sci, sizeof(m_sci))); + } + + MACsecSCI& operator= (const std::string &buffer) + { + SWSS_LOG_ENTER(); + + if (!swss::hex_to_binary(buffer, reinterpret_cast(&m_sci), sizeof(m_sci))) + { + SWSS_LOG_THROW("Invalid SCI %s", buffer.c_str()); + } + + return *this; + } + + MACsecSCI() = default; + + MACsecSCI(const sai_uint64_t sci) + { + SWSS_LOG_ENTER(); + + this->m_sci = sci; + } + +private: + sai_uint64_t m_sci; +}; + +namespace swss { + +template<> +inline void lexical_convert(const std::string &buffer, MACsecSCI &sci) +{ + SWSS_LOG_ENTER(); + + sci = buffer; +} + +} + +std::ostream& operator<<(std::ostream& stream, const MACsecSCI& sci) +{ + SWSS_LOG_ENTER(); + + stream << sci.str(); + return stream; +} + /* Recover from a fail action by a serial of pre-defined recover actions */ class RecoverStack { @@ -535,6 +605,7 @@ MACsecOrch::MACsecOrch( m_state_macsec_ingress_sc(state_db, STATE_MACSEC_INGRESS_SC_TABLE_NAME), m_state_macsec_egress_sa(state_db, STATE_MACSEC_EGRESS_SA_TABLE_NAME), m_state_macsec_ingress_sa(state_db, STATE_MACSEC_INGRESS_SA_TABLE_NAME), + m_applPortTable(app_db, APP_PORT_TABLE_NAME), m_counter_db("COUNTERS_DB", 0), m_macsec_counters_map(&m_counter_db, COUNTERS_MACSEC_NAME_MAP), m_macsec_flow_tx_counters_map(&m_counter_db, COUNTERS_MACSEC_FLOW_TX_NAME_MAP), @@ -799,7 +870,7 @@ task_process_status MACsecOrch::taskUpdateEgressSA( { SWSS_LOG_ENTER(); std::string port_name; - sai_uint64_t sci = 0; + MACsecSCI sci; macsec_an_t an = 0; if (!extract_variables(port_sci_an, ':', port_name, sci, an) || an > MAX_SA_NUMBER) { @@ -810,12 +881,35 @@ task_process_status MACsecOrch::taskUpdateEgressSA( MACsecOrchContext ctx(this, port_name, SAI_MACSEC_DIRECTION_EGRESS, sci, an); if (ctx.get_macsec_sc() == nullptr) { - SWSS_LOG_INFO("The MACsec SC 0x%" PRIx64 " hasn't been created at the port %s.", sci, port_name.c_str()); + SWSS_LOG_INFO("The MACsec SC %s hasn't been created at the port %s.", sci.str().c_str(), port_name.c_str()); return task_need_retry; } if (ctx.get_macsec_sc()->m_encoding_an == an) { - return createMACsecSA(port_sci_an, sa_attr, SAI_MACSEC_DIRECTION_EGRESS); + if (ctx.get_macsec_sa() == nullptr) + { + // The MACsec SA hasn't been created + return createMACsecSA(port_sci_an, sa_attr, SAI_MACSEC_DIRECTION_EGRESS); + } + else + { + // The MACsec SA has enabled, update SA's attributes + sai_uint64_t pn; + + if (get_value(sa_attr, "next_pn", pn)) + { + sai_attribute_t attr; + attr.id = SAI_MACSEC_SA_ATTR_CONFIGURED_EGRESS_XPN; + attr.value.u64 = pn; + if (!this->updateMACsecAttr(SAI_OBJECT_TYPE_MACSEC_SA, *(ctx.get_macsec_sa()), attr)) + { + SWSS_LOG_WARN("Fail to update next pn (%" PRIu64 ") of egress MACsec SA %s", pn, port_sci_an.c_str()); + return task_failed; + } + } + + return task_success; + } } return task_need_retry; } @@ -835,7 +929,7 @@ task_process_status MACsecOrch::taskUpdateIngressSA( SWSS_LOG_ENTER(); swss::AlphaBoolean alpha_boolean = false; - get_value(sa_attr, "active", alpha_boolean); + bool has_active_field = get_value(sa_attr, "active", alpha_boolean); bool active = alpha_boolean.operator bool(); if (active) { @@ -845,7 +939,7 @@ task_process_status MACsecOrch::taskUpdateIngressSA( { std::string port_name; - sai_uint64_t sci = 0; + MACsecSCI sci; macsec_an_t an = 0; if (!extract_variables(port_sci_an, ':', port_name, sci, an) || an > MAX_SA_NUMBER) { @@ -857,7 +951,29 @@ task_process_status MACsecOrch::taskUpdateIngressSA( if (ctx.get_macsec_sa() != nullptr) { - return deleteMACsecSA(port_sci_an, SAI_MACSEC_DIRECTION_INGRESS); + if (has_active_field) + { + // Delete MACsec SA explicitly by set active to false + return deleteMACsecSA(port_sci_an, SAI_MACSEC_DIRECTION_INGRESS); + } + else + { + sai_uint64_t pn; + + if (get_value(sa_attr, "lowest_acceptable_pn", pn)) + { + sai_attribute_t attr; + attr.id = SAI_MACSEC_SA_ATTR_MINIMUM_INGRESS_XPN; + attr.value.u64 = pn; + if (!this->updateMACsecAttr(SAI_OBJECT_TYPE_MACSEC_SA, *(ctx.get_macsec_sa()), attr)) + { + SWSS_LOG_WARN("Fail to update lowest acceptable PN (%" PRIu64 ") of ingress MACsec SA %s", pn, port_sci_an.c_str()); + return task_failed; + } + } + + return task_success; + } } else { @@ -868,6 +984,8 @@ task_process_status MACsecOrch::taskUpdateIngressSA( return task_need_retry; } } + + return task_success; } task_process_status MACsecOrch::taskDeleteIngressSA( @@ -964,6 +1082,32 @@ bool MACsecOrch::initMACsecObject(sai_object_id_t switch_id) } macsec_obj.first->second.m_sci_in_ingress_macsec_acl = attrs.front().value.booldata; + attrs.clear(); + attr.id = SAI_MACSEC_ATTR_MAX_SECURE_ASSOCIATIONS_PER_SC; + attrs.push_back(attr); + 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) + { + // Default to 4 if SAI_MACSEC_ATTR_MAX_SECURE_ASSOCIATION_PER_SC isn't supported + macsec_obj.first->second.m_max_sa_per_sc = 4; + } else { + switch (attrs.front().value.s32) + { + case SAI_MACSEC_MAX_SECURE_ASSOCIATIONS_PER_SC_TWO: + macsec_obj.first->second.m_max_sa_per_sc = 2; + break; + case SAI_MACSEC_MAX_SECURE_ASSOCIATIONS_PER_SC_FOUR: + macsec_obj.first->second.m_max_sa_per_sc = 4; + break; + default: + SWSS_LOG_WARN( "Unsupported value returned from SAI_MACSEC_ATTR_MAX_SECURE_ASSOCIATION_PER_SC" ); + return false; + } + } + recover.clear(); return true; } @@ -1085,16 +1229,19 @@ bool MACsecOrch::createMACsecPort( port_id, switch_id, SAI_MACSEC_DIRECTION_EGRESS, - macsec_port.m_sci_in_sectag)) + macsec_port.m_sci_in_sectag, + port_name, + phy)) { SWSS_LOG_WARN("Cannot init the ACL Table at the port %s.", port_name.c_str()); return false; } - recover.add_action([this, &macsec_port, port_id]() { + recover.add_action([this, &macsec_port, port_id, phy]() { this->deinitMACsecACLTable( macsec_port.m_egress_acl_table, port_id, - SAI_MACSEC_DIRECTION_EGRESS); + SAI_MACSEC_DIRECTION_EGRESS, + phy); }); if (!initMACsecACLTable( @@ -1102,35 +1249,50 @@ bool MACsecOrch::createMACsecPort( port_id, switch_id, SAI_MACSEC_DIRECTION_INGRESS, - macsec_port.m_sci_in_sectag)) + macsec_port.m_sci_in_sectag, + port_name, + phy)) { SWSS_LOG_WARN("Cannot init the ACL Table at the port %s.", port_name.c_str()); return false; } - recover.add_action([this, &macsec_port, port_id]() { + recover.add_action([this, &macsec_port, port_id, phy]() { this->deinitMACsecACLTable( macsec_port.m_ingress_acl_table, port_id, - SAI_MACSEC_DIRECTION_INGRESS); + SAI_MACSEC_DIRECTION_INGRESS, + phy); }); - if (phy && phy->macsec_ipg != 0) + if (phy) { - if (!m_port_orch->getPortIPG(port.m_port_id, macsec_port.m_original_ipg)) + if (!setPFCForward(port_id, true)) { - SWSS_LOG_WARN("Cannot get Port IPG at the port %s", port_name.c_str()); + SWSS_LOG_WARN("Cannot enable PFC forward at the port %s.", port_name.c_str()); return false; } - if (!m_port_orch->setPortIPG(port.m_port_id, phy->macsec_ipg)) + recover.add_action([this, port_id]() + { this->setPFCForward(port_id, false); }); + + if (phy->macsec_ipg != 0) { - SWSS_LOG_WARN("Cannot set MACsec IPG to %u at the port %s", phy->macsec_ipg, port_name.c_str()); - return false; + if (!m_port_orch->getPortIPG(port.m_port_id, macsec_port.m_original_ipg)) + { + SWSS_LOG_WARN("Cannot get Port IPG at the port %s", port_name.c_str()); + return false; + } + if (!m_port_orch->setPortIPG(port.m_port_id, phy->macsec_ipg)) + { + SWSS_LOG_WARN("Cannot set MACsec IPG to %u at the port %s", phy->macsec_ipg, port_name.c_str()); + return false; + } } } SWSS_LOG_NOTICE("MACsec port %s is created.", port_name.c_str()); std::vector fvVector; + fvVector.emplace_back("max_sa_per_sc", std::to_string(macsec_obj.m_max_sa_per_sc)); fvVector.emplace_back("state", "ok"); m_state_macsec_port.set(port_name, fvVector); @@ -1312,7 +1474,7 @@ bool MACsecOrch::deleteMACsecPort( auto sc = macsec_port.m_egress_scs.begin(); while (sc != macsec_port.m_egress_scs.end()) { - const std::string port_sci = swss::join(':', port_name, sc->first); + const std::string port_sci = swss::join(':', port_name, MACsecSCI(sc->first)); sc ++; if (deleteMACsecSC(port_sci, SAI_MACSEC_DIRECTION_EGRESS) != task_success) { @@ -1322,7 +1484,7 @@ bool MACsecOrch::deleteMACsecPort( sc = macsec_port.m_ingress_scs.begin(); while (sc != macsec_port.m_ingress_scs.end()) { - const std::string port_sci = swss::join(':', port_name, sc->first); + const std::string port_sci = swss::join(':', port_name, MACsecSCI(sc->first)); sc ++; if (deleteMACsecSC(port_sci, SAI_MACSEC_DIRECTION_INGRESS) != task_success) { @@ -1345,13 +1507,13 @@ bool MACsecOrch::deleteMACsecPort( } } - if (!deinitMACsecACLTable(macsec_port.m_ingress_acl_table, port_id, SAI_MACSEC_DIRECTION_INGRESS)) + if (!deinitMACsecACLTable(macsec_port.m_ingress_acl_table, port_id, SAI_MACSEC_DIRECTION_INGRESS, phy)) { SWSS_LOG_WARN("Cannot deinit ingress ACL table at the port %s.", port_name.c_str()); result &= false; } - if (!deinitMACsecACLTable(macsec_port.m_egress_acl_table, port_id, SAI_MACSEC_DIRECTION_EGRESS)) + if (!deinitMACsecACLTable(macsec_port.m_egress_acl_table, port_id, SAI_MACSEC_DIRECTION_EGRESS, phy)) { SWSS_LOG_WARN("Cannot deinit egress ACL table at the port %s.", port_name.c_str()); result &= false; @@ -1369,13 +1531,22 @@ bool MACsecOrch::deleteMACsecPort( result &= false; } - if (phy && phy->macsec_ipg != 0) + if (phy) { - if (!m_port_orch->setPortIPG(port.m_port_id, macsec_port.m_original_ipg)) + if (!setPFCForward(port_id, false)) { - SWSS_LOG_WARN("Cannot set MACsec IPG to %u at the port %s", macsec_port.m_original_ipg, port_name.c_str()); + SWSS_LOG_WARN("Cannot disable PFC forward at the port %s.", port_name.c_str()); result &= false; } + + if (phy->macsec_ipg != 0) + { + if (!m_port_orch->setPortIPG(port.m_port_id, macsec_port.m_original_ipg)) + { + SWSS_LOG_WARN("Cannot set MACsec IPG to %u at the port %s", macsec_port.m_original_ipg, port_name.c_str()); + result &= false; + } + } } m_state_macsec_port.del(port_name); @@ -1453,7 +1624,7 @@ task_process_status MACsecOrch::updateMACsecSC( SWSS_LOG_ENTER(); std::string port_name; - sai_uint64_t sci = {0}; + MACsecSCI sci; if (!extract_variables(port_sci, ':', port_name, sci)) { SWSS_LOG_WARN("The key %s isn't correct.", port_sci.c_str()); @@ -1532,7 +1703,7 @@ bool MACsecOrch::createMACsecSC( RecoverStack recover; - const std::string port_sci = swss::join(':', port_name, sci); + const std::string port_sci = swss::join(':', port_name, MACsecSCI(sci)); auto scs = (direction == SAI_MACSEC_DIRECTION_EGRESS) @@ -1625,11 +1796,11 @@ bool MACsecOrch::createMACsecSC( fvVector.emplace_back("state", "ok"); if (direction == SAI_MACSEC_DIRECTION_EGRESS) { - m_state_macsec_egress_sc.set(swss::join('|', port_name, sci), fvVector); + m_state_macsec_egress_sc.set(swss::join('|', port_name, MACsecSCI(sci)), fvVector); } else { - m_state_macsec_ingress_sc.set(swss::join('|', port_name, sci), fvVector); + m_state_macsec_ingress_sc.set(swss::join('|', port_name, MACsecSCI(sci)), fvVector); } recover.clear(); @@ -1677,7 +1848,7 @@ bool MACsecOrch::createMACsecSC( attrs.data()); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_WARN("Cannot create MACsec egress SC 0x%" PRIx64, sci); + SWSS_LOG_WARN("Cannot create MACsec egress SC %s", MACsecSCI(sci).str().c_str()); task_process_status handle_status = handleSaiCreateStatus(SAI_API_MACSEC, status); if (handle_status != task_success) { @@ -1694,7 +1865,7 @@ task_process_status MACsecOrch::deleteMACsecSC( SWSS_LOG_ENTER(); std::string port_name; - sai_uint64_t sci = 0; + MACsecSCI sci; if (!extract_variables(port_sci, ':', port_name, sci)) { SWSS_LOG_WARN("The key %s isn't correct.", port_sci.c_str()); @@ -1747,11 +1918,11 @@ task_process_status MACsecOrch::deleteMACsecSC( if (direction == SAI_MACSEC_DIRECTION_EGRESS) { - m_state_macsec_egress_sc.del(swss::join('|', port_name, sci)); + m_state_macsec_egress_sc.del(swss::join('|', port_name, MACsecSCI(sci))); } else { - m_state_macsec_ingress_sc.del(swss::join('|', port_name, sci)); + m_state_macsec_ingress_sc.del(swss::join('|', port_name, MACsecSCI(sci))); } return result; @@ -1817,7 +1988,7 @@ task_process_status MACsecOrch::createMACsecSA( SWSS_LOG_ENTER(); std::string port_name; - sai_uint64_t sci = 0; + MACsecSCI sci; macsec_an_t an = 0; if (!extract_variables(port_sci_an, ':', port_name, sci, an) || an > MAX_SA_NUMBER) { @@ -1835,7 +2006,7 @@ task_process_status MACsecOrch::createMACsecSA( if (ctx.get_macsec_sc() == nullptr) { - SWSS_LOG_INFO("The MACsec SC 0x%" PRIx64 " hasn't been created at the port %s.", sci, port_name.c_str()); + SWSS_LOG_INFO("The MACsec SC %s hasn't been created at the port %s.", sci.str().c_str(), port_name.c_str()); return task_need_retry; } auto sc = ctx.get_macsec_sc(); @@ -1978,7 +2149,7 @@ task_process_status MACsecOrch::deleteMACsecSA( SWSS_LOG_ENTER(); std::string port_name = ""; - sai_uint64_t sci = 0; + MACsecSCI sci; macsec_an_t an = 0; if (!extract_variables(port_sci_an, ':', port_name, sci, an) || an > MAX_SA_NUMBER) { @@ -2210,7 +2381,9 @@ bool MACsecOrch::initMACsecACLTable( sai_object_id_t port_id, sai_object_id_t switch_id, sai_macsec_direction_t direction, - bool sci_in_sectag) + bool sci_in_sectag, + const std::string &port_name, + const gearbox_phy_t* phy) { SWSS_LOG_ENTER(); @@ -2268,6 +2441,36 @@ bool MACsecOrch::initMACsecACLTable( } recover.add_action([&acl_table]() { acl_table.m_available_acl_priorities.clear(); }); + if (phy) + { + if (acl_table.m_available_acl_priorities.empty()) + { + SWSS_LOG_WARN("Available ACL priorities have been exhausted."); + return false; + } + priority = *(acl_table.m_available_acl_priorities.rbegin()); + acl_table.m_available_acl_priorities.erase(std::prev(acl_table.m_available_acl_priorities.end())); + + TaskArgs values; + if (!m_applPortTable.get(port_name, values)) + { + SWSS_LOG_ERROR("Port %s isn't existing", port_name.c_str()); + return false; + } + std::string pfc_mode = PFC_MODE_DEFAULT; + get_value(values, "pfc_encryption_mode", pfc_mode); + + if (!createPFCEntry(acl_table.m_pfc_entry_id, acl_table.m_table_id, switch_id, direction, priority, pfc_mode)) + { + return false; + } + recover.add_action([this, &acl_table, priority]() { + this->deleteMACsecACLEntry(acl_table.m_pfc_entry_id); + acl_table.m_pfc_entry_id = SAI_NULL_OBJECT_ID; + acl_table.m_available_acl_priorities.insert(priority); + }); + } + recover.clear(); return true; } @@ -2275,7 +2478,8 @@ bool MACsecOrch::initMACsecACLTable( bool MACsecOrch::deinitMACsecACLTable( const MACsecACLTable &acl_table, sai_object_id_t port_id, - sai_macsec_direction_t direction) + sai_macsec_direction_t direction, + const gearbox_phy_t* phy) { bool result = true; @@ -2286,9 +2490,17 @@ bool MACsecOrch::deinitMACsecACLTable( } if (!deleteMACsecACLEntry(acl_table.m_eapol_packet_forward_entry_id)) { - SWSS_LOG_WARN("Cannot delete ACL entry"); + SWSS_LOG_WARN("Cannot delete EAPOL ACL entry"); result &= false; } + if (phy) + { + if (!deleteMACsecACLEntry(acl_table.m_pfc_entry_id)) + { + SWSS_LOG_WARN("Cannot delete PFC ACL entry"); + result &= false; + } + } if (!deleteMACsecACLTable(acl_table.m_table_id)) { SWSS_LOG_WARN("Cannot delete ACL table"); @@ -2562,6 +2774,11 @@ bool MACsecOrch::setMACsecFlowActive(sai_object_id_t entry_id, sai_object_id_t f bool MACsecOrch::deleteMACsecACLEntry(sai_object_id_t entry_id) { + if (entry_id == SAI_NULL_OBJECT_ID) + { + return true; + } + sai_status_t status = sai_acl_api->remove_acl_entry(entry_id); if (status != SAI_STATUS_SUCCESS) { @@ -2593,3 +2810,151 @@ bool MACsecOrch::getAclPriority(sai_object_id_t switch_id, sai_attr_id_t priorit return true; } + +bool MACsecOrch::setPFCForward(sai_object_id_t port_id, bool enable) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + sai_status_t status; + + // Enable/Disable Forward pause frame + attr.id = SAI_PORT_ATTR_GLOBAL_FLOW_CONTROL_FORWARD; + attr.value.booldata = enable; + status = sai_port_api->set_port_attribute(port_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + // Enable/Disable Forward PFC frame + attr.id = SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL_FORWARD; + attr.value.booldata = enable; + status = sai_port_api->set_port_attribute(port_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + return true; +} + +bool MACsecOrch::createPFCEntry( + sai_object_id_t &entry_id, + sai_object_id_t table_id, + sai_object_id_t switch_id, + sai_macsec_direction_t direction, + sai_uint32_t priority, + const std::string &pfc_mode) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + std::vector attrs; + + if (pfc_mode == PFC_MODE_BYPASS) + { + attrs.push_back(identifyPFC()); + attrs.push_back(bypassPFC()); + } + else if (pfc_mode == PFC_MODE_ENCRYPT) + { + if (direction == SAI_MACSEC_DIRECTION_EGRESS) + { + entry_id = SAI_NULL_OBJECT_ID; + return true; + } + else + { + attrs.push_back(identifyPFC()); + attrs.push_back(bypassPFC()); + } + } + else if (pfc_mode == PFC_MODE_STRICT_ENCRYPT) + { + if (direction == SAI_MACSEC_DIRECTION_EGRESS) + { + entry_id = SAI_NULL_OBJECT_ID; + return true; + } + else + { + attrs.push_back(identifyPFC()); + attrs.push_back(dropPFC()); + } + } + + attr.id = SAI_ACL_ENTRY_ATTR_TABLE_ID; + attr.value.oid = table_id; + attrs.push_back(attr); + attr.id = SAI_ACL_ENTRY_ATTR_PRIORITY; + attr.value.u32 = priority; + attrs.push_back(attr); + attr.id = SAI_ACL_ENTRY_ATTR_ADMIN_STATE; + attr.value.booldata = true; + attrs.push_back(attr); + + sai_status_t status = sai_acl_api->create_acl_entry( + &entry_id, + switch_id, + static_cast(attrs.size()), + attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + task_process_status handle_status = handleSaiCreateStatus(SAI_API_ACL, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + + return true; +} + +sai_attribute_t MACsecOrch::identifyPFC() const +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + + attr.id = SAI_ACL_ENTRY_ATTR_FIELD_ETHER_TYPE; + attr.value.aclfield.data.u16 = PAUSE_ETHER_TYPE; + attr.value.aclfield.mask.u16 = 0xFFFF; + attr.value.aclfield.enable = true; + + return attr; +} + +sai_attribute_t MACsecOrch::bypassPFC() const +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + + attr.id = SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION; + attr.value.aclaction.parameter.s32 = SAI_PACKET_ACTION_FORWARD; + attr.value.aclaction.enable = true; + + return attr; +} + +sai_attribute_t MACsecOrch::dropPFC() const +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + + attr.id = SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION; + attr.value.aclaction.parameter.s32 = SAI_PACKET_ACTION_DROP; + attr.value.aclaction.enable = true; + + return attr; +} diff --git a/orchagent/macsecorch.h b/orchagent/macsecorch.h index 6702c75cf6..b59984a3a6 100644 --- a/orchagent/macsecorch.h +++ b/orchagent/macsecorch.h @@ -67,6 +67,7 @@ class MACsecOrch : public Orch Table m_macsec_flow_rx_counters_map; Table m_macsec_sa_tx_counters_map; Table m_macsec_sa_rx_counters_map; + Table m_applPortTable; FlexCounterManager m_macsec_sa_attr_manager; FlexCounterManager m_macsec_sa_stat_manager; FlexCounterManager m_macsec_flow_stat_manager; @@ -75,6 +76,7 @@ class MACsecOrch : public Orch { sai_object_id_t m_table_id; sai_object_id_t m_eapol_packet_forward_entry_id; + sai_object_id_t m_pfc_entry_id; std::set m_available_acl_priorities; }; struct MACsecSC @@ -108,6 +110,7 @@ class MACsecOrch : public Orch sai_object_id_t m_ingress_id; map > m_macsec_ports; bool m_sci_in_ingress_macsec_acl; + sai_uint8_t m_max_sa_per_sc; }; map m_macsec_objs; map > m_macsec_ports; @@ -179,6 +182,7 @@ class MACsecOrch : public Orch const std::string &port_sci, sai_macsec_direction_t direction); bool deleteMACsecSC(sai_object_id_t sc_id); + bool setMACsecSC(sai_object_id_t sc_id, const sai_attribute_t &attr); bool updateMACsecAttr(sai_object_type_t object_type, sai_object_id_t object_id, const sai_attribute_t &attr); @@ -222,11 +226,14 @@ class MACsecOrch : public Orch sai_object_id_t port_id, sai_object_id_t switch_id, sai_macsec_direction_t direction, - bool sci_in_sectag); + bool sci_in_sectag, + const std::string &port_name, + const gearbox_phy_t* phy); bool deinitMACsecACLTable( const MACsecACLTable &acl_table, sai_object_id_t port_id, - sai_macsec_direction_t direction); + sai_macsec_direction_t direction, + const gearbox_phy_t* phy); bool createMACsecACLTable( sai_object_id_t &table_id, sai_object_id_t switch_id, @@ -255,6 +262,19 @@ class MACsecOrch : public Orch sai_object_id_t switch_id, sai_attr_id_t priority_id, sai_uint32_t &priority) const; + + /* PFC */ + bool setPFCForward(sai_object_id_t port_id, bool enable); + bool createPFCEntry(sai_object_id_t &entry_id, + sai_object_id_t table_id, + sai_object_id_t switch_id, + sai_macsec_direction_t direction, + sai_uint32_t priority, + const std::string &pfc_mode); + sai_attribute_t identifyPFC() const; + sai_attribute_t bypassPFC() const; + sai_attribute_t dropPFC() const; + }; #endif // ORCHAGENT_MACSECORCH_H_ diff --git a/orchagent/main.cpp b/orchagent/main.cpp index de96234a2d..5a074450da 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -177,7 +177,7 @@ void getCfgSwitchType(DBConnector *cfgDb, string &switch_type) switch_type = "switch"; } - if (switch_type != "voq" && switch_type != "fabric" && switch_type != "switch") + if (switch_type != "voq" && switch_type != "fabric" && switch_type != "chassis-packet" && switch_type != "switch") { SWSS_LOG_ERROR("Invalid switch type %s configured", switch_type.c_str()); //If configured switch type is none of the supported, assume regular switch @@ -478,10 +478,6 @@ int main(int argc, char **argv) attr.value.ptr = (void *)on_port_state_change; attrs.push_back(attr); - attr.id = SAI_SWITCH_ATTR_BFD_SESSION_STATE_CHANGE_NOTIFY; - attr.value.ptr = (void *)on_bfd_session_state_change; - attrs.push_back(attr); - attr.id = SAI_SWITCH_ATTR_SHUTDOWN_REQUEST_NOTIFY; attr.value.ptr = (void *)on_switch_shutdown_request; attrs.push_back(attr); @@ -574,6 +570,36 @@ int main(int argc, char **argv) attr.value.u64 = gSwitchId; attrs.push_back(attr); + if (gMySwitchType == "voq" || gMySwitchType == "fabric" || gMySwitchType == "chassis-packet") + { + /* We set this long timeout in order for orchagent to wait enough time for + * response from syncd. It is needed since switch create takes more time + * than default time to create switch if there are lots of front panel ports + * and systems ports to initialize + */ + + if (gMySwitchType == "voq" || gMySwitchType == "chassis-packet") + { + attr.value.u64 = (5 * SAI_REDIS_DEFAULT_SYNC_OPERATION_RESPONSE_TIMEOUT); + } + else if (gMySwitchType == "fabric") + { + attr.value.u64 = (10 * SAI_REDIS_DEFAULT_SYNC_OPERATION_RESPONSE_TIMEOUT); + } + + attr.id = SAI_REDIS_SWITCH_ATTR_SYNC_OPERATION_RESPONSE_TIMEOUT; + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Failed to set SAI REDIS response timeout"); + } + else + { + SWSS_LOG_NOTICE("SAI REDIS response timeout set successfully to %" PRIu64 " ", attr.value.u64); + } + } + status = sai_switch_api->create_switch(&gSwitchId, (uint32_t)attrs.size(), attrs.data()); if (status != SAI_STATUS_SUCCESS) { @@ -582,6 +608,22 @@ int main(int argc, char **argv) } SWSS_LOG_NOTICE("Create a switch, id:%" PRIu64, gSwitchId); + if (gMySwitchType == "voq" || gMySwitchType == "fabric" || gMySwitchType == "chassis-packet") + { + /* Set syncd response timeout back to the default value */ + attr.id = SAI_REDIS_SWITCH_ATTR_SYNC_OPERATION_RESPONSE_TIMEOUT; + attr.value.u64 = SAI_REDIS_DEFAULT_SYNC_OPERATION_RESPONSE_TIMEOUT; + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Failed to set SAI REDIS response timeout to default"); + } + else + { + SWSS_LOG_NOTICE("SAI REDIS response timeout set successfully to default: %" PRIu64 " ", attr.value.u64); + } + } if (gMySwitchType != "fabric") { diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index 0a73030f40..75ff671fc8 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -327,6 +327,7 @@ bool MirrorOrch::validateSrcPortList(const string& srcPortList) if (port.m_type == Port::LAG) { vector portv; + int portCount = 0; m_portsOrch->getLagMember(port, portv); for (const auto p : portv) { @@ -336,6 +337,13 @@ bool MirrorOrch::validateSrcPortList(const string& srcPortList) p.m_alias.c_str(), port.m_alias.c_str(), srcPortList.c_str()); return false; } + portCount++; + } + if (!portCount) + { + SWSS_LOG_ERROR("Source LAG %s is empty. set mirror session to inactive", + port.m_alias.c_str());; + return false; } } } @@ -344,6 +352,27 @@ bool MirrorOrch::validateSrcPortList(const string& srcPortList) return true; } +bool MirrorOrch::isHwResourcesAvailable() +{ + uint64_t availCount = 0; + + sai_status_t status = sai_object_type_get_availability( + gSwitchId, SAI_OBJECT_TYPE_MIRROR_SESSION, 0, nullptr, &availCount + ); + if (status != SAI_STATUS_SUCCESS) + { + if (status == SAI_STATUS_NOT_SUPPORTED) + { + SWSS_LOG_WARN("Mirror session resource availability monitoring is not supported. Skipping ..."); + return true; + } + + return parseHandleSaiStatusFailure(handleSaiGetStatus(SAI_API_MIRROR, status)); + } + + return availCount > 0; +} + task_process_status MirrorOrch::createEntry(const string& key, const vector& data) { SWSS_LOG_ENTER(); @@ -351,8 +380,7 @@ task_process_status MirrorOrch::createEntry(const string& key, const vectorattach(this, entry.dstIp); } + SWSS_LOG_NOTICE("Created mirror session %s", key.c_str()); + return task_process_status::task_success; } @@ -764,8 +797,7 @@ bool MirrorOrch::setUnsetPortMirror(Port port, if (set) { port_attr.value.objlist.count = 1; - port_attr.value.objlist.list = reinterpret_cast(calloc(port_attr.value.objlist.count, sizeof(sai_object_id_t))); - port_attr.value.objlist.list[0] = sessionId; + port_attr.value.objlist.list = &sessionId; } else { diff --git a/orchagent/mirrororch.h b/orchagent/mirrororch.h index b31d58bff3..d498a7ef6c 100644 --- a/orchagent/mirrororch.h +++ b/orchagent/mirrororch.h @@ -104,6 +104,8 @@ class MirrorOrch : public Orch, public Observer, public Subject // session_name -> VLAN | monitor_port_alias | next_hop_ip map m_recoverySessionMap; + bool isHwResourcesAvailable(); + task_process_status createEntry(const string&, const vector&); task_process_status deleteEntry(const string&); diff --git a/orchagent/mplsrouteorch.cpp b/orchagent/mplsrouteorch.cpp index ef40987a19..73dbbdb194 100644 --- a/orchagent/mplsrouteorch.cpp +++ b/orchagent/mplsrouteorch.cpp @@ -465,7 +465,7 @@ bool RouteOrch::addLabelRoute(LabelRouteBulkContext& ctx, const NextHopGroupKey Label& label = ctx.label; /* next_hop_id indicates the next hop id or next hop group id of this route */ - sai_object_id_t next_hop_id; + sai_object_id_t next_hop_id = SAI_NULL_OBJECT_ID; bool blackhole = false; if (m_syncdLabelRoutes.find(vrf_id) == m_syncdLabelRoutes.end()) diff --git a/orchagent/muxorch.cpp b/orchagent/muxorch.cpp index 4746fa4926..6f87e70dc4 100644 --- a/orchagent/muxorch.cpp +++ b/orchagent/muxorch.cpp @@ -23,6 +23,7 @@ #include "aclorch.h" #include "routeorch.h" #include "fdborch.h" +#include "qosorch.h" /* Global variables */ extern Directory gDirectory; @@ -32,6 +33,7 @@ extern RouteOrch *gRouteOrch; extern AclOrch *gAclOrch; extern PortsOrch *gPortsOrch; extern FdbOrch *gFdbOrch; +extern QosOrch *gQosOrch; extern sai_object_id_t gVirtualRouterId; extern sai_object_id_t gUnderlayIfId; @@ -42,7 +44,6 @@ extern sai_next_hop_api_t* sai_next_hop_api; extern sai_router_interface_api_t* sai_router_intfs_api; /* Constants */ -#define MUX_TUNNEL "MuxTunnel0" #define MUX_ACL_TABLE_NAME INGRESS_TABLE_DROP #define MUX_ACL_RULE_NAME "mux_acl_rule" #define MUX_HW_STATE_UNKNOWN "unknown" @@ -162,7 +163,12 @@ static sai_status_t remove_route(IpPrefix &pfx) return status; } -static sai_object_id_t create_tunnel(const IpAddress* p_dst_ip, const IpAddress* p_src_ip) +static sai_object_id_t create_tunnel( + const IpAddress* p_dst_ip, + const IpAddress* p_src_ip, + sai_object_id_t tc_to_dscp_map_id, + sai_object_id_t tc_to_queue_map_id, + string dscp_mode_name) { sai_status_t status; @@ -206,6 +212,22 @@ static sai_object_id_t create_tunnel(const IpAddress* p_dst_ip, const IpAddress* attr.value.s32 = SAI_TUNNEL_TTL_MODE_PIPE_MODEL; tunnel_attrs.push_back(attr); + if (dscp_mode_name == "uniform" || dscp_mode_name == "pipe") + { + sai_tunnel_dscp_mode_t dscp_mode; + if (dscp_mode_name == "uniform") + { + dscp_mode = SAI_TUNNEL_DSCP_MODE_UNIFORM_MODEL; + } + else + { + dscp_mode = SAI_TUNNEL_DSCP_MODE_PIPE_MODEL; + } + attr.id = SAI_TUNNEL_ATTR_ENCAP_DSCP_MODE; + attr.value.s32 = dscp_mode; + tunnel_attrs.push_back(attr); + } + attr.id = SAI_TUNNEL_ATTR_LOOPBACK_PACKET_ACTION; attr.value.s32 = SAI_PACKET_ACTION_DROP; tunnel_attrs.push_back(attr); @@ -224,6 +246,22 @@ static sai_object_id_t create_tunnel(const IpAddress* p_dst_ip, const IpAddress* tunnel_attrs.push_back(attr); } + // DSCP rewriting + if (tc_to_dscp_map_id != SAI_NULL_OBJECT_ID) + { + attr.id = SAI_TUNNEL_ATTR_ENCAP_QOS_TC_AND_COLOR_TO_DSCP_MAP; + attr.value.oid = tc_to_dscp_map_id; + tunnel_attrs.push_back(attr); + } + + // TC remapping + if (tc_to_queue_map_id != SAI_NULL_OBJECT_ID) + { + attr.id = SAI_TUNNEL_ATTR_ENCAP_QOS_TC_TO_QUEUE_MAP; + attr.value.oid = tc_to_queue_map_id; + tunnel_attrs.push_back(attr); + } + sai_object_id_t tunnel_id; status = sai_tunnel_api->create_tunnel(&tunnel_id, gSwitchId, (uint32_t)tunnel_attrs.size(), tunnel_attrs.data()); if (status != SAI_STATUS_SUCCESS) @@ -1261,10 +1299,32 @@ bool MuxOrch::handlePeerSwitch(const Request& request) MUX_TUNNEL, peer_ip.to_string().c_str()); return false; } - auto it = dst_ips.getIpAddresses().begin(); const IpAddress& dst_ip = *it; - mux_tunnel_id_ = create_tunnel(&peer_ip, &dst_ip); + + // Read dscp_mode of MuxTunnel0 from decap_orch + string dscp_mode_name = decap_orch_->getDscpMode(MUX_TUNNEL); + if (dscp_mode_name == "") + { + SWSS_LOG_NOTICE("dscp_mode for tunnel %s is not available. Will not be applied", MUX_TUNNEL); + } + + // Read tc_to_dscp_map_id of MuxTunnel0 from decap_orch + sai_object_id_t tc_to_dscp_map_id = SAI_NULL_OBJECT_ID; + decap_orch_->getQosMapId(MUX_TUNNEL, encap_tc_to_dscp_field_name, tc_to_dscp_map_id); + if (tc_to_dscp_map_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_NOTICE("tc_to_dscp_map_id for tunnel %s is not available. Will not be applied", MUX_TUNNEL); + } + // Read tc_to_queue_map_id of MuxTunnel0 from decap_orch + sai_object_id_t tc_to_queue_map_id = SAI_NULL_OBJECT_ID; + decap_orch_->getQosMapId(MUX_TUNNEL, encap_tc_to_queue_field_name, tc_to_queue_map_id); + if (tc_to_queue_map_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_NOTICE("tc_to_queue_map_id for tunnel %s is not available. Will not be applied", MUX_TUNNEL); + } + + mux_tunnel_id_ = create_tunnel(&peer_ip, &dst_ip, tc_to_dscp_map_id, tc_to_queue_map_id, dscp_mode_name); SWSS_LOG_NOTICE("Mux peer ip '%s' was added, peer name '%s'", peer_ip.to_string().c_str(), peer_name.c_str()); } diff --git a/orchagent/muxorch.h b/orchagent/muxorch.h index 42895b65b4..acb8c6d6d4 100644 --- a/orchagent/muxorch.h +++ b/orchagent/muxorch.h @@ -131,6 +131,8 @@ const request_description_t mux_cfg_request_description = { { "server_ipv4", REQ_T_IP_PREFIX }, { "server_ipv6", REQ_T_IP_PREFIX }, { "address_ipv4", REQ_T_IP }, + { "soc_ipv4", REQ_T_IP_PREFIX }, + { "cable_type", REQ_T_STRING }, }, { } }; diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index 7e5919bade..335832b58c 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -685,19 +685,6 @@ void NeighOrch::doTask(Consumer &consumer) IpAddress ip_address(key.substr(found+1)); - /* Verify Ipv4 LinkLocal and skip neighbor entry added for RFC5549 */ - if ((ip_address.getAddrScope() == IpAddress::LINK_SCOPE) && (ip_address.isV4())) - { - /* Check if this prefix is not a configured ip, if so allow */ - IpPrefix ipll_prefix(ip_address.getV4Addr(), 16); - if (!m_intfsOrch->isPrefixSubnet (ipll_prefix, alias)) - { - SWSS_LOG_NOTICE("Skip IPv4LL neighbor %s, Intf:%s op: %s ", ip_address.to_string().c_str(), alias.c_str(), op.c_str()); - it = consumer.m_toSync.erase(it); - continue; - } - } - NeighborEntry neighbor_entry = { ip_address, alias }; if (op == SET_COMMAND) @@ -816,6 +803,18 @@ bool NeighOrch::addNeighbor(const NeighborEntry &neighborEntry, const MacAddress memcpy(neighbor_attr.value.mac, macAddress.getMac(), 6); neighbor_attrs.push_back(neighbor_attr); + if ((ip_address.getAddrScope() == IpAddress::LINK_SCOPE) && (ip_address.isV4())) + { + /* Check if this prefix is a configured ip, if not allow */ + IpPrefix ipll_prefix(ip_address.getV4Addr(), 16); + if (!m_intfsOrch->isPrefixSubnet (ipll_prefix, alias)) + { + neighbor_attr.id = SAI_NEIGHBOR_ENTRY_ATTR_NO_HOST_ROUTE; + neighbor_attr.value.booldata = 1; + neighbor_attrs.push_back(neighbor_attr); + } + } + MuxOrch* mux_orch = gDirectory.get(); bool hw_config = isHwConfigured(neighborEntry); diff --git a/orchagent/nvgreorch.cpp b/orchagent/nvgreorch.cpp new file mode 100644 index 0000000000..38f8c19874 --- /dev/null +++ b/orchagent/nvgreorch.cpp @@ -0,0 +1,582 @@ +#include "orch.h" +#include "nvgreorch.h" +#include "request_parser.h" +#include "swssnet.h" +#include "directory.h" + +#define NVGRE_VSID_MAX_VALUE 16777214 + +extern Directory gDirectory; +extern PortsOrch* gPortsOrch; +extern sai_object_id_t gSwitchId; +extern sai_object_id_t gUnderlayIfId; +extern sai_object_id_t gVirtualRouterId; +extern sai_tunnel_api_t *sai_tunnel_api; + +static const std::vector nvgreMapTypes = { + MAP_T_VLAN, + MAP_T_BRIDGE +}; + +static const std::map nvgreEncapTunnelMap = { + { MAP_T_VLAN, SAI_TUNNEL_MAP_TYPE_VLAN_ID_TO_VSID }, + { MAP_T_BRIDGE, SAI_TUNNEL_MAP_TYPE_BRIDGE_IF_TO_VSID } +}; + +static inline sai_tunnel_map_type_t get_encap_nvgre_mapper(map_type_t map) +{ + return nvgreEncapTunnelMap.at(map); +} + +static const std::map nvgreDecapTunnelMap = { + { MAP_T_VLAN, SAI_TUNNEL_MAP_TYPE_VSID_TO_VLAN_ID }, + { MAP_T_BRIDGE, SAI_TUNNEL_MAP_TYPE_VSID_TO_BRIDGE_IF } +}; + +static inline sai_tunnel_map_type_t get_decap_nvgre_mapper(map_type_t map) +{ + return nvgreDecapTunnelMap.at(map); +} + +static const map> nvgreEncapTunnelMapKeyVal = +{ + { MAP_T_VLAN, + { SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_VSID_ID_VALUE } + }, + { MAP_T_BRIDGE, + { SAI_TUNNEL_MAP_ENTRY_ATTR_BRIDGE_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_VSID_ID_VALUE } + } +}; + +static inline sai_tunnel_map_entry_attr_t get_encap_nvgre_map_key(map_type_t map) +{ + return nvgreEncapTunnelMapKeyVal.at(map).first; +} + +static inline sai_tunnel_map_entry_attr_t get_encap_nvgre_map_val(map_type_t map) +{ + return nvgreEncapTunnelMapKeyVal.at(map).second; +} + +static const map> nvgreDecapTunnelMapKeyVal = +{ + { MAP_T_VLAN, + { SAI_TUNNEL_MAP_ENTRY_ATTR_VSID_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE } + }, + { MAP_T_BRIDGE, + { SAI_TUNNEL_MAP_ENTRY_ATTR_VSID_ID_KEY, SAI_TUNNEL_MAP_ENTRY_ATTR_BRIDGE_ID_VALUE } + } +}; + +static inline sai_tunnel_map_entry_attr_t get_decap_nvgre_map_key(map_type_t map) +{ + return nvgreDecapTunnelMapKeyVal.at(map).first; +} + +static inline sai_tunnel_map_entry_attr_t get_decap_nvgre_map_val(map_type_t map) +{ + return nvgreDecapTunnelMapKeyVal.at(map).second; +} + +/** @brief Creates tunnel mapper in SAI. + * + * @param sai_tunnel_map_type SAI tunnel map type e.g. VSID_TO_VLAN + * + * @return Tunnel map SAI identifier. + */ +sai_object_id_t NvgreTunnel::sai_create_tunnel_map(sai_tunnel_map_type_t sai_tunnel_map_type) +{ + sai_attribute_t attr; + std::vector tunnel_map_attrs; + + attr.id = SAI_TUNNEL_MAP_ATTR_TYPE; + attr.value.u32 = sai_tunnel_map_type; + + tunnel_map_attrs.push_back(attr); + + sai_object_id_t tunnel_map_id; + sai_status_t status = sai_tunnel_api->create_tunnel_map( + &tunnel_map_id, + gSwitchId, + static_cast(tunnel_map_attrs.size()), + tunnel_map_attrs.data() + ); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't create the NVGRE tunnel map object"); + } + + return tunnel_map_id; +} + +/** @brief Removes tunnel mapper in SAI. + * + * @param sai_tunnel_map_type SAI tunnel map identifier. + * + * @return void. + */ +void NvgreTunnel::sai_remove_tunnel_map(sai_object_id_t tunnel_map_id) +{ + sai_status_t status = sai_tunnel_api->remove_tunnel_map(tunnel_map_id); + + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't remove the NVGRE tunnel map object"); + } +} + + +/** @brief Creates tunnel in SAI. + * + * @param ids Pointer to structure where stored tunnel and tunnel mappers identifiers. + * @param src_ip Pointer to source IP address. + * + * @return SAI tunnel identifier. + */ +sai_object_id_t NvgreTunnel::sai_create_tunnel(struct tunnel_sai_ids_t &ids, const sai_ip_address_t &src_ip, sai_object_id_t underlay_rif) +{ + sai_attribute_t attr; + std::vector tunnel_attrs; + + attr.id = SAI_TUNNEL_ATTR_TYPE; + attr.value.s32 = SAI_TUNNEL_TYPE_NVGRE; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE; + attr.value.oid = underlay_rif; + tunnel_attrs.push_back(attr); + + sai_object_id_t decap_map_list[MAP_T_MAX]; + uint8_t num_decap_map = 0; + + for (auto map_type : nvgreMapTypes) + { + decap_map_list[num_decap_map] = ids.tunnel_decap_id.at(map_type); + num_decap_map++; + } + + attr.id = SAI_TUNNEL_ATTR_DECAP_MAPPERS; + attr.value.objlist.count = num_decap_map; + attr.value.objlist.list = decap_map_list; + tunnel_attrs.push_back(attr); + + sai_object_id_t encap_map_list[MAP_T_MAX]; + uint8_t num_encap_map = 0; + + for (auto map_type : nvgreMapTypes) + { + encap_map_list[num_encap_map] = ids.tunnel_encap_id.at(map_type); + num_encap_map++; + } + + attr.id = SAI_TUNNEL_ATTR_ENCAP_MAPPERS; + attr.value.objlist.count = num_encap_map; + attr.value.objlist.list = encap_map_list; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; + attr.value.ipaddr = src_ip; + tunnel_attrs.push_back(attr); + + sai_object_id_t tunnel_id; + sai_status_t status = sai_tunnel_api->create_tunnel( + &tunnel_id, + gSwitchId, + static_cast(tunnel_attrs.size()), + tunnel_attrs.data() + ); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't create the NVGRE tunnel object"); + } + + return tunnel_id; +} + +/** @brief Removes tunnel in SAI. + * + * @param tunnel_id Pointer to tunnel identifier. + * + * @return void. + */ +void NvgreTunnel::sai_remove_tunnel(sai_object_id_t tunnel_id) +{ + sai_status_t status = sai_tunnel_api->remove_tunnel(tunnel_id); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't remove the NVGRE tunnel object"); + } +} + +/** @brief Creates tunnel termination in SAI. + * + * @param tunnel_id Tunnel identifier. + * @param src_ip Pointer to source IP address. + * @param default_vrid Virtual router identifier. + * + * @return SAI tunnel termination identifier. + */ +sai_object_id_t NvgreTunnel::sai_create_tunnel_termination(sai_object_id_t tunnel_id, const sai_ip_address_t &src_ip, sai_object_id_t default_vrid) +{ + sai_attribute_t attr; + std::vector tunnel_attrs; + + attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE; + attr.value.s32 = SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_VR_ID; + attr.value.oid = default_vrid; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP; + attr.value.ipaddr = src_ip; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE; + attr.value.s32 = SAI_TUNNEL_TYPE_NVGRE; + tunnel_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID; + attr.value.oid = tunnel_id; + tunnel_attrs.push_back(attr); + + sai_object_id_t term_table_id; + sai_status_t status = sai_tunnel_api->create_tunnel_term_table_entry( + &term_table_id, + gSwitchId, + static_cast(tunnel_attrs.size()), + tunnel_attrs.data() + ); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't create a tunnel term table object"); + } + + return term_table_id; +} + +/** @brief Removes tunnel termination in SAI. + * + * @param tunnel_id Pointer to tunnel termination identifier. + * + * @return void. + */ +void NvgreTunnel::sai_remove_tunnel_termination(sai_object_id_t tunnel_term_id) +{ + sai_status_t status = sai_tunnel_api->remove_tunnel_term_table_entry(tunnel_term_id); + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't remove a tunnel term object"); + } +} + +void NvgreTunnel::createNvgreMappers() +{ + for (auto map_type : nvgreMapTypes) + { + tunnel_ids_.tunnel_encap_id.insert( + make_pair(map_type, sai_create_tunnel_map(get_encap_nvgre_mapper(map_type))) + ); + } + + for (auto map_type : nvgreMapTypes) + { + tunnel_ids_.tunnel_decap_id.insert( + make_pair(map_type, sai_create_tunnel_map(get_decap_nvgre_mapper(map_type))) + ); + } +} + +void NvgreTunnel::removeNvgreMappers() +{ + for (auto map_type : nvgreMapTypes) + { + sai_remove_tunnel_map(getEncapMapId(map_type)); + } + + for (auto map_type : nvgreMapTypes) + { + sai_remove_tunnel_map(getDecapMapId(map_type)); + } + + tunnel_ids_.tunnel_encap_id.clear(); + tunnel_ids_.tunnel_decap_id.clear(); +} + +void NvgreTunnel::createNvgreTunnel() +{ + sai_ip_address_t ip_addr; + swss::copy(ip_addr, src_ip_); + + tunnel_ids_.tunnel_id = sai_create_tunnel(tunnel_ids_, ip_addr, gUnderlayIfId); + tunnel_ids_.tunnel_term_id = sai_create_tunnel_termination(tunnel_ids_.tunnel_id, ip_addr, gVirtualRouterId); + + SWSS_LOG_INFO("NVGRE tunnel '%s' was created", tunnel_name_.c_str()); +} + +void NvgreTunnel::removeNvgreTunnel() +{ + try + { + sai_remove_tunnel_termination(tunnel_ids_.tunnel_term_id); + sai_remove_tunnel(tunnel_ids_.tunnel_id); + } + catch(const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error while removing tunnel entry. Tunnel: %s. Error: %s", tunnel_name_.c_str(), error.what()); + } + + SWSS_LOG_INFO("NVGRE tunnel '%s' was removed", tunnel_name_.c_str()); + + tunnel_ids_.tunnel_id = SAI_NULL_OBJECT_ID; + tunnel_ids_.tunnel_term_id = SAI_NULL_OBJECT_ID; +} + +NvgreTunnel::NvgreTunnel(std::string tunnelName, IpAddress srcIp) : + tunnel_name_(tunnelName), + src_ip_(srcIp) +{ + createNvgreMappers(); + createNvgreTunnel(); +} + +NvgreTunnel::~NvgreTunnel() +{ + removeNvgreTunnel(); + removeNvgreMappers(); +} + +bool NvgreTunnelOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + auto src_ip = request.getAttrIP("src_ip"); + const auto& tunnel_name = request.getKeyString(0); + + if (isTunnelExists(tunnel_name)) + { + SWSS_LOG_WARN("NVGRE tunnel '%s' already exists", tunnel_name.c_str()); + return true; + } + + nvgre_tunnel_table_[tunnel_name] = std::unique_ptr(new NvgreTunnel(tunnel_name, src_ip)); + + return true; +} + +bool NvgreTunnelOrch::delOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + const auto& tunnel_name = request.getKeyString(0); + + if (!isTunnelExists(tunnel_name)) + { + SWSS_LOG_ERROR("NVGRE tunnel '%s' doesn't exist", tunnel_name.c_str()); + return true; + } + + nvgre_tunnel_table_.erase(tunnel_name); + + SWSS_LOG_INFO("NVGRE tunnel '%s' was removed", tunnel_name.c_str()); + + return true; +} + +/** @brief Creates tunnel map entry in SAI. + * + * @param map_type map type - VLAN or BRIDGE. + * @param vsid Virtual Subnet ID value. + * @param vlan_id VLAN ID value. + * @param bridge_obj_id SAI bridge object. + * @param encap encapsulation flag. + * + * @return SAI tunnel map entry ID. + */ +sai_object_id_t NvgreTunnel::sai_create_tunnel_map_entry( + map_type_t map_type, + sai_uint32_t vsid, + sai_vlan_id_t vlan_id, + sai_object_id_t bridge_obj_id, + bool encap) +{ + sai_attribute_t attr; + sai_object_id_t tunnel_map_entry_id; + std::vector tunnel_map_entry_attrs; + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE; + attr.value.u32 = (encap) ? get_encap_nvgre_mapper(map_type) : get_decap_nvgre_mapper(map_type); + tunnel_map_entry_attrs.push_back(attr); + + attr.id = SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP; + attr.value.oid = (encap) ? getEncapMapId(map_type) : getDecapMapId(map_type); + tunnel_map_entry_attrs.push_back(attr); + + attr.id = (encap) ? get_encap_nvgre_map_key(map_type) : get_decap_nvgre_map_val(map_type); + if (bridge_obj_id != SAI_NULL_OBJECT_ID) + { + attr.value.oid = bridge_obj_id; + } + else + { + attr.value.u16 = vlan_id; + } + + tunnel_map_entry_attrs.push_back(attr); + + attr.id = (encap) ? get_encap_nvgre_map_val(map_type) : get_decap_nvgre_map_key(map_type); + attr.value.u32 = vsid; + tunnel_map_entry_attrs.push_back(attr); + + sai_status_t status = sai_tunnel_api->create_tunnel_map_entry(&tunnel_map_entry_id, gSwitchId, + static_cast (tunnel_map_entry_attrs.size()), + tunnel_map_entry_attrs.data()); + + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't create the NVGRE tunnel map entry object"); + } + + return tunnel_map_entry_id; +} + + +bool NvgreTunnel::addDecapMapperEntry( + map_type_t map_type, + uint32_t vsid, + sai_vlan_id_t vlan_id, + std::string tunnel_map_entry_name, + sai_object_id_t bridge_obj) +{ + auto tunnel_map_entry_id = sai_create_tunnel_map_entry(map_type, vsid, vlan_id, bridge_obj); + + nvgre_tunnel_map_table_[tunnel_map_entry_name].map_entry_id = tunnel_map_entry_id; + nvgre_tunnel_map_table_[tunnel_map_entry_name].vlan_id = vlan_id; + nvgre_tunnel_map_table_[tunnel_map_entry_name].vsid = vsid; + + SWSS_LOG_INFO("NVGRE decap tunnel map entry '%s' for tunnel '%s' was created", + tunnel_map_entry_name.c_str(), tunnel_name_.c_str()); + + return true; +} + +bool NvgreTunnelMapOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + auto tunnel_name = request.getKeyString(0); + NvgreTunnelOrch* tunnel_orch = gDirectory.get(); + + if (!tunnel_orch->isTunnelExists(tunnel_name)) + { + SWSS_LOG_WARN("NVGRE tunnel '%s' doesn't exist", tunnel_name.c_str()); + return true; + } + + auto tunnel_obj = tunnel_orch->getNvgreTunnel(tunnel_name); + const auto full_tunnel_map_entry_name = request.getFullKey(); + + if (tunnel_obj->isTunnelMapExists(full_tunnel_map_entry_name)) + { + SWSS_LOG_WARN("NVGRE tunnel map '%s' already exist", full_tunnel_map_entry_name.c_str()); + return true; + } + + sai_vlan_id_t vlan_id = (sai_vlan_id_t) request.getAttrVlan("vlan_id"); + Port port; + + if (!gPortsOrch->getVlanByVlanId(vlan_id, port)) + { + SWSS_LOG_WARN("VLAN ID doesn't exist: %d", vlan_id); + return true; + } + + auto vsid = static_cast(request.getAttrUint("vsid")); + if (vsid > NVGRE_VSID_MAX_VALUE) + { + SWSS_LOG_WARN("VSID is invalid: %d", vsid); + return true; + } + + if (!tunnel_obj->addDecapMapperEntry(MAP_T_VLAN, vsid, vlan_id, full_tunnel_map_entry_name)) + { + return true; + } + + return true; +} + +/** @brief Removes tunnel map entry in SAI. + * + * @param obj_id SAI tunnel map identifier. + * + * @return void. + */ +void NvgreTunnel::sai_remove_tunnel_map_entry(sai_object_id_t obj_id) +{ + sai_status_t status = SAI_STATUS_SUCCESS; + + if (obj_id != SAI_NULL_OBJECT_ID) + { + status = sai_tunnel_api->remove_tunnel_map_entry(obj_id); + } + + if (status != SAI_STATUS_SUCCESS) + { + throw std::runtime_error("Can't delete the NVGRE tunnel map entry object"); + } +} + +bool NvgreTunnel::delMapperEntry(std::string tunnel_map_entry_name) +{ + auto tunnel_map_entry_id = getMapEntryId(tunnel_map_entry_name); + + try + { + sai_remove_tunnel_map_entry(tunnel_map_entry_id); + } + catch (const std::runtime_error& error) + { + SWSS_LOG_ERROR("Error while removing decap tunnel map %s: %s", + tunnel_map_entry_name.c_str(), error.what()); + return false; + } + + nvgre_tunnel_map_table_.erase(tunnel_map_entry_name); + + SWSS_LOG_INFO("NVGRE tunnel map entry '%s' for tunnel '%s' was removed", + tunnel_map_entry_name.c_str(), tunnel_name_.c_str()); + + return true; +} + +bool NvgreTunnelMapOrch::delOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + const auto& tunnel_name = request.getKeyString(0); + NvgreTunnelOrch* tunnel_orch = gDirectory.get(); + auto tunnel_obj = tunnel_orch->getNvgreTunnel(tunnel_name); + const auto& full_tunnel_map_entry_name = request.getFullKey(); + + if (!tunnel_orch->isTunnelExists(tunnel_name)) + { + SWSS_LOG_WARN("NVGRE tunnel '%s' does not exist", tunnel_name.c_str()); + return true; + } + + if (!tunnel_obj->isTunnelMapExists(full_tunnel_map_entry_name)) + { + SWSS_LOG_WARN("NVGRE tunnel map '%s' does not exist", + full_tunnel_map_entry_name.c_str()); + return true; + } + + if (!tunnel_obj->delMapperEntry(full_tunnel_map_entry_name)) + { + return true; + } + + return true; +} diff --git a/orchagent/nvgreorch.h b/orchagent/nvgreorch.h new file mode 100644 index 0000000000..82092565ac --- /dev/null +++ b/orchagent/nvgreorch.h @@ -0,0 +1,167 @@ +#pragma once + +#include + +#include "sai.h" +#include "orch.h" +#include "request_parser.h" +#include "portsorch.h" + +typedef enum { + MAP_T_VLAN = 0, + MAP_T_BRIDGE = 1, + MAP_T_MAX = 2 +} map_type_t; + +struct tunnel_sai_ids_t +{ + std::map tunnel_encap_id; + std::map tunnel_decap_id; + sai_object_id_t tunnel_id; + sai_object_id_t tunnel_term_id; +}; + +typedef struct nvgre_tunnel_map_entry_s +{ + sai_object_id_t map_entry_id; + sai_vlan_id_t vlan_id; + uint32_t vsid; +} nvgre_tunnel_map_entry_t; + +const request_description_t nvgre_tunnel_request_description = { + { REQ_T_STRING }, + { + { "src_ip", REQ_T_IP }, + }, + { "src_ip" } +}; + +typedef std::map NvgreTunnelMapTable; + +class NvgreTunnel +{ +public: + NvgreTunnel(std::string tunnelName, IpAddress srcIp); + ~NvgreTunnel(); + + bool isTunnelMapExists(const std::string& name) const + { + return nvgre_tunnel_map_table_.find(name) != std::end(nvgre_tunnel_map_table_); + } + + sai_object_id_t getDecapMapId(map_type_t type) const + { + return tunnel_ids_.tunnel_decap_id.at(type); + } + + sai_object_id_t getEncapMapId(map_type_t type) const + { + return tunnel_ids_.tunnel_encap_id.at(type); + } + + sai_object_id_t getMapEntryId(std::string tunnel_map_entry_name) + { + return nvgre_tunnel_map_table_.at(tunnel_map_entry_name).map_entry_id; + } + + sai_object_id_t getMapEntryVlanId(std::string tunnel_map_entry_name) + { + return nvgre_tunnel_map_table_.at(tunnel_map_entry_name).vlan_id; + } + + sai_object_id_t getMapEntryVsid(std::string tunnel_map_entry_name) + { + return nvgre_tunnel_map_table_.at(tunnel_map_entry_name).vsid; + } + + bool addDecapMapperEntry(map_type_t map_type, uint32_t vsid, sai_vlan_id_t vlan_id, std::string tunnel_map_entry_name, sai_object_id_t bridge_obj=SAI_NULL_OBJECT_ID); + + bool delMapperEntry(std::string tunnel_map_entry_name); + +private: + void createNvgreMappers(); + void removeNvgreMappers(); + + void createNvgreTunnel(); + void removeNvgreTunnel(); + + sai_object_id_t sai_create_tunnel_map(sai_tunnel_map_type_t sai_tunnel_map_type); + void sai_remove_tunnel_map(sai_object_id_t tunnel_map_id); + + sai_object_id_t sai_create_tunnel(struct tunnel_sai_ids_t &ids, const sai_ip_address_t &src_ip, sai_object_id_t underlay_rif); + void sai_remove_tunnel(sai_object_id_t tunnel_id); + + sai_object_id_t sai_create_tunnel_termination(sai_object_id_t tunnel_id, const sai_ip_address_t &src_ip, sai_object_id_t default_vrid); + void sai_remove_tunnel_termination(sai_object_id_t tunnel_term_id); + + sai_object_id_t sai_create_tunnel_map_entry(map_type_t map_type, sai_uint32_t vsid, sai_vlan_id_t vlan_id, sai_object_id_t bridge_obj_id, bool encap=false); + void sai_remove_tunnel_map_entry(sai_object_id_t obj_id); + + std::string tunnel_name_; + IpAddress src_ip_; + tunnel_sai_ids_t tunnel_ids_; + + NvgreTunnelMapTable nvgre_tunnel_map_table_; +}; + +typedef std::map> NvgreTunnelTable; + +class NvgreTunnelRequest : public Request +{ +public: + NvgreTunnelRequest() : Request(nvgre_tunnel_request_description, '|') { } +}; + +class NvgreTunnelOrch : public Orch2 +{ +public: + NvgreTunnelOrch(DBConnector *db, const std::string& tableName) : + Orch2(db, tableName, request_) + { } + + bool isTunnelExists(const std::string& tunnelName) const + { + return nvgre_tunnel_table_.find(tunnelName) != std::end(nvgre_tunnel_table_); + } + + NvgreTunnel* getNvgreTunnel(const std::string& tunnelName) + { + return nvgre_tunnel_table_.at(tunnelName).get(); + } + +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + NvgreTunnelRequest request_; + NvgreTunnelTable nvgre_tunnel_table_; +}; + +const request_description_t nvgre_tunnel_map_request_description = { + { REQ_T_STRING, REQ_T_STRING }, + { + { "vsid", REQ_T_UINT }, + { "vlan_id", REQ_T_VLAN }, + }, + { "vsid", "vlan_id" } +}; + +class NvgreTunnelMapRequest : public Request +{ +public: + NvgreTunnelMapRequest() : Request(nvgre_tunnel_map_request_description, '|') { } +}; + +class NvgreTunnelMapOrch : public Orch2 +{ +public: + NvgreTunnelMapOrch(DBConnector *db, const std::string& tableName) : + Orch2(db, tableName, request_) + {} + +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + NvgreTunnelMapRequest request_; +}; \ No newline at end of file diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index 0992e329a4..f04a438a5d 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -357,6 +357,11 @@ bool Orch::parseReference(type_map &type_maps, string &ref_in, const string &typ SWSS_LOG_INFO("map:%s does not contain object with name:%s\n", type_name.c_str(), ref_in.c_str()); return false; } + if (obj_it->second.m_pendingRemove) + { + SWSS_LOG_NOTICE("map:%s contains a pending removed object %s, skip\n", type_name.c_str(), ref_in.c_str()); + return false; + } object_name = ref_in; SWSS_LOG_DEBUG("parsed: type_name:%s, object_name:%s", type_name.c_str(), object_name.c_str()); return true; @@ -410,7 +415,8 @@ void Orch::removeMeFromObjsReferencedByMe( const string &table, const string &obj_name, const string &field, - const string &old_referenced_obj_name) + const string &old_referenced_obj_name, + bool remove_field) { vector objects = tokenize(old_referenced_obj_name, list_item_delimiter); for (auto &obj : objects) @@ -426,6 +432,12 @@ void Orch::removeMeFromObjsReferencedByMe( referenced_table.c_str(), ref_obj_name.c_str(), to_string(old_referenced_obj.m_objsDependingOnMe.size()).c_str()); } + + if (remove_field) + { + auto &referencing_object = (*type_maps[table])[obj_name]; + referencing_object.m_objsReferencingByMe.erase(field); + } } void Orch::setObjectReference( @@ -439,7 +451,7 @@ void Orch::setObjectReference( auto field_ref = obj.m_objsReferencingByMe.find(field); if (field_ref != obj.m_objsReferencingByMe.end()) - removeMeFromObjsReferencedByMe(type_maps, table, obj_name, field, field_ref->second); + removeMeFromObjsReferencedByMe(type_maps, table, obj_name, field, field_ref->second, false); obj.m_objsReferencingByMe[field] = referenced_obj; @@ -459,16 +471,44 @@ void Orch::setObjectReference( } } +bool Orch::doesObjectExist( + type_map &type_maps, + const string &table, + const string &obj_name, + const string &field, + string &referenced_obj) +{ + auto &&searchRef = (*type_maps[table]).find(obj_name); + if (searchRef != (*type_maps[table]).end()) + { + auto &obj = searchRef->second; + auto &&searchReferencingObjectRef = obj.m_objsReferencingByMe.find(field); + if (searchReferencingObjectRef != obj.m_objsReferencingByMe.end()) + { + referenced_obj = searchReferencingObjectRef->second; + return true; + } + } + + return false; +} + void Orch::removeObject( type_map &type_maps, const string &table, const string &obj_name) { - auto &obj = (*type_maps[table])[obj_name]; + auto &&searchRef = (*type_maps[table]).find(obj_name); + if (searchRef == (*type_maps[table]).end()) + { + return; + } + + auto &obj = searchRef->second; for (auto field_ref : obj.m_objsReferencingByMe) { - removeMeFromObjsReferencedByMe(type_maps, table, obj_name, field_ref.first, field_ref.second); + removeMeFromObjsReferencedByMe(type_maps, table, obj_name, field_ref.first, field_ref.second, false); } // Update the field store @@ -847,7 +887,7 @@ task_process_status Orch::handleSaiCreateStatus(sai_api_t api, sai_status_t stat 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); + abort(); } break; case SAI_API_HOSTIF: @@ -865,7 +905,7 @@ task_process_status Orch::handleSaiCreateStatus(sai_api_t api, sai_status_t stat 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); + abort(); } default: switch (status) @@ -876,7 +916,7 @@ task_process_status Orch::handleSaiCreateStatus(sai_api_t api, sai_status_t stat 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); + abort(); } } return task_need_retry; @@ -917,12 +957,12 @@ task_process_status Orch::handleSaiSetStatus(sai_api_t api, sai_status_t status, default: SWSS_LOG_ERROR("Encountered failure in set operation, exiting orchagent, SAI API: %s, status: %s", sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); - exit(EXIT_FAILURE); + abort(); } default: SWSS_LOG_ERROR("Encountered failure in set operation, exiting orchagent, SAI API: %s, status: %s", sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); - exit(EXIT_FAILURE); + abort(); } return task_need_retry; @@ -950,7 +990,7 @@ task_process_status Orch::handleSaiRemoveStatus(sai_api_t api, sai_status_t stat default: SWSS_LOG_ERROR("Encountered failure in remove operation, exiting orchagent, SAI API: %s, status: %s", sai_serialize_api(api).c_str(), sai_serialize_status(status).c_str()); - exit(EXIT_FAILURE); + abort(); } return task_need_retry; } diff --git a/orchagent/orch.h b/orchagent/orch.h index 46a5d446ce..a808fef326 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -34,6 +34,7 @@ const char state_db_key_delimiter = '|'; #define INVM_PLATFORM_SUBSTRING "innovium" #define MLNX_PLATFORM_SUBSTRING "mellanox" #define BRCM_PLATFORM_SUBSTRING "broadcom" +#define BRCM_DNX_PLATFORM_SUBSTRING "broadcom-dnx" #define BFN_PLATFORM_SUBSTRING "barefoot" #define VS_PLATFORM_SUBSTRING "vs" #define NPS_PLATFORM_SUBSTRING "nephos" @@ -65,6 +66,7 @@ typedef struct // multiple objects being referenced are separated by ',' std::map m_objsReferencingByMe; sai_object_id_t m_saiObjectId; + bool m_pendingRemove; } referenced_object; typedef std::map object_reference_map; @@ -233,9 +235,11 @@ class Orch bool parseReference(type_map &type_maps, std::string &ref, const std::string &table_name, std::string &object_name); ref_resolve_status resolveFieldRefArray(type_map&, const std::string&, const std::string&, swss::KeyOpFieldsValuesTuple&, std::vector&, std::string&); void setObjectReference(type_map&, const std::string&, const std::string&, const std::string&, const std::string&); + bool doesObjectExist(type_map&, const std::string&, const std::string&, const std::string&, std::string&); void removeObject(type_map&, const std::string&, const std::string&); bool isObjectBeingReferenced(type_map&, const std::string&, const std::string&); std::string objectReferenceInfo(type_map&, const std::string&, const std::string&); + 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, bool remove_field=true); /* Note: consumer will be owned by this class */ void addExecutor(Executor* executor); @@ -250,7 +254,6 @@ class Orch ResponsePublisher m_publisher; 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); void addConsumer(swss::DBConnector *db, std::string tableName, int pri = default_orch_pri); }; diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 0341f69ea9..d4b40844a8 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -41,9 +41,11 @@ PbhOrch *gPbhOrch; MirrorOrch *gMirrorOrch; CrmOrch *gCrmOrch; BufferOrch *gBufferOrch; +QosOrch *gQosOrch; SwitchOrch *gSwitchOrch; Directory gDirectory; NatOrch *gNatOrch; +PolicerOrch *gPolicerOrch; MlagOrch *gMlagOrch; IsoGrpOrch *gIsoGrpOrch; MACsecOrch *gMacsecOrch; @@ -51,6 +53,8 @@ CoppOrch *gCoppOrch; P4Orch *gP4Orch; BfdOrch *gBfdOrch; Srv6Orch *gSrv6Orch; +FlowCounterRouteOrch *gFlowCounterRouteOrch; +DebugCounterOrch *gDebugCounterOrch; bool gIsNatSupported = false; @@ -129,6 +133,12 @@ bool OrchDaemon::init() TableConnector stateDbBfdSessionTable(m_stateDb, STATE_BFD_SESSION_TABLE_NAME); gBfdOrch = new BfdOrch(m_applDb, APP_BFD_SESSION_TABLE_NAME, stateDbBfdSessionTable); + static const vector route_pattern_tables = { + CFG_FLOW_COUNTER_ROUTE_PATTERN_TABLE_NAME, + }; + gFlowCounterRouteOrch = new FlowCounterRouteOrch(m_configDb, route_pattern_tables); + gDirectory.set(gFlowCounterRouteOrch); + vector vnet_tables = { APP_VNET_RT_TABLE_NAME, APP_VNET_RT_TUNNEL_TABLE_NAME @@ -200,6 +210,10 @@ bool OrchDaemon::init() EvpnNvoOrch* evpn_nvo_orch = new EvpnNvoOrch(m_applDb, APP_VXLAN_EVPN_NVO_TABLE_NAME); gDirectory.set(evpn_nvo_orch); + NvgreTunnelOrch *nvgre_tunnel_orch = new NvgreTunnelOrch(m_configDb, CFG_NVGRE_TUNNEL_TABLE_NAME); + gDirectory.set(nvgre_tunnel_orch); + NvgreTunnelMapOrch *nvgre_tunnel_map_orch = new NvgreTunnelMapOrch(m_configDb, CFG_NVGRE_TUNNEL_MAP_TABLE_NAME); + gDirectory.set(nvgre_tunnel_map_orch); vector qos_tables = { CFG_TC_TO_QUEUE_MAP_TABLE_NAME, @@ -214,9 +228,10 @@ bool OrchDaemon::init() CFG_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_TABLE_NAME, CFG_PFC_PRIORITY_TO_QUEUE_MAP_TABLE_NAME, CFG_DSCP_TO_FC_MAP_TABLE_NAME, - CFG_EXP_TO_FC_MAP_TABLE_NAME + CFG_EXP_TO_FC_MAP_TABLE_NAME, + CFG_TC_TO_DSCP_MAP_TABLE_NAME }; - QosOrch *qos_orch = new QosOrch(m_configDb, qos_tables); + gQosOrch = new QosOrch(m_configDb, qos_tables); vector buffer_tables = { APP_BUFFER_POOL_TABLE_NAME, @@ -228,11 +243,17 @@ bool OrchDaemon::init() }; gBufferOrch = new BufferOrch(m_applDb, m_configDb, m_stateDb, buffer_tables); - PolicerOrch *policer_orch = new PolicerOrch(m_configDb, "POLICER"); + vector policer_tables = { + TableConnector(m_configDb, CFG_POLICER_TABLE_NAME), + TableConnector(m_configDb, CFG_PORT_STORM_CONTROL_TABLE_NAME) + }; + + TableConnector stateDbStorm(m_stateDb, "BUM_STORM_CAPABILITY"); + gPolicerOrch = new PolicerOrch(policer_tables, gPortsOrch); TableConnector stateDbMirrorSession(m_stateDb, STATE_MIRROR_SESSION_TABLE_NAME); TableConnector confDbMirrorSession(m_configDb, CFG_MIRROR_SESSION_TABLE_NAME); - gMirrorOrch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch, policer_orch); + gMirrorOrch = new MirrorOrch(stateDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch, gPolicerOrch); TableConnector confDbAclTable(m_configDb, CFG_ACL_TABLE_TABLE_NAME); TableConnector confDbAclTableType(m_configDb, CFG_ACL_TABLE_TYPE_TABLE_NAME); @@ -277,7 +298,7 @@ bool OrchDaemon::init() CFG_DEBUG_COUNTER_DROP_REASON_TABLE_NAME }; - DebugCounterOrch *debug_counter_orch = new DebugCounterOrch(m_configDb, debug_counter_tables, 1000); + gDebugCounterOrch = new DebugCounterOrch(m_configDb, debug_counter_tables, 1000); const int natorch_base_pri = 50; @@ -325,7 +346,7 @@ bool OrchDaemon::init() * when iterating ConsumerMap. This is ensured implicitly by the order of keys in ordered map. * For cases when Orch has to process tables in specific order, like PortsOrch during warm start, it has to override Orch::doTask() */ - m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, qos_orch, wm_orch, policer_orch, tunnel_decap_orch, sflow_orch, debug_counter_orch, gMacsecOrch, gBfdOrch, gSrv6Orch}; + m_orchList = { gSwitchOrch, gCrmOrch, gPortsOrch, gBufferOrch, gFlowCounterRouteOrch, mux_orch, mux_cb_orch, gIntfsOrch, gNeighOrch, gNhgMapOrch, gNhgOrch, gCbfNhgOrch, gRouteOrch, gCoppOrch, gQosOrch, wm_orch, gPolicerOrch, tunnel_decap_orch, sflow_orch, gDebugCounterOrch, gMacsecOrch, gBfdOrch, gSrv6Orch}; bool initialize_dtel = false; if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) @@ -424,6 +445,8 @@ bool OrchDaemon::init() m_orchList.push_back(gIsoGrpOrch); m_orchList.push_back(gFgNhgOrch); m_orchList.push_back(mux_st_orch); + m_orchList.push_back(nvgre_tunnel_orch); + m_orchList.push_back(nvgre_tunnel_map_orch); if (m_fabricEnabled) { @@ -633,7 +656,7 @@ void OrchDaemon::flush() if (status != SAI_STATUS_SUCCESS) { SWSS_LOG_ERROR("Failed to flush redis pipeline %d", status); - exit(EXIT_FAILURE); + abort(); } // check if logroate is requested diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index ea49affbfc..def4b78629 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -11,6 +11,7 @@ #include "intfsorch.h" #include "neighorch.h" #include "routeorch.h" +#include "flowcounterrouteorch.h" #include "nhgorch.h" #include "cbf/cbfnhgorch.h" #include "cbf/nhgmaporch.h" @@ -43,6 +44,7 @@ #include "p4orch/p4orch.h" #include "bfdorch.h" #include "srv6orch.h" +#include "nvgreorch.h" using namespace swss; diff --git a/orchagent/pbh/pbhcap.cpp b/orchagent/pbh/pbhcap.cpp new file mode 100644 index 0000000000..46a3a49e19 --- /dev/null +++ b/orchagent/pbh/pbhcap.cpp @@ -0,0 +1,688 @@ +// includes ----------------------------------------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include + +#include "pbhschema.h" +#include "schema.h" +#include "logger.h" + +#include "pbhcap.h" + +using namespace swss; + +// defines ------------------------------------------------------------------------------------------------------------ + +#define PBH_PLATFORM_ENV_VAR "ASIC_VENDOR" +#define PBH_PLATFORM_GENERIC "generic" +#define PBH_PLATFORM_MELLANOX "mellanox" +#define PBH_PLATFORM_UNKN "unknown" + +#define PBH_TABLE_CAPABILITIES_KEY "table" +#define PBH_RULE_CAPABILITIES_KEY "rule" +#define PBH_HASH_CAPABILITIES_KEY "hash" +#define PBH_HASH_FIELD_CAPABILITIES_KEY "hash-field" + +#define PBH_FIELD_CAPABILITY_ADD "ADD" +#define PBH_FIELD_CAPABILITY_UPDATE "UPDATE" +#define PBH_FIELD_CAPABILITY_REMOVE "REMOVE" +#define PBH_FIELD_CAPABILITY_UNKN "UNKNOWN" + +#define PBH_STATE_DB_NAME "STATE_DB" +#define PBH_STATE_DB_TIMEOUT 0 + +// constants ---------------------------------------------------------------------------------------------------------- + +static const std::map pbhAsicVendorMap = +{ + { PbhAsicVendor::GENERIC, PBH_PLATFORM_GENERIC }, + { PbhAsicVendor::MELLANOX, PBH_PLATFORM_MELLANOX } +}; + +static const std::map pbhFieldCapabilityMap = +{ + { PbhFieldCapability::ADD, PBH_FIELD_CAPABILITY_ADD }, + { PbhFieldCapability::UPDATE, PBH_FIELD_CAPABILITY_UPDATE }, + { PbhFieldCapability::REMOVE, PBH_FIELD_CAPABILITY_REMOVE } +}; + +// functions ---------------------------------------------------------------------------------------------------------- + +static std::string toStr(PbhAsicVendor value) noexcept +{ + const auto &cit = pbhAsicVendorMap.find(value); + if (cit != pbhAsicVendorMap.cend()) + { + return cit->second; + } + + return PBH_PLATFORM_UNKN; +} + +static std::string toStr(PbhFieldCapability value) noexcept +{ + const auto &cit = pbhFieldCapabilityMap.find(value); + if (cit != pbhFieldCapabilityMap.cend()) + { + return cit->second; + } + + return PBH_FIELD_CAPABILITY_UNKN; +} + +static std::string toStr(const std::set &value) noexcept +{ + std::stringstream ss; + bool separator = false; + + for (const auto &cit : value) + { + if (!separator) + { + ss << toStr(cit); + separator = true; + } + else + { + ss << "," << toStr(cit); + } + } + + return ss.str(); +} + +// PBH field capabilities --------------------------------------------------------------------------------------------- + +void PbhVendorFieldCapabilities::setPbhDefaults(std::set &fieldCap) noexcept +{ + fieldCap.insert(PbhFieldCapability::ADD); + fieldCap.insert(PbhFieldCapability::UPDATE); + fieldCap.insert(PbhFieldCapability::REMOVE); +} + +PbhGenericFieldCapabilities::PbhGenericFieldCapabilities() noexcept +{ + this->table.interface_list.insert(PbhFieldCapability::UPDATE); + this->table.description.insert(PbhFieldCapability::UPDATE); + + this->rule.priority.insert(PbhFieldCapability::UPDATE); + this->setPbhDefaults(this->rule.gre_key); + this->setPbhDefaults(this->rule.ether_type); + this->setPbhDefaults(this->rule.ip_protocol); + this->setPbhDefaults(this->rule.ipv6_next_header); + this->setPbhDefaults(this->rule.l4_dst_port); + this->setPbhDefaults(this->rule.inner_ether_type); + this->rule.hash.insert(PbhFieldCapability::UPDATE); + this->setPbhDefaults(this->rule.packet_action); + this->setPbhDefaults(this->rule.flow_counter); + + this->hash.hash_field_list.insert(PbhFieldCapability::UPDATE); +} + +PbhMellanoxFieldCapabilities::PbhMellanoxFieldCapabilities() noexcept +{ + this->table.interface_list.insert(PbhFieldCapability::UPDATE); + this->table.description.insert(PbhFieldCapability::UPDATE); + + this->rule.priority.insert(PbhFieldCapability::UPDATE); + this->setPbhDefaults(this->rule.gre_key); + this->setPbhDefaults(this->rule.ether_type); + this->setPbhDefaults(this->rule.ip_protocol); + this->setPbhDefaults(this->rule.ipv6_next_header); + this->setPbhDefaults(this->rule.l4_dst_port); + this->setPbhDefaults(this->rule.inner_ether_type); + this->rule.hash.insert(PbhFieldCapability::UPDATE); + this->setPbhDefaults(this->rule.packet_action); + this->setPbhDefaults(this->rule.flow_counter); +} + +// PBH entity capabilities -------------------------------------------------------------------------------------------- + +PbhEntityCapabilities::PbhEntityCapabilities(const std::shared_ptr &fieldCap) noexcept : + fieldCap(fieldCap) +{ + +} + +bool PbhEntityCapabilities::validate(const std::set &fieldCap, PbhFieldCapability value) const +{ + const auto &cit = fieldCap.find(value); + if (cit == fieldCap.cend()) + { + return false; + } + + return true; +} + +PbhTableCapabilities::PbhTableCapabilities(const std::shared_ptr &fieldCap) noexcept : + PbhEntityCapabilities(fieldCap) +{ + +} + +bool PbhTableCapabilities::validatePbhInterfaceList(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().interface_list, value); +} + +bool PbhTableCapabilities::validatePbhDescription(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().description, value); +} + +auto PbhTableCapabilities::getPbhCap() const -> const decltype(PbhVendorFieldCapabilities::table) & +{ + return this->fieldCap->table; +} + +PbhRuleCapabilities::PbhRuleCapabilities(const std::shared_ptr &fieldCap) noexcept : + PbhEntityCapabilities(fieldCap) +{ + +} + +bool PbhRuleCapabilities::validatePbhPriority(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().priority, value); +} + +bool PbhRuleCapabilities::validatePbhGreKey(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().gre_key, value); +} + +bool PbhRuleCapabilities::validatePbhEtherType(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().ether_type, value); +} + +bool PbhRuleCapabilities::validatePbhIpProtocol(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().ip_protocol, value); +} + +bool PbhRuleCapabilities::validatePbhIpv6NextHeader(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().ipv6_next_header, value); +} + +bool PbhRuleCapabilities::validatePbhL4DstPort(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().l4_dst_port, value); +} + +bool PbhRuleCapabilities::validatePbhInnerEtherType(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().inner_ether_type, value); +} + +bool PbhRuleCapabilities::validatePbhHash(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().hash, value); +} + +bool PbhRuleCapabilities::validatePbhPacketAction(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().packet_action, value); +} + +bool PbhRuleCapabilities::validatePbhFlowCounter(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().flow_counter, value); +} + +auto PbhRuleCapabilities::getPbhCap() const -> const decltype(PbhVendorFieldCapabilities::rule) & +{ + return this->fieldCap->rule; +} + +PbhHashCapabilities::PbhHashCapabilities(const std::shared_ptr &fieldCap) noexcept : + PbhEntityCapabilities(fieldCap) +{ + +} + +bool PbhHashCapabilities::validatePbhHashFieldList(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().hash_field_list, value); +} + +auto PbhHashCapabilities::getPbhCap() const -> const decltype(PbhVendorFieldCapabilities::hash) & +{ + return this->fieldCap->hash; +} + +PbhHashFieldCapabilities::PbhHashFieldCapabilities(const std::shared_ptr &fieldCap) noexcept : + PbhEntityCapabilities(fieldCap) +{ + +} + +bool PbhHashFieldCapabilities::validatePbhHashField(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().hash_field, value); +} + +bool PbhHashFieldCapabilities::validatePbhIpMask(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().ip_mask, value); +} + +bool PbhHashFieldCapabilities::validatePbhSequenceId(PbhFieldCapability value) const +{ + return this->validate(this->getPbhCap().sequence_id, value); +} + +auto PbhHashFieldCapabilities::getPbhCap() const -> const decltype(PbhVendorFieldCapabilities::hashField) & +{ + return this->fieldCap->hashField; +} + +// PBH capabilities --------------------------------------------------------------------------------------------------- + +DBConnector PbhCapabilities::stateDb(PBH_STATE_DB_NAME, PBH_STATE_DB_TIMEOUT); +Table PbhCapabilities::capTable(&stateDb, STATE_PBH_CAPABILITIES_TABLE_NAME); + +PbhCapabilities::PbhCapabilities() noexcept +{ + SWSS_LOG_ENTER(); + + if (!this->parsePbhAsicVendor()) + { + SWSS_LOG_WARN("Failed to parse ASIC vendor: fallback to %s platform", PBH_PLATFORM_GENERIC); + this->asicVendor = PbhAsicVendor::GENERIC; + } + + this->initPbhVendorCapabilities(); + this->writePbhVendorCapabilitiesToDb(); +} + +PbhAsicVendor PbhCapabilities::getAsicVendor() const noexcept +{ + return this->asicVendor; +} + +bool PbhCapabilities::parsePbhAsicVendor() +{ + SWSS_LOG_ENTER(); + + const auto *envVar = std::getenv(PBH_PLATFORM_ENV_VAR); + if (envVar == nullptr) + { + SWSS_LOG_WARN("Failed to detect ASIC vendor: environmental variable(%s) is not found", PBH_PLATFORM_ENV_VAR); + return false; + } + + std::string platform(envVar); + + if (platform == PBH_PLATFORM_MELLANOX) + { + this->asicVendor = PbhAsicVendor::MELLANOX; + } + else + { + this->asicVendor = PbhAsicVendor::GENERIC; + } + + SWSS_LOG_NOTICE("Parsed PBH ASIC vendor: %s", toStr(this->asicVendor).c_str()); + + return true; +} + +void PbhCapabilities::initPbhVendorCapabilities() +{ + std::shared_ptr fieldCap; + + switch (this->asicVendor) + { + case PbhAsicVendor::GENERIC: + fieldCap = std::make_shared(); + break; + + case PbhAsicVendor::MELLANOX: + fieldCap = std::make_shared(); + break; + + default: + SWSS_LOG_WARN("Unknown ASIC vendor: skipping ..."); + break; + } + + if (!fieldCap) + { + SWSS_LOG_ERROR("Failed to initialize PBH capabilities: unknown ASIC vendor"); + return; + } + + this->table = std::make_shared(fieldCap); + this->rule = std::make_shared(fieldCap); + this->hash = std::make_shared(fieldCap); + this->hashField = std::make_shared(fieldCap); + + SWSS_LOG_NOTICE("Initialized PBH capabilities: %s platform", toStr(this->asicVendor).c_str()); +} + +template<> +void PbhCapabilities::writePbhEntityCapabilitiesToDb(const std::shared_ptr &entityCap) +{ + SWSS_LOG_ENTER(); + + auto key = PbhCapabilities::capTable.getKeyName(PBH_TABLE_CAPABILITIES_KEY); + std::vector fvList; + + fvList.push_back(FieldValueTuple(PBH_TABLE_INTERFACE_LIST, toStr(entityCap->getPbhCap().interface_list))); + fvList.push_back(FieldValueTuple(PBH_TABLE_DESCRIPTION, toStr(entityCap->getPbhCap().description))); + + PbhCapabilities::capTable.set(PBH_TABLE_CAPABILITIES_KEY, fvList); + + SWSS_LOG_NOTICE("Wrote PBH table capabilities to State DB: %s key", key.c_str()); +} + +template<> +void PbhCapabilities::writePbhEntityCapabilitiesToDb(const std::shared_ptr &entityCap) +{ + SWSS_LOG_ENTER(); + + auto key = PbhCapabilities::capTable.getKeyName(PBH_RULE_CAPABILITIES_KEY); + std::vector fvList; + + fvList.push_back(FieldValueTuple(PBH_RULE_PRIORITY, toStr(entityCap->getPbhCap().priority))); + fvList.push_back(FieldValueTuple(PBH_RULE_GRE_KEY, toStr(entityCap->getPbhCap().gre_key))); + fvList.push_back(FieldValueTuple(PBH_RULE_ETHER_TYPE, toStr(entityCap->getPbhCap().ether_type))); + fvList.push_back(FieldValueTuple(PBH_RULE_IP_PROTOCOL, toStr(entityCap->getPbhCap().ip_protocol))); + fvList.push_back(FieldValueTuple(PBH_RULE_IPV6_NEXT_HEADER, toStr(entityCap->getPbhCap().ipv6_next_header))); + fvList.push_back(FieldValueTuple(PBH_RULE_L4_DST_PORT, toStr(entityCap->getPbhCap().l4_dst_port))); + fvList.push_back(FieldValueTuple(PBH_RULE_INNER_ETHER_TYPE, toStr(entityCap->getPbhCap().inner_ether_type))); + fvList.push_back(FieldValueTuple(PBH_RULE_HASH, toStr(entityCap->getPbhCap().hash))); + fvList.push_back(FieldValueTuple(PBH_RULE_PACKET_ACTION, toStr(entityCap->getPbhCap().packet_action))); + fvList.push_back(FieldValueTuple(PBH_RULE_FLOW_COUNTER, toStr(entityCap->getPbhCap().flow_counter))); + + PbhCapabilities::capTable.set(PBH_RULE_CAPABILITIES_KEY, fvList); + + SWSS_LOG_NOTICE("Wrote PBH rule capabilities to State DB: %s key", key.c_str()); +} + +template<> +void PbhCapabilities::writePbhEntityCapabilitiesToDb(const std::shared_ptr &entityCap) +{ + SWSS_LOG_ENTER(); + + auto key = PbhCapabilities::capTable.getKeyName(PBH_HASH_CAPABILITIES_KEY); + std::vector fvList; + + fvList.push_back(FieldValueTuple(PBH_HASH_HASH_FIELD_LIST, toStr(entityCap->getPbhCap().hash_field_list))); + + PbhCapabilities::capTable.set(PBH_HASH_CAPABILITIES_KEY, fvList); + + SWSS_LOG_NOTICE("Wrote PBH hash capabilities to State DB: %s key", key.c_str()); +} + +template<> +void PbhCapabilities::writePbhEntityCapabilitiesToDb(const std::shared_ptr &entityCap) +{ + SWSS_LOG_ENTER(); + + auto key = PbhCapabilities::capTable.getKeyName(PBH_HASH_FIELD_CAPABILITIES_KEY); + std::vector fvList; + + fvList.push_back(FieldValueTuple(PBH_HASH_FIELD_HASH_FIELD, toStr(entityCap->getPbhCap().hash_field))); + fvList.push_back(FieldValueTuple(PBH_HASH_FIELD_IP_MASK, toStr(entityCap->getPbhCap().ip_mask))); + fvList.push_back(FieldValueTuple(PBH_HASH_FIELD_SEQUENCE_ID, toStr(entityCap->getPbhCap().sequence_id))); + + PbhCapabilities::capTable.set(PBH_HASH_FIELD_CAPABILITIES_KEY, fvList); + + SWSS_LOG_NOTICE("Wrote PBH hash field capabilities to State DB: %s key", key.c_str()); +} + +void PbhCapabilities::writePbhVendorCapabilitiesToDb() +{ + SWSS_LOG_ENTER(); + + this->writePbhEntityCapabilitiesToDb(this->table); + this->writePbhEntityCapabilitiesToDb(this->rule); + this->writePbhEntityCapabilitiesToDb(this->hash); + this->writePbhEntityCapabilitiesToDb(this->hashField); + + SWSS_LOG_NOTICE("Wrote PBH capabilities to State DB: %s table", STATE_PBH_CAPABILITIES_TABLE_NAME); +} + +bool PbhCapabilities::validatePbhTableCap(const std::vector &fieldList, PbhFieldCapability value) const +{ + SWSS_LOG_ENTER(); + + for (const auto &cit : fieldList) + { + if (cit == PBH_TABLE_INTERFACE_LIST) + { + if (!this->table->validatePbhInterfaceList(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_TABLE_DESCRIPTION) + { + if (!this->table->validatePbhDescription(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else + { + SWSS_LOG_WARN("Unknown field(%s): skipping ...", cit.c_str()); + } + } + + return true; +} + +bool PbhCapabilities::validatePbhRuleCap(const std::vector &fieldList, PbhFieldCapability value) const +{ + SWSS_LOG_ENTER(); + + for (const auto &cit : fieldList) + { + if (cit == PBH_RULE_PRIORITY) + { + if (!this->rule->validatePbhPriority(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_RULE_GRE_KEY) + { + if (!this->rule->validatePbhGreKey(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_RULE_ETHER_TYPE) + { + if (!this->rule->validatePbhEtherType(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_RULE_IP_PROTOCOL) + { + if (!this->rule->validatePbhIpProtocol(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_RULE_IPV6_NEXT_HEADER) + { + if (!this->rule->validatePbhIpv6NextHeader(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_RULE_L4_DST_PORT) + { + if (!this->rule->validatePbhL4DstPort(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_RULE_INNER_ETHER_TYPE) + { + if (!this->rule->validatePbhInnerEtherType(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_RULE_HASH) + { + if (!this->rule->validatePbhHash(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_RULE_PACKET_ACTION) + { + if (!this->rule->validatePbhPacketAction(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_RULE_FLOW_COUNTER) + { + if (!this->rule->validatePbhFlowCounter(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else + { + SWSS_LOG_WARN("Unknown field(%s): skipping ...", cit.c_str()); + } + } + + return true; +} + +bool PbhCapabilities::validatePbhHashCap(const std::vector &fieldList, PbhFieldCapability value) const +{ + SWSS_LOG_ENTER(); + + for (const auto &cit : fieldList) + { + if (cit == PBH_HASH_HASH_FIELD_LIST) + { + if (!this->hash->validatePbhHashFieldList(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else + { + SWSS_LOG_WARN("Unknown field(%s): skipping ...", cit.c_str()); + } + } + + return true; +} + +bool PbhCapabilities::validatePbhHashFieldCap(const std::vector &fieldList, PbhFieldCapability value) const +{ + SWSS_LOG_ENTER(); + + for (const auto &cit : fieldList) + { + if (cit == PBH_HASH_FIELD_HASH_FIELD) + { + if (!this->hashField->validatePbhHashField(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_HASH_FIELD_IP_MASK) + { + if (!this->hashField->validatePbhIpMask(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else if (cit == PBH_HASH_FIELD_SEQUENCE_ID) + { + if (!this->hashField->validatePbhSequenceId(value)) + { + SWSS_LOG_ERROR("Failed to validate field(%s): capability(%s) is not supported", + cit.c_str(), + toStr(value).c_str() + ); + return false; + } + } + else + { + SWSS_LOG_WARN("Unknown field(%s): skipping ...", cit.c_str()); + } + } + + return true; +} diff --git a/orchagent/pbh/pbhcap.h b/orchagent/pbh/pbhcap.h new file mode 100644 index 0000000000..adc2a4c9e6 --- /dev/null +++ b/orchagent/pbh/pbhcap.h @@ -0,0 +1,186 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include "dbconnector.h" +#include "table.h" + +enum class PbhAsicVendor : std::int32_t +{ + GENERIC, + MELLANOX +}; + +enum class PbhFieldCapability : std::int32_t +{ + ADD, + UPDATE, + REMOVE +}; + +class PbhVendorFieldCapabilities +{ +public: + PbhVendorFieldCapabilities() = default; + virtual ~PbhVendorFieldCapabilities() = default; + +protected: + void setPbhDefaults(std::set &fieldCap) noexcept; + +public: + struct { + std::set interface_list; + std::set description; + } table; + + struct { + std::set priority; + std::set gre_key; + std::set ether_type; + std::set ip_protocol; + std::set ipv6_next_header; + std::set l4_dst_port; + std::set inner_ether_type; + std::set hash; + std::set packet_action; + std::set flow_counter; + } rule; + + struct { + std::set hash_field_list; + } hash; + + struct { + std::set hash_field; + std::set ip_mask; + std::set sequence_id; + } hashField; +}; + +class PbhGenericFieldCapabilities final : public PbhVendorFieldCapabilities +{ +public: + PbhGenericFieldCapabilities() noexcept; + ~PbhGenericFieldCapabilities() = default; +}; + +class PbhMellanoxFieldCapabilities final : public PbhVendorFieldCapabilities +{ +public: + PbhMellanoxFieldCapabilities() noexcept; + ~PbhMellanoxFieldCapabilities() = default; +}; + +class PbhEntityCapabilities +{ +public: + PbhEntityCapabilities() = delete; + virtual ~PbhEntityCapabilities() = default; + + PbhEntityCapabilities(const std::shared_ptr &fieldCap) noexcept; + +protected: + bool validate(const std::set &fieldCap, PbhFieldCapability value) const; + + std::shared_ptr fieldCap; +}; + +class PbhTableCapabilities final : public PbhEntityCapabilities +{ +public: + PbhTableCapabilities() = delete; + ~PbhTableCapabilities() = default; + + PbhTableCapabilities(const std::shared_ptr &fieldCap) noexcept; + + bool validatePbhInterfaceList(PbhFieldCapability value) const; + bool validatePbhDescription(PbhFieldCapability value) const; + + auto getPbhCap() const -> const decltype(PbhVendorFieldCapabilities::table) &; +}; + +class PbhRuleCapabilities final : public PbhEntityCapabilities +{ +public: + PbhRuleCapabilities() = delete; + ~PbhRuleCapabilities() = default; + + PbhRuleCapabilities(const std::shared_ptr &fieldCap) noexcept; + + bool validatePbhPriority(PbhFieldCapability value) const; + bool validatePbhGreKey(PbhFieldCapability value) const; + bool validatePbhEtherType(PbhFieldCapability value) const; + bool validatePbhIpProtocol(PbhFieldCapability value) const; + bool validatePbhIpv6NextHeader(PbhFieldCapability value) const; + bool validatePbhL4DstPort(PbhFieldCapability value) const; + bool validatePbhInnerEtherType(PbhFieldCapability value) const; + bool validatePbhHash(PbhFieldCapability value) const; + bool validatePbhPacketAction(PbhFieldCapability value) const; + bool validatePbhFlowCounter(PbhFieldCapability value) const; + + auto getPbhCap() const -> const decltype(PbhVendorFieldCapabilities::rule) &; +}; + +class PbhHashCapabilities final : public PbhEntityCapabilities +{ +public: + PbhHashCapabilities() = delete; + ~PbhHashCapabilities() = default; + + PbhHashCapabilities(const std::shared_ptr &fieldCap) noexcept; + + bool validatePbhHashFieldList(PbhFieldCapability value) const; + + auto getPbhCap() const -> const decltype(PbhVendorFieldCapabilities::hash) &; +}; + +class PbhHashFieldCapabilities final : public PbhEntityCapabilities +{ +public: + PbhHashFieldCapabilities() = delete; + ~PbhHashFieldCapabilities() = default; + + PbhHashFieldCapabilities(const std::shared_ptr &fieldCap) noexcept; + + bool validatePbhHashField(PbhFieldCapability value) const; + bool validatePbhIpMask(PbhFieldCapability value) const; + bool validatePbhSequenceId(PbhFieldCapability value) const; + + auto getPbhCap() const -> const decltype(PbhVendorFieldCapabilities::hashField) &; +}; + +class PbhCapabilities final +{ +public: + PbhCapabilities() noexcept; + ~PbhCapabilities() = default; + + bool validatePbhTableCap(const std::vector &fieldList, PbhFieldCapability value) const; + bool validatePbhRuleCap(const std::vector &fieldList, PbhFieldCapability value) const; + bool validatePbhHashCap(const std::vector &fieldList, PbhFieldCapability value) const; + bool validatePbhHashFieldCap(const std::vector &fieldList, PbhFieldCapability value) const; + + PbhAsicVendor getAsicVendor() const noexcept; + +private: + template + void writePbhEntityCapabilitiesToDb(const std::shared_ptr &entityCap); + + bool parsePbhAsicVendor(); + void initPbhVendorCapabilities(); + void writePbhVendorCapabilitiesToDb(); + + PbhAsicVendor asicVendor; + + std::shared_ptr table; + std::shared_ptr rule; + std::shared_ptr hash; + std::shared_ptr hashField; + + static swss::DBConnector stateDb; + static swss::Table capTable; +}; diff --git a/orchagent/pbh/pbhcnt.h b/orchagent/pbh/pbhcnt.h index 787d91b63c..90c40bb681 100644 --- a/orchagent/pbh/pbhcnt.h +++ b/orchagent/pbh/pbhcnt.h @@ -105,19 +105,22 @@ class PbhRule final : public PbhContainer } inner_ether_type; struct { + struct { + std::string name; + } meta; std::string value; bool is_set = false; } hash; struct { + struct { + std::string name; + } meta; sai_acl_entry_attr_t value; bool is_set = false; } packet_action; struct { - struct { - std::string name; - } meta; bool value; bool is_set = false; } flow_counter; diff --git a/orchagent/pbh/pbhmgr.cpp b/orchagent/pbh/pbhmgr.cpp index ed10ff756c..8dfb8e09f8 100644 --- a/orchagent/pbh/pbhmgr.cpp +++ b/orchagent/pbh/pbhmgr.cpp @@ -7,6 +7,7 @@ #include #include +#include "pbhschema.h" #include "ipaddress.h" #include "converter.h" #include "tokenize.h" @@ -16,42 +17,6 @@ using namespace swss; -// defines ------------------------------------------------------------------------------------------------------------ - -#define PBH_TABLE_INTERFACE_LIST "interface_list" -#define PBH_TABLE_DESCRIPTION "description" - -#define PBH_RULE_PACKET_ACTION_SET_ECMP_HASH "SET_ECMP_HASH" -#define PBH_RULE_PACKET_ACTION_SET_LAG_HASH "SET_LAG_HASH" - -#define PBH_RULE_FLOW_COUNTER_ENABLED "ENABLED" -#define PBH_RULE_FLOW_COUNTER_DISABLED "DISABLED" - -#define PBH_RULE_PRIORITY "priority" -#define PBH_RULE_GRE_KEY "gre_key" -#define PBH_RULE_ETHER_TYPE "ether_type" -#define PBH_RULE_IP_PROTOCOL "ip_protocol" -#define PBH_RULE_IPV6_NEXT_HEADER "ipv6_next_header" -#define PBH_RULE_L4_DST_PORT "l4_dst_port" -#define PBH_RULE_INNER_ETHER_TYPE "inner_ether_type" -#define PBH_RULE_HASH "hash" -#define PBH_RULE_PACKET_ACTION "packet_action" -#define PBH_RULE_FLOW_COUNTER "flow_counter" - -#define PBH_HASH_HASH_FIELD_LIST "hash_field_list" - -#define PBH_HASH_FIELD_HASH_FIELD_INNER_IP_PROTOCOL "INNER_IP_PROTOCOL" -#define PBH_HASH_FIELD_HASH_FIELD_INNER_L4_DST_PORT "INNER_L4_DST_PORT" -#define PBH_HASH_FIELD_HASH_FIELD_INNER_L4_SRC_PORT "INNER_L4_SRC_PORT" -#define PBH_HASH_FIELD_HASH_FIELD_INNER_DST_IPV4 "INNER_DST_IPV4" -#define PBH_HASH_FIELD_HASH_FIELD_INNER_SRC_IPV4 "INNER_SRC_IPV4" -#define PBH_HASH_FIELD_HASH_FIELD_INNER_DST_IPV6 "INNER_DST_IPV6" -#define PBH_HASH_FIELD_HASH_FIELD_INNER_SRC_IPV6 "INNER_SRC_IPV6" - -#define PBH_HASH_FIELD_HASH_FIELD "hash_field" -#define PBH_HASH_FIELD_IP_MASK "ip_mask" -#define PBH_HASH_FIELD_SEQUENCE_ID "sequence_id" - // constants ---------------------------------------------------------------------------------------------------------- static const std::unordered_map pbhRulePacketActionMap = @@ -712,6 +677,7 @@ bool PbhHelper::parsePbhRuleHash(PbhRule &rule, const std::string &field, const return false; } + rule.hash.meta.name = field; rule.hash.value = value; rule.hash.is_set = true; @@ -729,6 +695,7 @@ bool PbhHelper::parsePbhRulePacketAction(PbhRule &rule, const std::string &field return false; } + rule.packet_action.meta.name = field; rule.packet_action.value = cit->second; rule.packet_action.is_set = true; @@ -746,7 +713,6 @@ bool PbhHelper::parsePbhRuleFlowCounter(PbhRule &rule, const std::string &field, return false; } - rule.flow_counter.meta.name = field; rule.flow_counter.value = cit->second; rule.flow_counter.is_set = true; @@ -1036,6 +1002,7 @@ bool PbhHelper::validatePbhRule(PbhRule &rule) const PBH_RULE_PACKET_ACTION_SET_ECMP_HASH ); + rule.packet_action.meta.name = PBH_RULE_PACKET_ACTION; rule.packet_action.value = SAI_ACL_ENTRY_ATTR_ACTION_SET_ECMP_HASH_ID; rule.packet_action.is_set = true; @@ -1049,7 +1016,7 @@ bool PbhHelper::validatePbhRule(PbhRule &rule) const PBH_RULE_FLOW_COUNTER, PBH_RULE_FLOW_COUNTER_DISABLED ); - rule.flow_counter.meta.name = PBH_RULE_FLOW_COUNTER; + rule.flow_counter.value = false; rule.flow_counter.is_set = true; diff --git a/orchagent/pbh/pbhrule.cpp b/orchagent/pbh/pbhrule.cpp index 7d35f4bb8f..0b00e40e44 100644 --- a/orchagent/pbh/pbhrule.cpp +++ b/orchagent/pbh/pbhrule.cpp @@ -98,3 +98,50 @@ bool AclRulePbh::validateAddAction(string attr_name, string attr_value) { SWSS_LOG_THROW("This API should not be used on PbhRule"); } + +bool AclRulePbh::disableAction() +{ + const auto &cit1 = m_actions.find(SAI_ACL_ENTRY_ATTR_ACTION_SET_ECMP_HASH_ID); + if (cit1 != m_actions.cend()) + { + const auto &attr1 = cit1->second.getSaiAttr(); + if (attr1.value.aclaction.enable) + { + sai_attribute_t attr; + + attr.id = attr1.id; + attr.value.aclaction.enable = false; + attr.value.aclaction.parameter.oid = SAI_NULL_OBJECT_ID; + + if (!setAttribute(attr)) + { + return false; + } + + m_actions.erase(cit1); + } + } + + const auto &cit2 = m_actions.find(SAI_ACL_ENTRY_ATTR_ACTION_SET_LAG_HASH_ID); + if (cit2 != m_actions.cend()) + { + const auto &attr2 = cit2->second.getSaiAttr(); + if (attr2.value.aclaction.enable) + { + sai_attribute_t attr; + + attr.id = attr2.id; + attr.value.aclaction.enable = false; + attr.value.aclaction.parameter.oid = SAI_NULL_OBJECT_ID; + + if (!setAttribute(attr)) + { + return false; + } + + m_actions.erase(cit2); + } + } + + return true; +} diff --git a/orchagent/pbh/pbhrule.h b/orchagent/pbh/pbhrule.h index 9e661761c4..5fa5ddf1fc 100644 --- a/orchagent/pbh/pbhrule.h +++ b/orchagent/pbh/pbhrule.h @@ -13,4 +13,5 @@ class AclRulePbh: public AclRule bool validate() override; void onUpdate(SubjectType, void *) override; bool validateAddAction(string attr_name, string attr_value) override; + bool disableAction(); }; diff --git a/orchagent/pbh/pbhschema.h b/orchagent/pbh/pbhschema.h new file mode 100644 index 0000000000..3ea280f769 --- /dev/null +++ b/orchagent/pbh/pbhschema.h @@ -0,0 +1,37 @@ +#pragma once + +// defines ------------------------------------------------------------------------------------------------------------ + +#define PBH_TABLE_INTERFACE_LIST "interface_list" +#define PBH_TABLE_DESCRIPTION "description" + +#define PBH_RULE_PACKET_ACTION_SET_ECMP_HASH "SET_ECMP_HASH" +#define PBH_RULE_PACKET_ACTION_SET_LAG_HASH "SET_LAG_HASH" + +#define PBH_RULE_FLOW_COUNTER_ENABLED "ENABLED" +#define PBH_RULE_FLOW_COUNTER_DISABLED "DISABLED" + +#define PBH_RULE_PRIORITY "priority" +#define PBH_RULE_GRE_KEY "gre_key" +#define PBH_RULE_ETHER_TYPE "ether_type" +#define PBH_RULE_IP_PROTOCOL "ip_protocol" +#define PBH_RULE_IPV6_NEXT_HEADER "ipv6_next_header" +#define PBH_RULE_L4_DST_PORT "l4_dst_port" +#define PBH_RULE_INNER_ETHER_TYPE "inner_ether_type" +#define PBH_RULE_HASH "hash" +#define PBH_RULE_PACKET_ACTION "packet_action" +#define PBH_RULE_FLOW_COUNTER "flow_counter" + +#define PBH_HASH_HASH_FIELD_LIST "hash_field_list" + +#define PBH_HASH_FIELD_HASH_FIELD_INNER_IP_PROTOCOL "INNER_IP_PROTOCOL" +#define PBH_HASH_FIELD_HASH_FIELD_INNER_L4_DST_PORT "INNER_L4_DST_PORT" +#define PBH_HASH_FIELD_HASH_FIELD_INNER_L4_SRC_PORT "INNER_L4_SRC_PORT" +#define PBH_HASH_FIELD_HASH_FIELD_INNER_DST_IPV4 "INNER_DST_IPV4" +#define PBH_HASH_FIELD_HASH_FIELD_INNER_SRC_IPV4 "INNER_SRC_IPV4" +#define PBH_HASH_FIELD_HASH_FIELD_INNER_DST_IPV6 "INNER_DST_IPV6" +#define PBH_HASH_FIELD_HASH_FIELD_INNER_SRC_IPV6 "INNER_SRC_IPV6" + +#define PBH_HASH_FIELD_HASH_FIELD "hash_field" +#define PBH_HASH_FIELD_IP_MASK "ip_mask" +#define PBH_HASH_FIELD_SEQUENCE_ID "sequence_id" diff --git a/orchagent/pbhorch.cpp b/orchagent/pbhorch.cpp index 83a1e80bd0..e2146cb362 100644 --- a/orchagent/pbhorch.cpp +++ b/orchagent/pbhorch.cpp @@ -53,7 +53,26 @@ static inline std::vector uMapDiffByKey(const umap_t &uMap1, const umap const auto &s1 = uMapToKeySet(uMap1); const auto &s2 = uMapToKeySet(uMap2); - std::set_symmetric_difference( + std::set_difference( + s1.cbegin(), + s1.cend(), + s2.cbegin(), + s2.cend(), + std::back_inserter(v) + ); + + return v; +} + +template +static inline std::vector uMapIntersectByKey(const umap_t &uMap1, const umap_t &uMap2) +{ + std::vector v; + + const auto &s1 = uMapToKeySet(uMap1); + const auto &s2 = uMapToKeySet(uMap2); + + std::set_intersection( s1.cbegin(), s1.cend(), s2.cbegin(), @@ -76,11 +95,52 @@ PbhOrch::PbhOrch( this->portsOrch = portsOrch; } -PbhOrch::~PbhOrch() +template +std::vector PbhOrch::getPbhAddedFields(const T &obj, const T &nObj) const +{ + return uMapDiffByKey(nObj.fieldValueMap, obj.fieldValueMap); +} + +template std::vector PbhOrch::getPbhAddedFields(const PbhTable &obj, const PbhTable &nObj) const; +template std::vector PbhOrch::getPbhAddedFields(const PbhRule &obj, const PbhRule &nObj) const; +template std::vector PbhOrch::getPbhAddedFields(const PbhHash &obj, const PbhHash &nObj) const; +template std::vector PbhOrch::getPbhAddedFields(const PbhHashField &obj, const PbhHashField &nObj) const; + +template +std::vector PbhOrch::getPbhUpdatedFields(const T &obj, const T &nObj) const { + std::vector v; + + const auto &iv = uMapIntersectByKey(obj.fieldValueMap, nObj.fieldValueMap); + std::copy_if( + iv.cbegin(), + iv.cend(), + std::back_inserter(v), + [&obj, &nObj](const auto &f) { + return obj.fieldValueMap.at(f) != nObj.fieldValueMap.at(f); + } + ); + + return v; } +template std::vector PbhOrch::getPbhUpdatedFields(const PbhTable &obj, const PbhTable &nObj) const; +template std::vector PbhOrch::getPbhUpdatedFields(const PbhRule &obj, const PbhRule &nObj) const; +template std::vector PbhOrch::getPbhUpdatedFields(const PbhHash &obj, const PbhHash &nObj) const; +template std::vector PbhOrch::getPbhUpdatedFields(const PbhHashField &obj, const PbhHashField &nObj) const; + +template +std::vector PbhOrch::getPbhRemovedFields(const T &obj, const T &nObj) const +{ + return uMapDiffByKey(obj.fieldValueMap, nObj.fieldValueMap); +} + +template std::vector PbhOrch::getPbhRemovedFields(const PbhTable &obj, const PbhTable &nObj) const; +template std::vector PbhOrch::getPbhRemovedFields(const PbhRule &obj, const PbhRule &nObj) const; +template std::vector PbhOrch::getPbhRemovedFields(const PbhHash &obj, const PbhHash &nObj) const; +template std::vector PbhOrch::getPbhRemovedFields(const PbhHashField &obj, const PbhHashField &nObj) const; + template<> auto PbhOrch::getPbhSetupTaskMap() const -> const std::unordered_map& { @@ -252,6 +312,34 @@ bool PbhOrch::updatePbhTable(const PbhTable &table) return false; } + const auto &aFields = this->getPbhAddedFields(tObj, table); + const auto &uFields = this->getPbhUpdatedFields(tObj, table); + const auto &rFields = this->getPbhRemovedFields(tObj, table); + + if (aFields.empty() && uFields.empty() && rFields.empty()) + { + SWSS_LOG_NOTICE("PBH table(%s) in SAI is up-to-date", table.key.c_str()); + return true; + } + + if (!this->pbhCap.validatePbhTableCap(aFields, PbhFieldCapability::ADD)) + { + SWSS_LOG_ERROR("Failed to validate PBH table(%s) added fields: unsupported capabilities", table.key.c_str()); + return false; + } + + if (!this->pbhCap.validatePbhTableCap(uFields, PbhFieldCapability::UPDATE)) + { + SWSS_LOG_ERROR("Failed to validate PBH table(%s) updated fields: unsupported capabilities", table.key.c_str()); + return false; + } + + if (!this->pbhCap.validatePbhTableCap(rFields, PbhFieldCapability::REMOVE)) + { + SWSS_LOG_ERROR("Failed to validate PBH table(%s) removed fields: unsupported capabilities", table.key.c_str()); + return false; + } + AclTable pbhTable(this->aclOrch, table.name); if (table.interface_list.is_set) @@ -577,57 +665,227 @@ bool PbhOrch::updatePbhRule(const PbhRule &rule) return false; } - if (!uMapDiffByKey(rObj.fieldValueMap, rule.fieldValueMap).empty()) + const auto &aFields = this->getPbhAddedFields(rObj, rule); + const auto &uFields = this->getPbhUpdatedFields(rObj, rule); + const auto &rFields = this->getPbhRemovedFields(rObj, rule); + + if (aFields.empty() && uFields.empty() && rFields.empty()) + { + SWSS_LOG_NOTICE("PBH rule(%s) in SAI is up-to-date", rule.key.c_str()); + return true; + } + + if (!this->pbhCap.validatePbhRuleCap(aFields, PbhFieldCapability::ADD)) { - SWSS_LOG_ERROR("Failed to update PBH rule(%s) in SAI: fields add/remove is prohibited", rule.key.c_str()); + SWSS_LOG_ERROR("Failed to validate PBH rule(%s) added fields: unsupported capabilities", rule.key.c_str()); return false; } - bool flowCounterUpdate = false; + if (!this->pbhCap.validatePbhRuleCap(uFields, PbhFieldCapability::UPDATE)) + { + SWSS_LOG_ERROR("Failed to validate PBH rule(%s) updated fields: unsupported capabilities", rule.key.c_str()); + return false; + } - for (const auto &oCit : rObj.fieldValueMap) + if (!this->pbhCap.validatePbhRuleCap(rFields, PbhFieldCapability::REMOVE)) { - const auto &field = oCit.first; + SWSS_LOG_ERROR("Failed to validate PBH rule(%s) removed fields: unsupported capabilities", rule.key.c_str()); + return false; + } - const auto &oValue = oCit.second; - const auto &nValue = rule.fieldValueMap.at(field); + std::shared_ptr pbhRule; + + if (rule.flow_counter.is_set) + { + pbhRule = std::make_shared(this->aclOrch, rule.name, rule.table, rule.flow_counter.value); + } + else + { + pbhRule = std::make_shared(this->aclOrch, rule.name, rule.table); + } - if (oValue == nValue) + if (rule.priority.is_set) + { + if (!pbhRule->validateAddPriority(rule.priority.value)) { - continue; + SWSS_LOG_ERROR("Failed to configure PBH rule(%s) priority", rule.key.c_str()); + return false; } + } + + if (rule.gre_key.is_set) + { + sai_attribute_t attr; - if (field != rule.flow_counter.meta.name) + attr.id = SAI_ACL_ENTRY_ATTR_FIELD_GRE_KEY; + attr.value.aclfield.enable = true; + attr.value.aclfield.data.u32 = rule.gre_key.value; + attr.value.aclfield.mask.u32 = rule.gre_key.mask; + + if (!pbhRule->validateAddMatch(attr)) { - SWSS_LOG_ERROR( - "Failed to update PBH rule(%s) in SAI: field(%s) update is prohibited", - rule.key.c_str(), - field.c_str() - ); + SWSS_LOG_ERROR("Failed to configure PBH rule(%s) match: GRE_KEY", rule.key.c_str()); return false; } + } - flowCounterUpdate = true; + if (rule.ether_type.is_set) + { + sai_attribute_t attr; + + attr.id = SAI_ACL_ENTRY_ATTR_FIELD_ETHER_TYPE; + attr.value.aclfield.enable = true; + attr.value.aclfield.data.u16 = rule.ether_type.value; + attr.value.aclfield.mask.u16 = rule.ether_type.mask; + + if (!pbhRule->validateAddMatch(attr)) + { + SWSS_LOG_ERROR("Failed to configure PBH rule(%s) match: ETHER_TYPE", rule.key.c_str()); + return false; + } } - if (!flowCounterUpdate) + if (rule.ip_protocol.is_set) { - SWSS_LOG_NOTICE("PBH rule(%s) in SAI is up-to-date", rule.key.c_str()); - return true; + sai_attribute_t attr; + + attr.id = SAI_ACL_ENTRY_ATTR_FIELD_IP_PROTOCOL; + attr.value.aclfield.enable = true; + attr.value.aclfield.data.u8 = rule.ip_protocol.value; + attr.value.aclfield.mask.u8 = rule.ip_protocol.mask; + + if (!pbhRule->validateAddMatch(attr)) + { + SWSS_LOG_ERROR("Failed to configure PBH rule(%s) match: IP_PROTOCOL", rule.key.c_str()); + return false; + } + } + + if (rule.ipv6_next_header.is_set) + { + sai_attribute_t attr; + + attr.id = SAI_ACL_ENTRY_ATTR_FIELD_IPV6_NEXT_HEADER; + attr.value.aclfield.enable = true; + attr.value.aclfield.data.u8 = rule.ipv6_next_header.value; + attr.value.aclfield.mask.u8 = rule.ipv6_next_header.mask; + + if (!pbhRule->validateAddMatch(attr)) + { + SWSS_LOG_ERROR("Failed to configure PBH rule(%s) match: IPV6_NEXT_HEADER", rule.key.c_str()); + return false; + } + } + + if (rule.l4_dst_port.is_set) + { + sai_attribute_t attr; + + attr.id = SAI_ACL_ENTRY_ATTR_FIELD_L4_DST_PORT; + attr.value.aclfield.enable = true; + attr.value.aclfield.data.u16 = rule.l4_dst_port.value; + attr.value.aclfield.mask.u16 = rule.l4_dst_port.mask; + + if (!pbhRule->validateAddMatch(attr)) + { + SWSS_LOG_ERROR("Failed to configure PBH rule(%s) match: L4_DST_PORT", rule.key.c_str()); + return false; + } + } + + if (rule.inner_ether_type.is_set) + { + sai_attribute_t attr; + + attr.id = SAI_ACL_ENTRY_ATTR_FIELD_INNER_ETHER_TYPE; + attr.value.aclfield.enable = true; + attr.value.aclfield.data.u16 = rule.inner_ether_type.value; + attr.value.aclfield.mask.u16 = rule.inner_ether_type.mask; + + if (!pbhRule->validateAddMatch(attr)) + { + SWSS_LOG_ERROR("Failed to configure PBH rule(%s) match: INNER_ETHER_TYPE", rule.key.c_str()); + return false; + } } - if (!this->aclOrch->updateAclRule(rule.table, rule.name, rule.flow_counter.value)) + if (rule.hash.is_set && rule.packet_action.is_set) + { + PbhHash hObj; + + if (this->pbhHlpr.getPbhHash(hObj, rule.hash.value)) + { + sai_attribute_t attr; + + attr.id = rule.packet_action.value; + attr.value.aclaction.enable = true; + attr.value.aclaction.parameter.oid = hObj.getOid(); + + if (!pbhRule->validateAddAction(attr)) + { + SWSS_LOG_ERROR("Failed to configure PBH rule(%s) action", rule.key.c_str()); + return false; + } + } + } + + if (!pbhRule->validate()) + { + SWSS_LOG_ERROR("Failed to validate PBH rule(%s)", rule.key.c_str()); + return false; + } + + // Mellanox W/A + if (this->pbhCap.getAsicVendor() == PbhAsicVendor::MELLANOX) + { + const auto &hMeta = rule.hash.meta; + const auto &paMeta = rule.packet_action.meta; + + auto cond1 = std::find(uFields.cbegin(), uFields.cend(), hMeta.name) != uFields.cend(); + auto cond2 = std::find(uFields.cbegin(), uFields.cend(), paMeta.name) != uFields.cend(); + + if (cond1 || cond2) + { + auto pbhRulePtr = dynamic_cast(this->aclOrch->getAclRule(rule.table, rule.name)); + + if (pbhRulePtr == nullptr) + { + SWSS_LOG_ERROR("Failed to update PBH rule(%s) in SAI: invalid object type", rule.key.c_str()); + return false; + } + + if (!pbhRulePtr->disableAction()) + { + SWSS_LOG_ERROR("Failed to disable PBH rule(%s) action", rule.key.c_str()); + return false; + } + } + } + + if (!this->aclOrch->updateAclRule(pbhRule)) { SWSS_LOG_ERROR("Failed to update PBH rule(%s) in SAI", rule.key.c_str()); return false; } + if (!this->pbhHlpr.decRefCount(rObj)) + { + SWSS_LOG_ERROR("Failed to remove PBH rule(%s) dependencies", rObj.key.c_str()); + return false; + } + if (!this->pbhHlpr.updatePbhRule(rule)) { SWSS_LOG_ERROR("Failed to update PBH rule(%s) in internal cache", rule.key.c_str()); return false; } + if (!this->pbhHlpr.incRefCount(rule)) + { + SWSS_LOG_ERROR("Failed to add PBH rule(%s) dependencies", rule.key.c_str()); + return false; + } + SWSS_LOG_NOTICE("Updated PBH rule(%s) in SAI", rule.key.c_str()); return true; @@ -832,31 +1090,98 @@ bool PbhOrch::updatePbhHash(const PbhHash &hash) return false; } - if (!uMapDiffByKey(hObj.fieldValueMap, hash.fieldValueMap).empty()) + const auto &aFields = this->getPbhAddedFields(hObj, hash); + const auto &uFields = this->getPbhUpdatedFields(hObj, hash); + const auto &rFields = this->getPbhRemovedFields(hObj, hash); + + if (aFields.empty() && uFields.empty() && rFields.empty()) { - SWSS_LOG_ERROR("Failed to update PBH hash(%s) in SAI: fields add/remove is prohibited", hash.key.c_str()); + SWSS_LOG_NOTICE("PBH hash(%s) in SAI is up-to-date", hash.key.c_str()); + return true; + } + + if (!this->pbhCap.validatePbhHashCap(aFields, PbhFieldCapability::ADD)) + { + SWSS_LOG_ERROR("Failed to validate PBH hash(%s) added fields: unsupported capabilities", hash.key.c_str()); + return false; + } + + if (!this->pbhCap.validatePbhHashCap(uFields, PbhFieldCapability::UPDATE)) + { + SWSS_LOG_ERROR("Failed to validate PBH hash(%s) updated fields: unsupported capabilities", hash.key.c_str()); return false; } - for (const auto &oCit : hObj.fieldValueMap) + if (!this->pbhCap.validatePbhHashCap(rFields, PbhFieldCapability::REMOVE)) { - const auto &field = oCit.first; + SWSS_LOG_ERROR("Failed to validate PBH hash(%s) removed fields: unsupported capabilities", hash.key.c_str()); + return false; + } - const auto &oValue = oCit.second; - const auto &nValue = hash.fieldValueMap.at(field); + std::vector hashFieldOidList; - if (oValue != nValue) + if (hash.hash_field_list.is_set) + { + for (const auto &cit : hash.hash_field_list.value) { - SWSS_LOG_ERROR( - "Failed to update PBH hash(%s) in SAI: field(%s) update is prohibited", - hash.key.c_str(), - field.c_str() - ); - return false; + PbhHashField hfObj; + + if (!this->pbhHlpr.getPbhHashField(hfObj, cit)) + { + SWSS_LOG_ERROR( + "Failed to update PBH hash(%s) in SAI: missing hash field(%s)", + hash.key.c_str(), + cit.c_str() + ); + return false; + } + + hashFieldOidList.push_back(hfObj.getOid()); } } - SWSS_LOG_NOTICE("PBH hash(%s) in SAI is up-to-date", hash.key.c_str()); + if (hashFieldOidList.empty()) + { + SWSS_LOG_ERROR("Failed to update PBH hash(%s) in SAI: missing hash fields", hash.key.c_str()); + return false; + } + + sai_attribute_t attr; + + attr.id = SAI_HASH_ATTR_FINE_GRAINED_HASH_FIELD_LIST; + attr.value.objlist.count = static_cast(hashFieldOidList.size()); + attr.value.objlist.list = hashFieldOidList.data(); + + sai_status_t status; + + status = sai_hash_api->set_hash_attribute(hObj.getOid(), &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to update PBH hash(%s) in SAI", hash.key.c_str()); + return false; + } + + if (!this->pbhHlpr.decRefCount(hObj)) + { + SWSS_LOG_ERROR("Failed to remove PBH hash(%s) dependencies", hObj.key.c_str()); + return false; + } + + hObj.hash_field_list = hash.hash_field_list; + + if (!this->pbhHlpr.updatePbhHash(hObj)) + { + SWSS_LOG_ERROR("Failed to update PBH hash(%s) in internal cache", hObj.key.c_str()); + return false; + } + + if (!this->pbhHlpr.incRefCount(hObj)) + { + SWSS_LOG_ERROR("Failed to add PBH hash(%s) dependencies", hObj.key.c_str()); + return false; + } + + SWSS_LOG_NOTICE("Updated PBH hash(%s) in SAI", hObj.key.c_str()); return true; } @@ -1072,33 +1397,37 @@ bool PbhOrch::updatePbhHashField(const PbhHashField &hashField) return false; } - if (!uMapDiffByKey(hfObj.fieldValueMap, hashField.fieldValueMap).empty()) + const auto &aFields = this->getPbhAddedFields(hfObj, hashField); + const auto &uFields = this->getPbhUpdatedFields(hfObj, hashField); + const auto &rFields = this->getPbhRemovedFields(hfObj, hashField); + + if (aFields.empty() && uFields.empty() && rFields.empty()) { - SWSS_LOG_ERROR("Failed to update PBH hash field(%s) in SAI: fields add/remove is prohibited", hashField.key.c_str()); - return false; + SWSS_LOG_NOTICE("PBH hash field(%s) in SAI is up-to-date", hashField.key.c_str()); + return true; } - for (const auto &oCit : hfObj.fieldValueMap) + if (!this->pbhCap.validatePbhHashFieldCap(aFields, PbhFieldCapability::ADD)) { - const auto &field = oCit.first; + SWSS_LOG_ERROR("Failed to validate PBH hash field(%s) added fields: unsupported capabilities", hashField.key.c_str()); + return false; + } - const auto &oValue = oCit.second; - const auto &nValue = hashField.fieldValueMap.at(field); + if (!this->pbhCap.validatePbhHashFieldCap(uFields, PbhFieldCapability::UPDATE)) + { + SWSS_LOG_ERROR("Failed to validate PBH hash field(%s) updated fields: unsupported capabilities", hashField.key.c_str()); + return false; + } - if (oValue != nValue) - { - SWSS_LOG_ERROR( - "Failed to update PBH hash field(%s) in SAI: field(%s) update is prohibited", - hashField.key.c_str(), - field.c_str() - ); - return false; - } + if (!this->pbhCap.validatePbhHashFieldCap(rFields, PbhFieldCapability::REMOVE)) + { + SWSS_LOG_ERROR("Failed to validate PBH hash field(%s) removed fields: unsupported capabilities", hashField.key.c_str()); + return false; } - SWSS_LOG_NOTICE("PBH hash field(%s) in SAI is up-to-date", hashField.key.c_str()); + SWSS_LOG_ERROR("Failed to update PBH hash field(%s) in SAI: update is prohibited", hfObj.key.c_str()); - return true; + return false; } bool PbhOrch::removePbhHashField(const PbhHashField &hashField) diff --git a/orchagent/pbhorch.h b/orchagent/pbhorch.h index 1aa49e1d26..250963f54a 100644 --- a/orchagent/pbhorch.h +++ b/orchagent/pbhorch.h @@ -8,20 +8,30 @@ #include "pbh/pbhrule.h" #include "pbh/pbhmgr.h" +#include "pbh/pbhcap.h" class PbhOrch final : public Orch { public: + PbhOrch() = delete; + ~PbhOrch() = default; + PbhOrch( std::vector &connectorList, AclOrch *aclOrch, PortsOrch *portsOrch ); - ~PbhOrch(); using Orch::doTask; // Allow access to the basic doTask private: + template + std::vector getPbhAddedFields(const T &obj, const T &nObj) const; + template + std::vector getPbhUpdatedFields(const T &obj, const T &nObj) const; + template + std::vector getPbhRemovedFields(const T &obj, const T &nObj) const; + template auto getPbhSetupTaskMap() const -> const std::unordered_map&; template @@ -75,4 +85,5 @@ class PbhOrch final : public Orch PortsOrch *portsOrch; PbhHelper pbhHlpr; + PbhCapabilities pbhCap; }; diff --git a/orchagent/pfcactionhandler.cpp b/orchagent/pfcactionhandler.cpp index 05f2d6ef56..6fb497812d 100644 --- a/orchagent/pfcactionhandler.cpp +++ b/orchagent/pfcactionhandler.cpp @@ -3,6 +3,7 @@ #include "logger.h" #include "sai_serialize.h" #include "portsorch.h" +#include "bufferorch.h" #include #include @@ -26,6 +27,7 @@ extern sai_object_id_t gSwitchId; extern PortsOrch *gPortsOrch; extern AclOrch * gAclOrch; +extern BufferOrch *gBufferOrch; extern sai_port_api_t *sai_port_api; extern sai_queue_api_t *sai_queue_api; extern sai_buffer_api_t *sai_buffer_api; @@ -715,6 +717,25 @@ PfcWdZeroBufferHandler::ZeroBufferProfile &PfcWdZeroBufferHandler::ZeroBufferPro return instance; } +sai_object_id_t& PfcWdZeroBufferHandler::ZeroBufferProfile::getPool(bool ingress) +{ + // If there is a cached zero buffer pool, just use it + // else fetch zero buffer pool from buffer orch + // If there is one, use it and increase the reference number. + // otherwise, just return NULL OID + // PfcWdZeroBufferHandler will create it later and notify buffer orch later + auto &poolId = ingress ? m_zeroIngressBufferPool : m_zeroEgressBufferPool; + if (poolId == SAI_NULL_OBJECT_ID) + { + poolId = gBufferOrch->getZeroBufferPool(ingress); + if (poolId != SAI_NULL_OBJECT_ID) + { + gBufferOrch->lockZeroBufferPool(ingress); + } + } + return poolId; +} + sai_object_id_t PfcWdZeroBufferHandler::ZeroBufferProfile::getZeroBufferProfile(bool ingress) { SWSS_LOG_ENTER(); @@ -733,29 +754,39 @@ void PfcWdZeroBufferHandler::ZeroBufferProfile::createZeroBufferProfile(bool ing sai_attribute_t attr; vector attribs; + sai_status_t status; - // Create zero pool - attr.id = SAI_BUFFER_POOL_ATTR_SIZE; - attr.value.u64 = 0; - attribs.push_back(attr); + auto &poolId = getPool(ingress); - attr.id = SAI_BUFFER_POOL_ATTR_TYPE; - attr.value.u32 = ingress ? SAI_BUFFER_POOL_TYPE_INGRESS : SAI_BUFFER_POOL_TYPE_EGRESS; - attribs.push_back(attr); + if (SAI_NULL_OBJECT_ID == poolId) + { + // Create zero pool + attr.id = SAI_BUFFER_POOL_ATTR_SIZE; + attr.value.u64 = 0; + attribs.push_back(attr); - attr.id = SAI_BUFFER_POOL_ATTR_THRESHOLD_MODE; - attr.value.u32 = SAI_BUFFER_POOL_THRESHOLD_MODE_DYNAMIC; - attribs.push_back(attr); + attr.id = SAI_BUFFER_POOL_ATTR_TYPE; + attr.value.u32 = ingress ? SAI_BUFFER_POOL_TYPE_INGRESS : SAI_BUFFER_POOL_TYPE_EGRESS; + attribs.push_back(attr); + + attr.id = SAI_BUFFER_POOL_ATTR_THRESHOLD_MODE; + attr.value.u32 = SAI_BUFFER_POOL_THRESHOLD_MODE_STATIC; + attribs.push_back(attr); - sai_status_t status = sai_buffer_api->create_buffer_pool( - &getPool(ingress), + status = sai_buffer_api->create_buffer_pool( + &poolId, gSwitchId, static_cast(attribs.size()), attribs.data()); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to create dynamic zero buffer pool for PFC WD: %d", status); - return; + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create dynamic zero buffer pool for PFC WD: %d", status); + return; + } + + // Pass the ownership to BufferOrch + gBufferOrch->setZeroBufferPool(ingress, poolId); + gBufferOrch->lockZeroBufferPool(ingress); } // Create zero profile @@ -766,15 +797,15 @@ void PfcWdZeroBufferHandler::ZeroBufferProfile::createZeroBufferProfile(bool ing attribs.push_back(attr); attr.id = SAI_BUFFER_PROFILE_ATTR_THRESHOLD_MODE; - attr.value.u32 = SAI_BUFFER_PROFILE_THRESHOLD_MODE_DYNAMIC; + attr.value.u32 = SAI_BUFFER_PROFILE_THRESHOLD_MODE_STATIC; attribs.push_back(attr); attr.id = SAI_BUFFER_PROFILE_ATTR_BUFFER_SIZE; attr.value.u64 = 0; attribs.push_back(attr); - attr.id = SAI_BUFFER_PROFILE_ATTR_SHARED_DYNAMIC_TH; - attr.value.s8 = -8; // ALPHA_0 + attr.id = SAI_BUFFER_PROFILE_ATTR_SHARED_STATIC_TH; + attr.value.s8 = 0; attribs.push_back(attr); status = sai_buffer_api->create_buffer_profile( @@ -793,16 +824,19 @@ void PfcWdZeroBufferHandler::ZeroBufferProfile::destroyZeroBufferProfile(bool in { SWSS_LOG_ENTER(); - sai_status_t status = sai_buffer_api->remove_buffer_profile(getProfile(ingress)); - if (status != SAI_STATUS_SUCCESS) + if (getProfile(ingress) != SAI_NULL_OBJECT_ID) { - SWSS_LOG_ERROR("Failed to remove static zero buffer profile for PFC WD: %d", status); - return; + sai_status_t status = sai_buffer_api->remove_buffer_profile(getProfile(ingress)); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove static zero buffer profile for PFC WD: %d", status); + return; + } } - status = sai_buffer_api->remove_buffer_pool(getPool(ingress)); - if (status != SAI_STATUS_SUCCESS) + auto &pool = ingress ? m_zeroIngressBufferPool : m_zeroEgressBufferPool; + if (pool != SAI_NULL_OBJECT_ID) { - SWSS_LOG_ERROR("Failed to remove static zero buffer pool for PFC WD: %d", status); + gBufferOrch->unlockZeroBufferPool(ingress); } } diff --git a/orchagent/pfcactionhandler.h b/orchagent/pfcactionhandler.h index a55ac3b4a4..22908fbe08 100644 --- a/orchagent/pfcactionhandler.h +++ b/orchagent/pfcactionhandler.h @@ -148,10 +148,7 @@ class PfcWdZeroBufferHandler: public PfcWdLossyHandler return ingress ? m_zeroIngressBufferProfile : m_zeroEgressBufferProfile; } - sai_object_id_t& getPool(bool ingress) - { - return ingress ? m_zeroIngressBufferPool : m_zeroEgressBufferPool; - } + sai_object_id_t& getPool(bool ingress); sai_object_id_t m_zeroIngressBufferPool = SAI_NULL_OBJECT_ID; sai_object_id_t m_zeroEgressBufferPool = SAI_NULL_OBJECT_ID; diff --git a/orchagent/pfcwdorch.cpp b/orchagent/pfcwdorch.cpp index be4c1e51c4..62765ab0a1 100644 --- a/orchagent/pfcwdorch.cpp +++ b/orchagent/pfcwdorch.cpp @@ -399,9 +399,9 @@ void PfcWdSwOrch::enableBigRedSwitchMode() continue; } - if (!gPortsOrch->getPortPfc(port.m_port_id, &pfcMask)) + if (!gPortsOrch->getPortPfcWatchdogStatus(port.m_port_id, &pfcMask)) { - SWSS_LOG_ERROR("Failed to get PFC mask on port %s", port.m_alias.c_str()); + SWSS_LOG_ERROR("Failed to get PFC watchdog mask on port %s", port.m_alias.c_str()); return; } @@ -443,9 +443,9 @@ void PfcWdSwOrch::enableBigRedSwitchMode() continue; } - if (!gPortsOrch->getPortPfc(port.m_port_id, &pfcMask)) + if (!gPortsOrch->getPortPfcWatchdogStatus(port.m_port_id, &pfcMask)) { - SWSS_LOG_ERROR("Failed to get PFC mask on port %s", port.m_alias.c_str()); + SWSS_LOG_ERROR("Failed to get PFC watchdog mask on port %s", port.m_alias.c_str()); return; } @@ -489,7 +489,7 @@ bool PfcWdSwOrch::registerInWdDb(const Port& port, uint8_t pfcMask = 0; - if (!gPortsOrch->getPortPfc(port.m_port_id, &pfcMask)) + if (!gPortsOrch->getPortPfcWatchdogStatus(port.m_port_id, &pfcMask)) { SWSS_LOG_ERROR("Failed to get PFC mask on port %s", port.m_alias.c_str()); return false; diff --git a/orchagent/policerorch.cpp b/orchagent/policerorch.cpp index c4528f6330..68dfffe898 100644 --- a/orchagent/policerorch.cpp +++ b/orchagent/policerorch.cpp @@ -8,10 +8,13 @@ using namespace std; using namespace swss; extern sai_policer_api_t* sai_policer_api; +extern sai_port_api_t *sai_port_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; +#define ETHERNET_PREFIX "Ethernet" + static const string meter_type_field = "METER_TYPE"; static const string mode_field = "MODE"; static const string color_source_field = "COLOR_SOURCE"; @@ -23,6 +26,11 @@ static const string green_packet_action_field = "GREEN_PACKET_ACTION"; static const string red_packet_action_field = "RED_PACKET_ACTION"; static const string yellow_packet_action_field = "YELLOW_PACKET_ACTION"; +static const string storm_control_kbps = "KBPS"; +static const string storm_broadcast = "broadcast"; +static const string storm_unknown_unicast = "unknown-unicast"; +static const string storm_unknown_mcast = "unknown-multicast"; + static const map meter_type_map = { {"PACKETS", SAI_METER_TYPE_PACKETS}, {"BYTES", SAI_METER_TYPE_BYTES} @@ -105,15 +113,268 @@ bool PolicerOrch::decreaseRefCount(const string &name) return true; } -PolicerOrch::PolicerOrch(DBConnector* db, string tableName) : - Orch(db, tableName) +PolicerOrch::PolicerOrch(vector &tableNames, PortsOrch *portOrch) : Orch(tableNames), m_portsOrch(portOrch) { SWSS_LOG_ENTER(); } +task_process_status PolicerOrch::handlePortStormControlTable(swss::KeyOpFieldsValuesTuple tuple) +{ + auto key = kfvKey(tuple); + auto op = kfvOp(tuple); + string storm_key = key; + auto tokens = tokenize(storm_key, config_db_key_delimiter); + auto interface_name = tokens[0]; + auto storm_type = tokens[1]; + Port port; + + /*Only proceed for Ethernet interfaces*/ + if (strncmp(interface_name.c_str(), ETHERNET_PREFIX, strlen(ETHERNET_PREFIX))) + { + SWSS_LOG_ERROR("%s: Unsupported / Invalid interface %s", + storm_type.c_str(), interface_name.c_str()); + return task_process_status::task_success; + } + if (!gPortsOrch->getPort(interface_name, port)) + { + SWSS_LOG_ERROR("Failed to apply storm-control %s to port %s. Port not found", + storm_type.c_str(), interface_name.c_str()); + /*continue here as there can be more interfaces*/ + return task_process_status::task_success; + } + /*Policer Name: __*/ + const auto storm_policer_name = "_"+interface_name+"_"+storm_type; + + if (op == SET_COMMAND) + { + // Mark the operation as an 'update', if the policer exists. + bool update = m_syncdPolicers.find(storm_policer_name) != m_syncdPolicers.end(); + vector attrs; + bool cir = false; + sai_attribute_t attr; + + /*Meter type hardcoded to BYTES*/ + attr.id = SAI_POLICER_ATTR_METER_TYPE; + attr.value.s32 = (sai_meter_type_t) meter_type_map.at("BYTES"); + attrs.push_back(attr); + + /*Policer mode hardcoded to STORM_CONTROL*/ + attr.id = SAI_POLICER_ATTR_MODE; + attr.value.s32 = (sai_policer_mode_t) policer_mode_map.at("STORM_CONTROL"); + attrs.push_back(attr); + + /*Red Packet Action hardcoded to DROP*/ + attr.id = SAI_POLICER_ATTR_RED_PACKET_ACTION; + attr.value.s32 = packet_action_map.at("DROP"); + attrs.push_back(attr); + + for (auto i = kfvFieldsValues(tuple).begin(); + i != kfvFieldsValues(tuple).end(); ++i) + { + auto field = to_upper(fvField(*i)); + auto value = to_upper(fvValue(*i)); + + /*BPS value is used as CIR*/ + if (field == storm_control_kbps) + { + attr.id = SAI_POLICER_ATTR_CIR; + /*convert kbps to bps*/ + attr.value.u64 = (stoul(value)*1000/8); + cir = true; + attrs.push_back(attr); + SWSS_LOG_DEBUG("CIR %s",value.c_str()); + } + else + { + SWSS_LOG_ERROR("Unknown storm control attribute %s specified", + field.c_str()); + continue; + } + } + /*CIR is mandatory parameter*/ + if (!cir) + { + SWSS_LOG_ERROR("Failed to create storm control policer %s,\ + missing mandatory fields", storm_policer_name.c_str()); + return task_process_status::task_failed; + } + + /*Enabling storm-control on port*/ + sai_attribute_t port_attr; + if (storm_type == storm_broadcast) + { + port_attr.id = SAI_PORT_ATTR_BROADCAST_STORM_CONTROL_POLICER_ID; + } + else if (storm_type == storm_unknown_unicast) + { + port_attr.id = SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID; + } + else if (storm_type == storm_unknown_mcast) + { + port_attr.id = SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID; + } + else + { + SWSS_LOG_ERROR("Unknown storm_type %s", storm_type.c_str()); + return task_process_status::task_failed; + } + + sai_object_id_t policer_id; + // Create a new policer + if (!update) + { + sai_status_t status = sai_policer_api->create_policer( + &policer_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create policer %s, rv:%d", + storm_policer_name.c_str(), status); + if (handleSaiCreateStatus(SAI_API_POLICER, status) == task_need_retry) + { + return task_process_status::task_need_retry; + } + } + + SWSS_LOG_DEBUG("Created storm-control policer %s", storm_policer_name.c_str()); + m_syncdPolicers[storm_policer_name] = policer_id; + m_policerRefCounts[storm_policer_name] = 0; + } + // Update an existing policer + else + { + policer_id = m_syncdPolicers[storm_policer_name]; + + // The update operation has limitations that it could only update + // the rate and the size accordingly. + // STORM_CONTROL: CIR, CBS + for (auto & attr: attrs) + { + if (attr.id != SAI_POLICER_ATTR_CIR) + { + continue; + } + + sai_status_t status = sai_policer_api->set_policer_attribute( + policer_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to update policer %s attribute, rv:%d", + storm_policer_name.c_str(), status); + if (handleSaiSetStatus(SAI_API_POLICER, status) == task_need_retry) + { + return task_process_status::task_need_retry; + } + + } + } + } + policer_id = m_syncdPolicers[storm_policer_name]; + + if (update) + { + SWSS_LOG_NOTICE("update storm-control policer %s", storm_policer_name.c_str()); + port_attr.value.oid = SAI_NULL_OBJECT_ID; + /*Remove and re-apply policer*/ + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove storm-control %s from port %s, rv:%d", + storm_type.c_str(), interface_name.c_str(), status); + if (handleSaiSetStatus(SAI_API_POLICER, status) == task_need_retry) + { + return task_process_status::task_need_retry; + } + } + } + port_attr.value.oid = policer_id; + + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to apply storm-control %s to port %s, rv:%d", + storm_type.c_str(), interface_name.c_str(),status); + + /*TODO: Do the below policer cleanup in an API*/ + /*Remove the already created policer*/ + if (SAI_STATUS_SUCCESS != sai_policer_api->remove_policer( + m_syncdPolicers[storm_policer_name])) + { + SWSS_LOG_ERROR("Failed to remove policer %s, rv:%d", + storm_policer_name.c_str(), status); + /*TODO: Just doing a syslog. */ + } + + SWSS_LOG_NOTICE("Removed policer %s as set_port_attribute for %s failed", + storm_policer_name.c_str(),interface_name.c_str()); + m_syncdPolicers.erase(storm_policer_name); + m_policerRefCounts.erase(storm_policer_name); + + return task_process_status::task_need_retry; + } + } + else if (op == DEL_COMMAND) + { + if (m_syncdPolicers.find(storm_policer_name) == m_syncdPolicers.end()) + { + SWSS_LOG_ERROR("Policer %s not configured", storm_policer_name.c_str()); + return task_process_status::task_success; + } + + sai_attribute_t port_attr; + if (storm_type == storm_broadcast) + { + port_attr.id = SAI_PORT_ATTR_BROADCAST_STORM_CONTROL_POLICER_ID; + } + else if (storm_type == storm_unknown_unicast) + { + port_attr.id = SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID; + } + else if (storm_type == storm_unknown_mcast) + { + port_attr.id = SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID; + } + else + { + SWSS_LOG_ERROR("Unknown storm_type %s", storm_type.c_str()); + return task_process_status::task_failed; + } + + port_attr.value.oid = SAI_NULL_OBJECT_ID; + + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove storm-control %s from port %s, rv:%d", + storm_type.c_str(), interface_name.c_str(), status); + if (handleSaiRemoveStatus(SAI_API_POLICER, status) == task_need_retry) + { + return task_process_status::task_need_retry; + } + } + + status = sai_policer_api->remove_policer( + m_syncdPolicers[storm_policer_name]); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove policer %s, rv:%d", + storm_policer_name.c_str(), status); + if (handleSaiRemoveStatus(SAI_API_POLICER, status) == task_need_retry) + { + return task_process_status::task_need_retry; + } + } + + SWSS_LOG_NOTICE("Removed policer %s", storm_policer_name.c_str()); + m_syncdPolicers.erase(storm_policer_name); + m_policerRefCounts.erase(storm_policer_name); + } + return task_process_status::task_success; +} + void PolicerOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); + task_process_status storm_status = task_success; if (!gPortsOrch->allPortsReady()) { @@ -127,7 +388,23 @@ void PolicerOrch::doTask(Consumer &consumer) auto key = kfvKey(tuple); auto op = kfvOp(tuple); + auto table_name = consumer.getTableName(); + // Special handling for storm-control configuration. + if (table_name == CFG_PORT_STORM_CONTROL_TABLE_NAME) + { + storm_status = handlePortStormControlTable(tuple); + if ((storm_status == task_process_status::task_success) || + (storm_status == task_process_status::task_failed)) + { + it = consumer.m_toSync.erase(it); + } + else + { + it++; + } + continue; + } if (op == SET_COMMAND) { // Mark the operation as an 'update', if the policer exists. diff --git a/orchagent/policerorch.h b/orchagent/policerorch.h index d735da03b7..9814179958 100644 --- a/orchagent/policerorch.h +++ b/orchagent/policerorch.h @@ -14,16 +14,20 @@ typedef map PolicerRefCountTable; class PolicerOrch : public Orch { public: - PolicerOrch(DBConnector* db, string tableName); + PolicerOrch(vector &tableNames, PortsOrch *portOrch); bool policerExists(const string &name); bool getPolicerOid(const string &name, sai_object_id_t &oid); bool increaseRefCount(const string &name); bool decreaseRefCount(const string &name); + task_process_status handlePortStormControlTable(swss::KeyOpFieldsValuesTuple tuple); private: + PortsOrch *m_portsOrch; virtual void doTask(Consumer& consumer); PolicerTable m_syncdPolicers; PolicerRefCountTable m_policerRefCounts; }; + + diff --git a/orchagent/port.h b/orchagent/port.h index 83f61e1b1c..db9f2b7bff 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -139,7 +139,8 @@ class Port std::vector m_queue_ids; std::vector m_priority_group_ids; sai_port_priority_flow_control_mode_t m_pfc_asym = SAI_PORT_PRIORITY_FLOW_CONTROL_MODE_COMBINED; - uint8_t m_pfc_bitmask = 0; + uint8_t m_pfc_bitmask = 0; // PFC enable bit mask + uint8_t m_pfcwd_sw_bitmask = 0; // PFC software watchdog enable uint16_t m_tpid = DEFAULT_TPID; uint32_t m_nat_zone_id = 0; uint32_t m_vnid = VNID_NONE; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index 52a35c7a39..5a6ba61e5c 100755 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -118,12 +118,16 @@ static map interface_type_map = { "none", SAI_PORT_INTERFACE_TYPE_NONE }, { "cr", SAI_PORT_INTERFACE_TYPE_CR }, { "cr4", SAI_PORT_INTERFACE_TYPE_CR4 }, + { "cr8", SAI_PORT_INTERFACE_TYPE_CR8 }, { "sr", SAI_PORT_INTERFACE_TYPE_SR }, { "sr4", SAI_PORT_INTERFACE_TYPE_SR4 }, + { "sr8", SAI_PORT_INTERFACE_TYPE_SR8 }, { "lr", SAI_PORT_INTERFACE_TYPE_LR }, { "lr4", SAI_PORT_INTERFACE_TYPE_LR4 }, + { "lr8", SAI_PORT_INTERFACE_TYPE_LR8 }, { "kr", SAI_PORT_INTERFACE_TYPE_KR }, - { "kr4", SAI_PORT_INTERFACE_TYPE_KR4 } + { "kr4", SAI_PORT_INTERFACE_TYPE_KR4 }, + { "kr8", SAI_PORT_INTERFACE_TYPE_KR8 } }; // Interface type map used for auto negotiation @@ -133,13 +137,17 @@ static map interface_type_map_for_an = { "cr", SAI_PORT_INTERFACE_TYPE_CR }, { "cr2", SAI_PORT_INTERFACE_TYPE_CR2 }, { "cr4", SAI_PORT_INTERFACE_TYPE_CR4 }, + { "cr8", SAI_PORT_INTERFACE_TYPE_CR8 }, { "sr", SAI_PORT_INTERFACE_TYPE_SR }, { "sr2", SAI_PORT_INTERFACE_TYPE_SR2 }, { "sr4", SAI_PORT_INTERFACE_TYPE_SR4 }, + { "sr8", SAI_PORT_INTERFACE_TYPE_SR8 }, { "lr", SAI_PORT_INTERFACE_TYPE_LR }, { "lr4", SAI_PORT_INTERFACE_TYPE_LR4 }, + { "lr8", SAI_PORT_INTERFACE_TYPE_LR8 }, { "kr", SAI_PORT_INTERFACE_TYPE_KR }, { "kr4", SAI_PORT_INTERFACE_TYPE_KR4 }, + { "kr8", SAI_PORT_INTERFACE_TYPE_KR8 }, { "caui", SAI_PORT_INTERFACE_TYPE_CAUI }, { "gmii", SAI_PORT_INTERFACE_TYPE_GMII }, { "sfi", SAI_PORT_INTERFACE_TYPE_SFI }, @@ -1193,6 +1201,43 @@ bool PortsOrch::setPortPfc(sai_object_id_t portId, uint8_t pfc_bitmask) return true; } +bool PortsOrch::setPortPfcWatchdogStatus(sai_object_id_t portId, uint8_t pfcwd_bitmask) +{ + SWSS_LOG_ENTER(); + + Port p; + + if (!getPort(portId, p)) + { + SWSS_LOG_ERROR("Failed to get port object for port id 0x%" PRIx64, portId); + return false; + } + + p.m_pfcwd_sw_bitmask = pfcwd_bitmask; + + m_portList[p.m_alias] = p; + + SWSS_LOG_INFO("Set PFC watchdog port id=0x%" PRIx64 ", bitmast=0x%x", portId, pfcwd_bitmask); + return true; +} + +bool PortsOrch::getPortPfcWatchdogStatus(sai_object_id_t portId, uint8_t *pfcwd_bitmask) +{ + SWSS_LOG_ENTER(); + + Port p; + + if (!pfcwd_bitmask || !getPort(portId, p)) + { + SWSS_LOG_ERROR("Failed to get port object for port id 0x%" PRIx64, portId); + return false; + } + + *pfcwd_bitmask = p.m_pfcwd_sw_bitmask; + + return true; +} + bool PortsOrch::setPortPfcAsym(Port &port, string pfc_asym) { SWSS_LOG_ENTER(); @@ -2289,6 +2334,13 @@ sai_status_t PortsOrch::removePort(sai_object_id_t port_id) } /* else : port is in default state or not yet created */ + /* + * Remove port serdes (if exists) before removing port since this + * reference is dependency. + */ + + removePortSerdesAttribute(port_id); + sai_status_t status = sai_port_api->remove_port(port_id); if (status != SAI_STATUS_SUCCESS) { @@ -2371,6 +2423,18 @@ bool PortsOrch::initPort(const string &alias, const string &role, const int inde port_buffer_drop_stat_manager.setCounterIdList(p.m_port_id, CounterType::PORT, port_buffer_drop_stats); } + /* when a port is added and priority group map counter is enabled --> we need to add pg counter for it */ + if (m_isPriorityGroupMapGenerated) + { + generatePriorityGroupMapPerPort(p); + } + + /* when a port is added and queue map counter is enabled --> we need to add queue map counter for it */ + if (m_isQueueMapGenerated) + { + generateQueueMapPerPort(p); + } + PortUpdate update = { p, true }; notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); @@ -2403,8 +2467,13 @@ void PortsOrch::deInitPort(string alias, sai_object_id_t port_id) { SWSS_LOG_ENTER(); - Port p(alias, Port::PHY); - p.m_port_id = port_id; + Port p; + + if (!getPort(port_id, p)) + { + SWSS_LOG_ERROR("Failed to get port object for port id 0x%" PRIx64, port_id); + return; + } /* remove port from flex_counter_table for updating counters */ auto flex_counters_orch = gDirectory.get(); @@ -2418,9 +2487,20 @@ void PortsOrch::deInitPort(string alias, sai_object_id_t port_id) port_buffer_drop_stat_manager.clearCounterIdList(p.m_port_id); } + /* remove pg port counters */ + if (m_isPriorityGroupMapGenerated) + { + removePriorityGroupMapPerPort(p); + } + + /* remove queue port counters */ + if (m_isQueueMapGenerated) + { + removeQueueMapPerPort(p); + } /* remove port name map from counter table */ - m_counter_db->hdel(COUNTERS_PORT_NAME_MAP, alias); + m_counterTable->hdel("", alias); /* Remove the associated port serdes attribute */ removePortSerdesAttribute(p.m_port_id); @@ -2429,7 +2509,6 @@ void PortsOrch::deInitPort(string alias, sai_object_id_t port_id) SWSS_LOG_NOTICE("De-Initialized port %s", alias.c_str()); } - bool PortsOrch::bake() { SWSS_LOG_ENTER(); @@ -3844,6 +3923,17 @@ void PortsOrch::doLagMemberTask(Consumer &consumer) continue; } + if (!port.m_ingress_acl_tables_uset.empty() || !port.m_egress_acl_tables_uset.empty()) + { + SWSS_LOG_ERROR( + "Failed to add member %s to LAG %s: ingress/egress ACL configuration is present", + port.m_alias.c_str(), + lag.m_alias.c_str() + ); + it = consumer.m_toSync.erase(it); + continue; + } + if (!addLagMember(lag, port, (status == "enabled"))) { it++; @@ -4483,9 +4573,10 @@ bool PortsOrch::removeVlan(Port vlan) return false for retry */ if (vlan.m_fdb_count > 0) { - SWSS_LOG_NOTICE("VLAN %s still has assiciated FDB entries", vlan.m_alias.c_str()); + SWSS_LOG_NOTICE("VLAN %s still has %d FDB entries", vlan.m_alias.c_str(), vlan.m_fdb_count); return false; } + if (m_port_ref_count[vlan.m_alias] > 0) { SWSS_LOG_ERROR("Failed to remove ref count %d VLAN %s", @@ -4927,7 +5018,7 @@ bool PortsOrch::addLag(string lag_alias, uint32_t spa_id, int32_t switch_id) auto lagport = m_portList.find(lag_alias); if (lagport != m_portList.end()) { - /* The deletion of bridgeport attached to the lag may still be + /* The deletion of bridgeport attached to the lag may still be * pending due to fdb entries still present on the lag. Wait * until the cleanup is done. */ @@ -5391,6 +5482,44 @@ void PortsOrch::generateQueueMap() m_isQueueMapGenerated = true; } +void PortsOrch::removeQueueMapPerPort(const Port& port) +{ + /* Remove the Queue map in the Counter DB */ + + for (size_t queueIndex = 0; queueIndex < port.m_queue_ids.size(); ++queueIndex) + { + std::ostringstream name; + name << port.m_alias << ":" << queueIndex; + std::unordered_set counter_stats; + + const auto id = sai_serialize_object_id(port.m_queue_ids[queueIndex]); + + m_queueTable->hdel("",name.str()); + m_queuePortTable->hdel("",id); + + string queueType; + uint8_t queueRealIndex = 0; + if (getQueueTypeAndIndex(port.m_queue_ids[queueIndex], queueType, queueRealIndex)) + { + m_queueTypeTable->hdel("",id); + m_queueIndexTable->hdel("",id); + } + + for (const auto& it: queue_stat_ids) + { + counter_stats.emplace(sai_serialize_queue_stat(it)); + } + queue_stat_manager.clearCounterIdList(port.m_queue_ids[queueIndex]); + + /* remove watermark queue counters */ + string key = getQueueWatermarkFlexCounterTableKey(id); + + m_flexCounterTable->del(key); + } + + CounterCheckOrch::getInstance().removePort(port); +} + void PortsOrch::generateQueueMapPerPort(const Port& port) { /* Create the Queue map in the Counter DB */ @@ -5469,6 +5598,32 @@ void PortsOrch::generatePriorityGroupMap() m_isPriorityGroupMapGenerated = true; } +void PortsOrch::removePriorityGroupMapPerPort(const Port& port) +{ + /* Remove the PG map in the Counter DB */ + + for (size_t pgIndex = 0; pgIndex < port.m_priority_group_ids.size(); ++pgIndex) + { + std::ostringstream name; + name << port.m_alias << ":" << pgIndex; + + const auto id = sai_serialize_object_id(port.m_priority_group_ids[pgIndex]); + string key = getPriorityGroupWatermarkFlexCounterTableKey(id); + + m_pgTable->hdel("",name.str()); + m_pgPortTable->hdel("",id); + m_pgIndexTable->hdel("",id); + + m_flexCounterTable->del(key); + + key = getPriorityGroupDropPacketsFlexCounterTableKey(id); + /* remove dropped packets counters to flex_counter */ + m_flexCounterTable->del(key); + } + + CounterCheckOrch::getInstance().removePort(port); +} + void PortsOrch::generatePriorityGroupMapPerPort(const Port& port) { /* Create the PG map in the Counter DB */ @@ -5621,6 +5776,10 @@ void PortsOrch::doTask(NotificationConsumer &consumer) SWSS_LOG_NOTICE("%s oper speed is %d", port.m_alias.c_str(), speed); updateDbPortOperSpeed(port, speed); } + else + { + updateDbPortOperSpeed(port, 0); + } } /* update m_portList */ @@ -5682,9 +5841,9 @@ void PortsOrch::updateDbPortOperSpeed(Port &port, sai_uint32_t speed) SWSS_LOG_ENTER(); vector tuples; - FieldValueTuple tuple("speed", to_string(speed)); - tuples.push_back(tuple); - m_portTable->set(port.m_alias, tuples); + string speedStr = speed != 0 ? to_string(speed) : "N/A"; + tuples.emplace_back(std::make_pair("speed", speedStr)); + m_portStateTable.set(port.m_alias, tuples); // We don't set port.m_speed = speed here, because CONFIG_DB still hold the old // value. If we set it here, next time configure any attributes related port will @@ -5731,6 +5890,10 @@ void PortsOrch::refreshPortStatus() SWSS_LOG_INFO("%s oper speed is %d", port.m_alias.c_str(), speed); updateDbPortOperSpeed(port, speed); } + else + { + updateDbPortOperSpeed(port, 0); + } } } } @@ -6766,3 +6929,17 @@ std::unordered_set PortsOrch::generateCounterStats(const string& ty } return counter_stats; } + +bool PortsOrch::decrFdbCount(const std::string& alias, int count) +{ + auto itr = m_portList.find(alias); + if (itr == m_portList.end()) + { + return false; + } + else + { + itr->second.m_fdb_count -= count; + } + return true; +} diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 843ffbd7f1..2848cdcb91 100755 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -124,6 +124,9 @@ class PortsOrch : public Orch, public Subject bool getPortPfc(sai_object_id_t portId, uint8_t *pfc_bitmask); bool setPortPfc(sai_object_id_t portId, uint8_t pfc_bitmask); + bool setPortPfcWatchdogStatus(sai_object_id_t portId, uint8_t pfc_bitmask); + bool getPortPfcWatchdogStatus(sai_object_id_t portId, uint8_t *pfc_bitmask); + void generateQueueMap(); void generatePriorityGroupMap(); void generatePortCounterMap(); @@ -165,6 +168,7 @@ class PortsOrch : public Orch, public Subject bool getPortOperStatus(const Port& port, sai_port_oper_status_t& status) const; + bool decrFdbCount(const string& alias, int count); private: unique_ptr
m_counterTable; unique_ptr
m_counterLagTable; @@ -312,9 +316,11 @@ class PortsOrch : public Orch, public Subject bool m_isQueueMapGenerated = false; void generateQueueMapPerPort(const Port& port); + void removeQueueMapPerPort(const Port& port); bool m_isPriorityGroupMapGenerated = false; void generatePriorityGroupMapPerPort(const Port& port); + void removePriorityGroupMapPerPort(const Port& port); bool m_isPortCounterMapGenerated = false; bool m_isPortBufferDropCounterMapGenerated = false; diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp index 5a48b69969..5cb9d8ad2e 100644 --- a/orchagent/qosorch.cpp +++ b/orchagent/qosorch.cpp @@ -25,6 +25,7 @@ extern sai_acl_api_t* sai_acl_api; extern SwitchOrch *gSwitchOrch; extern PortsOrch *gPortsOrch; +extern QosOrch *gQosOrch; extern sai_object_id_t gSwitchId; extern CrmOrch *gCrmOrch; @@ -78,6 +79,8 @@ type_map QosOrch::m_qos_maps = { {CFG_PFC_PRIORITY_TO_QUEUE_MAP_TABLE_NAME, new object_reference_map()}, {CFG_DSCP_TO_FC_MAP_TABLE_NAME, new object_reference_map()}, {CFG_EXP_TO_FC_MAP_TABLE_NAME, new object_reference_map()}, + {CFG_TC_TO_DSCP_MAP_TABLE_NAME, new object_reference_map()}, + {APP_TUNNEL_DECAP_TABLE_NAME, new object_reference_map()} }; map qos_to_ref_table_map = { @@ -91,19 +94,21 @@ map qos_to_ref_table_map = { {scheduler_field_name, CFG_SCHEDULER_TABLE_NAME}, {wred_profile_field_name, CFG_WRED_PROFILE_TABLE_NAME}, {dscp_to_fc_field_name, CFG_DSCP_TO_FC_MAP_TABLE_NAME}, - {exp_to_fc_field_name, CFG_EXP_TO_FC_MAP_TABLE_NAME} + {exp_to_fc_field_name, CFG_EXP_TO_FC_MAP_TABLE_NAME}, + {decap_dscp_to_tc_field_name, CFG_DSCP_TO_TC_MAP_TABLE_NAME}, + {decap_tc_to_pg_field_name, CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME}, + {encap_tc_to_dscp_field_name, CFG_TC_TO_DSCP_MAP_TABLE_NAME}, + {encap_tc_to_queue_field_name, CFG_TC_TO_QUEUE_MAP_TABLE_NAME} }; #define DSCP_MAX_VAL 63 #define EXP_MAX_VAL 7 -task_process_status QosMapHandler::processWorkItem(Consumer& consumer) +task_process_status QosMapHandler::processWorkItem(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); sai_object_id_t sai_object = SAI_NULL_OBJECT_ID; - auto it = consumer.m_toSync.begin(); - KeyOpFieldsValuesTuple tuple = it->second; string qos_object_name = kfvKey(tuple); string qos_map_type_name = consumer.getTableName(); string op = kfvOp(tuple); @@ -111,6 +116,11 @@ task_process_status QosMapHandler::processWorkItem(Consumer& consumer) if (QosOrch::getTypeMap()[qos_map_type_name]->find(qos_object_name) != QosOrch::getTypeMap()[qos_map_type_name]->end()) { sai_object = (*(QosOrch::getTypeMap()[qos_map_type_name]))[qos_object_name].m_saiObjectId; + if ((*(QosOrch::getTypeMap()[qos_map_type_name]))[qos_object_name].m_pendingRemove && op == SET_COMMAND) + { + SWSS_LOG_NOTICE("Entry %s %s is pending remove, need retry", qos_map_type_name.c_str(), qos_object_name.c_str()); + return task_process_status::task_need_retry; + } } if (op == SET_COMMAND) { @@ -139,6 +149,7 @@ task_process_status QosMapHandler::processWorkItem(Consumer& consumer) return task_process_status::task_failed; } (*(QosOrch::getTypeMap()[qos_map_type_name]))[qos_object_name].m_saiObjectId = sai_object; + (*(QosOrch::getTypeMap()[qos_map_type_name]))[qos_object_name].m_pendingRemove = false; SWSS_LOG_NOTICE("Created [%s:%s]", qos_map_type_name.c_str(), qos_object_name.c_str()); } freeAttribResources(attributes); @@ -150,6 +161,13 @@ task_process_status QosMapHandler::processWorkItem(Consumer& consumer) SWSS_LOG_ERROR("Object with name:%s not found.", qos_object_name.c_str()); return task_process_status::task_invalid_entry; } + if (gQosOrch->isObjectBeingReferenced(QosOrch::getTypeMap(), qos_map_type_name, qos_object_name)) + { + auto hint = gQosOrch->objectReferenceInfo(QosOrch::getTypeMap(), qos_map_type_name, qos_object_name); + SWSS_LOG_NOTICE("Can't remove object %s due to being referenced (%s)", qos_object_name.c_str(), hint.c_str()); + (*(QosOrch::getTypeMap()[qos_map_type_name]))[qos_object_name].m_pendingRemove = true; + return task_process_status::task_need_retry; + } if (!removeQosItem(sai_object)) { SWSS_LOG_ERROR("Failed to remove dscp_to_tc map. db name:%s sai object:%" PRIx64, qos_object_name.c_str(), sai_object); @@ -243,6 +261,9 @@ void DscpToTcMapHandler::applyDscpToTcMapToSwitch(sai_attr_id_t attr_id, sai_obj return; } + if (map_id != gQosOrch->m_globalDscpToTcMap) + gQosOrch->m_globalDscpToTcMap = map_id; + SWSS_LOG_NOTICE("Applied DSCP_TO_TC QoS map to switch successfully"); } @@ -276,11 +297,46 @@ sai_object_id_t DscpToTcMapHandler::addQosItem(const vector &at return sai_object; } -task_process_status QosOrch::handleDscpToTcTable(Consumer& consumer) +bool DscpToTcMapHandler::removeQosItem(sai_object_id_t sai_object) +{ + SWSS_LOG_ENTER(); + + if (sai_object == gQosOrch->m_globalDscpToTcMap) + { + // The current global dscp to tc map is about to be removed. + // Find another one to set to the switch or NULL in case this is the last one + const auto &dscpToTcObjects = (*QosOrch::getTypeMap()[CFG_DSCP_TO_TC_MAP_TABLE_NAME]); + bool found = false; + for (const auto &ref : dscpToTcObjects) + { + if (ref.second.m_saiObjectId == sai_object) + continue; + SWSS_LOG_NOTICE("Current global dscp_to_tc map is about to be removed, set it to %s %" PRIx64, ref.first.c_str(), ref.second.m_saiObjectId); + applyDscpToTcMapToSwitch(SAI_SWITCH_ATTR_QOS_DSCP_TO_TC_MAP, ref.second.m_saiObjectId); + found = true; + break; + } + if (!found) + { + applyDscpToTcMapToSwitch(SAI_SWITCH_ATTR_QOS_DSCP_TO_TC_MAP, SAI_NULL_OBJECT_ID); + } + } + + SWSS_LOG_DEBUG("Removing DscpToTcMap object:%" PRIx64, sai_object); + sai_status_t sai_status = sai_qos_map_api->remove_qos_map(sai_object); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to remove DSCP_TO_TC map, status:%d", sai_status); + return false; + } + return true; +} + +task_process_status QosOrch::handleDscpToTcTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); DscpToTcMapHandler dscp_tc_handler; - return dscp_tc_handler.processWorkItem(consumer); + return dscp_tc_handler.processWorkItem(consumer, tuple); } bool MplsTcToTcMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) @@ -331,11 +387,11 @@ sai_object_id_t MplsTcToTcMapHandler::addQosItem(const vector & return sai_object; } -task_process_status QosOrch::handleMplsTcToTcTable(Consumer& consumer) +task_process_status QosOrch::handleMplsTcToTcTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); MplsTcToTcMapHandler mpls_tc_to_tc_handler; - return mpls_tc_to_tc_handler.processWorkItem(consumer); + return mpls_tc_to_tc_handler.processWorkItem(consumer, tuple); } bool Dot1pToTcMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) @@ -400,11 +456,11 @@ sai_object_id_t Dot1pToTcMapHandler::addQosItem(const vector &a return object_id; } -task_process_status QosOrch::handleDot1pToTcTable(Consumer &consumer) +task_process_status QosOrch::handleDot1pToTcTable(Consumer &consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); Dot1pToTcMapHandler dot1p_tc_handler; - return dot1p_tc_handler.processWorkItem(consumer); + return dot1p_tc_handler.processWorkItem(consumer, tuple); } bool TcToQueueMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) @@ -453,11 +509,11 @@ sai_object_id_t TcToQueueMapHandler::addQosItem(const vector &a return sai_object; } -task_process_status QosOrch::handleTcToQueueTable(Consumer& consumer) +task_process_status QosOrch::handleTcToQueueTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); TcToQueueMapHandler tc_queue_handler; - return tc_queue_handler.processWorkItem(consumer); + return tc_queue_handler.processWorkItem(consumer, tuple); } void WredMapHandler::freeAttribResources(vector &attributes) @@ -674,11 +730,11 @@ bool WredMapHandler::removeQosItem(sai_object_id_t sai_object) return true; } -task_process_status QosOrch::handleWredProfileTable(Consumer& consumer) +task_process_status QosOrch::handleWredProfileTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); WredMapHandler wred_handler; - return wred_handler.processWorkItem(consumer); + return wred_handler.processWorkItem(consumer, tuple); } bool TcToPgHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) @@ -727,11 +783,11 @@ sai_object_id_t TcToPgHandler::addQosItem(const vector &attribu } -task_process_status QosOrch::handleTcToPgTable(Consumer& consumer) +task_process_status QosOrch::handleTcToPgTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); TcToPgHandler tc_to_pg_handler; - return tc_to_pg_handler.processWorkItem(consumer); + return tc_to_pg_handler.processWorkItem(consumer, tuple); } bool PfcPrioToPgHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) @@ -781,11 +837,11 @@ sai_object_id_t PfcPrioToPgHandler::addQosItem(const vector &at } -task_process_status QosOrch::handlePfcPrioToPgTable(Consumer& consumer) +task_process_status QosOrch::handlePfcPrioToPgTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); PfcPrioToPgHandler pfc_prio_to_pg_handler; - return pfc_prio_to_pg_handler.processWorkItem(consumer); + return pfc_prio_to_pg_handler.processWorkItem(consumer, tuple); } bool PfcToQueueHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) @@ -922,11 +978,11 @@ sai_object_id_t DscpToFcMapHandler::addQosItem(const vector &at return sai_object; } -task_process_status QosOrch::handleDscpToFcTable(Consumer& consumer) +task_process_status QosOrch::handleDscpToFcTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); DscpToFcMapHandler dscp_fc_handler; - return dscp_fc_handler.processWorkItem(consumer); + return dscp_fc_handler.processWorkItem(consumer, tuple); } bool ExpToFcMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, @@ -1013,18 +1069,101 @@ sai_object_id_t ExpToFcMapHandler::addQosItem(const vector &att return sai_object; } -task_process_status QosOrch::handleExpToFcTable(Consumer& consumer) +bool TcToDscpMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, + vector &attributes) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t list_attr; + list_attr.id = SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST; + list_attr.value.qosmap.count = (uint32_t)kfvFieldsValues(tuple).size(); + list_attr.value.qosmap.list = new sai_qos_map_t[list_attr.value.qosmap.count](); + uint32_t ind = 0; + + for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++, ind++) + { + try + { + auto value = stoi(fvValue(*i)); + if (value < 0) + { + SWSS_LOG_ERROR("DSCP value %d is negative", value); + delete[] list_attr.value.qosmap.list; + return false; + } + else if (value > DSCP_MAX_VAL) + { + SWSS_LOG_ERROR("DSCP value %d is greater than max value %d", value, DSCP_MAX_VAL); + delete[] list_attr.value.qosmap.list; + return false; + } + list_attr.value.qosmap.list[ind].key.tc = static_cast(stoi(fvField(*i))); + list_attr.value.qosmap.list[ind].value.dscp = static_cast(value); + + SWSS_LOG_DEBUG("key.tc:%d, value.dscp:%d", + list_attr.value.qosmap.list[ind].key.tc, + list_attr.value.qosmap.list[ind].value.dscp); + } + catch(const invalid_argument& e) + { + SWSS_LOG_ERROR("Got exception during conversion: %s", e.what()); + delete[] list_attr.value.qosmap.list; + return false; + } + } + attributes.push_back(list_attr); + return true; +} + +sai_object_id_t TcToDscpMapHandler::addQosItem(const vector &attributes) +{ + SWSS_LOG_ENTER(); + sai_status_t sai_status; + sai_object_id_t sai_object; + vector qos_map_attrs; + + sai_attribute_t qos_map_attr; + qos_map_attr.id = SAI_QOS_MAP_ATTR_TYPE; + qos_map_attr.value.u32 = SAI_QOS_MAP_TYPE_TC_AND_COLOR_TO_DSCP; + qos_map_attrs.push_back(qos_map_attr); + + qos_map_attr.id = SAI_QOS_MAP_ATTR_MAP_TO_VALUE_LIST; + qos_map_attr.value.qosmap.count = attributes[0].value.qosmap.count; + qos_map_attr.value.qosmap.list = attributes[0].value.qosmap.list; + qos_map_attrs.push_back(qos_map_attr); + + sai_status = sai_qos_map_api->create_qos_map(&sai_object, + gSwitchId, + (uint32_t)qos_map_attrs.size(), + qos_map_attrs.data()); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to create tc_to_dscp map. status:%d", sai_status); + return SAI_NULL_OBJECT_ID; + } + SWSS_LOG_DEBUG("created QosMap object:%" PRIx64, sai_object); + return sai_object; +} + +task_process_status QosOrch::handleExpToFcTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); ExpToFcMapHandler exp_fc_handler; - return exp_fc_handler.processWorkItem(consumer); + return exp_fc_handler.processWorkItem(consumer, tuple); } -task_process_status QosOrch::handlePfcToQueueTable(Consumer& consumer) +task_process_status QosOrch::handlePfcToQueueTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); PfcToQueueHandler pfc_to_queue_handler; - return pfc_to_queue_handler.processWorkItem(consumer); + return pfc_to_queue_handler.processWorkItem(consumer, tuple); +} + +task_process_status QosOrch::handleTcToDscpTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) +{ + SWSS_LOG_ENTER(); + TcToDscpMapHandler tc_to_dscp_handler; + return tc_to_dscp_handler.processWorkItem(consumer, tuple); } QosOrch::QosOrch(DBConnector *db, vector &tableNames) : Orch(db, tableNames) @@ -1053,20 +1192,20 @@ void QosOrch::initTableHandlers() m_qos_handler_map.insert(qos_handler_pair(CFG_WRED_PROFILE_TABLE_NAME, &QosOrch::handleWredProfileTable)); m_qos_handler_map.insert(qos_handler_pair(CFG_DSCP_TO_FC_MAP_TABLE_NAME, &QosOrch::handleDscpToFcTable)); m_qos_handler_map.insert(qos_handler_pair(CFG_EXP_TO_FC_MAP_TABLE_NAME, &QosOrch::handleExpToFcTable)); + m_qos_handler_map.insert(qos_handler_pair(CFG_TC_TO_DSCP_MAP_TABLE_NAME, &QosOrch::handleTcToDscpTable)); m_qos_handler_map.insert(qos_handler_pair(CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, &QosOrch::handleTcToPgTable)); m_qos_handler_map.insert(qos_handler_pair(CFG_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_TABLE_NAME, &QosOrch::handlePfcPrioToPgTable)); m_qos_handler_map.insert(qos_handler_pair(CFG_PFC_PRIORITY_TO_QUEUE_MAP_TABLE_NAME, &QosOrch::handlePfcToQueueTable)); } -task_process_status QosOrch::handleSchedulerTable(Consumer& consumer) +task_process_status QosOrch::handleSchedulerTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); sai_status_t sai_status; sai_object_id_t sai_object = SAI_NULL_OBJECT_ID; - KeyOpFieldsValuesTuple tuple = consumer.m_toSync.begin()->second; string qos_map_type_name = CFG_SCHEDULER_TABLE_NAME; string qos_object_name = kfvKey(tuple); string op = kfvOp(tuple); @@ -1079,6 +1218,11 @@ task_process_status QosOrch::handleSchedulerTable(Consumer& consumer) SWSS_LOG_ERROR("Error sai_object must exist for key %s", qos_object_name.c_str()); return task_process_status::task_invalid_entry; } + if ((*(m_qos_maps[qos_map_type_name]))[qos_object_name].m_pendingRemove && op == SET_COMMAND) + { + SWSS_LOG_NOTICE("Entry %s %s is pending remove, need retry", qos_map_type_name.c_str(), qos_object_name.c_str()); + return task_process_status::task_need_retry; + } } if (op == SET_COMMAND) { @@ -1186,6 +1330,7 @@ task_process_status QosOrch::handleSchedulerTable(Consumer& consumer) } SWSS_LOG_NOTICE("Created [%s:%s]", qos_map_type_name.c_str(), qos_object_name.c_str()); (*(m_qos_maps[qos_map_type_name]))[qos_object_name].m_saiObjectId = sai_object; + (*(m_qos_maps[qos_map_type_name]))[qos_object_name].m_pendingRemove = false; } } else if (op == DEL_COMMAND) @@ -1195,6 +1340,13 @@ task_process_status QosOrch::handleSchedulerTable(Consumer& consumer) SWSS_LOG_ERROR("Object with name:%s not found.", qos_object_name.c_str()); return task_process_status::task_invalid_entry; } + if (gQosOrch->isObjectBeingReferenced(QosOrch::getTypeMap(), qos_map_type_name, qos_object_name)) + { + auto hint = gQosOrch->objectReferenceInfo(QosOrch::getTypeMap(), qos_map_type_name, qos_object_name); + SWSS_LOG_NOTICE("Can't remove object %s due to being referenced (%s)", qos_object_name.c_str(), hint.c_str()); + (*(m_qos_maps[qos_map_type_name]))[qos_object_name].m_pendingRemove = true; + return task_process_status::task_need_retry; + } sai_status = sai_scheduler_api->remove_scheduler(sai_object); if (SAI_STATUS_SUCCESS != sai_status) { @@ -1406,11 +1558,9 @@ bool QosOrch::applyWredProfileToQueue(Port &port, size_t queue_ind, sai_object_i return true; } -task_process_status QosOrch::handleQueueTable(Consumer& consumer) +task_process_status QosOrch::handleQueueTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); - auto it = consumer.m_toSync.begin(); - KeyOpFieldsValuesTuple tuple = it->second; Port port; bool result; string key = kfvKey(tuple); @@ -1435,6 +1585,94 @@ task_process_status QosOrch::handleQueueTable(Consumer& consumer) SWSS_LOG_ERROR("Failed to parse range:%s", tokens[1].c_str()); return task_process_status::task_invalid_entry; } + + bool donotChangeScheduler = false; + bool donotChangeWredProfile = false; + sai_object_id_t sai_scheduler_profile; + sai_object_id_t sai_wred_profile; + + if (op == SET_COMMAND) + { + string scheduler_profile_name; + resolve_result = resolveFieldRefValue(m_qos_maps, scheduler_field_name, + qos_to_ref_table_map.at(scheduler_field_name), tuple, + sai_scheduler_profile, scheduler_profile_name); + if (ref_resolve_status::success != resolve_result) + { + if (resolve_result != ref_resolve_status::field_not_found) + { + if(ref_resolve_status::not_resolved == resolve_result) + { + SWSS_LOG_INFO("Missing or invalid scheduler reference"); + return task_process_status::task_need_retry; + } + SWSS_LOG_ERROR("Resolving scheduler reference failed"); + return task_process_status::task_failed; + } + + if (doesObjectExist(m_qos_maps, CFG_QUEUE_TABLE_NAME, key, scheduler_field_name, scheduler_profile_name)) + { + SWSS_LOG_NOTICE("QUEUE|%s %s was configured but is not any more. Remove it", key.c_str(), scheduler_field_name.c_str()); + removeMeFromObjsReferencedByMe(m_qos_maps, CFG_QUEUE_TABLE_NAME, key, scheduler_field_name, scheduler_profile_name); + sai_scheduler_profile = SAI_NULL_OBJECT_ID; + } + else + { + // Did not exist and do not exist. No action + donotChangeScheduler = true; + } + } + else + { + setObjectReference(m_qos_maps, CFG_QUEUE_TABLE_NAME, key, scheduler_field_name, scheduler_profile_name); + SWSS_LOG_INFO("QUEUE %s Field %s %s has been resolved to %" PRIx64 , key.c_str(), scheduler_field_name.c_str(), scheduler_profile_name.c_str(), sai_scheduler_profile); + } + + string wred_profile_name; + resolve_result = resolveFieldRefValue(m_qos_maps, wred_profile_field_name, + qos_to_ref_table_map.at(wred_profile_field_name), tuple, + sai_wred_profile, wred_profile_name); + if (ref_resolve_status::success != resolve_result) + { + if (resolve_result != ref_resolve_status::field_not_found) + { + if(ref_resolve_status::not_resolved == resolve_result) + { + SWSS_LOG_INFO("Missing or invalid wred profile reference"); + return task_process_status::task_need_retry; + } + SWSS_LOG_ERROR("Resolving wred profile reference failed"); + return task_process_status::task_failed; + } + + if (doesObjectExist(m_qos_maps, CFG_QUEUE_TABLE_NAME, key, wred_profile_field_name, wred_profile_name)) + { + SWSS_LOG_NOTICE("QUEUE|%s %s was configured but is not any more. Remove it", key.c_str(), wred_profile_field_name.c_str()); + removeMeFromObjsReferencedByMe(m_qos_maps, CFG_QUEUE_TABLE_NAME, key, wred_profile_field_name, wred_profile_name); + sai_wred_profile = SAI_NULL_OBJECT_ID; + } + else + { + donotChangeWredProfile = true; + } + } + else + { + setObjectReference(m_qos_maps, CFG_QUEUE_TABLE_NAME, key, wred_profile_field_name, wred_profile_name); + } + } + else if (op == DEL_COMMAND) + { + removeObject(QosOrch::getTypeMap(), CFG_QUEUE_TABLE_NAME, key); + sai_scheduler_profile = SAI_NULL_OBJECT_ID; + sai_wred_profile = SAI_NULL_OBJECT_ID; + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + return task_process_status::task_invalid_entry; + } + for (string port_name : port_names) { Port port; @@ -1449,27 +1687,11 @@ task_process_status QosOrch::handleQueueTable(Consumer& consumer) { queue_ind = ind; SWSS_LOG_DEBUG("processing queue:%zd", queue_ind); - sai_object_id_t sai_scheduler_profile; - string scheduler_profile_name; - resolve_result = resolveFieldRefValue(m_qos_maps, scheduler_field_name, - qos_to_ref_table_map.at(scheduler_field_name), tuple, - sai_scheduler_profile, scheduler_profile_name); - if (ref_resolve_status::success == resolve_result) + + if (!donotChangeScheduler) { - if (op == SET_COMMAND) - { - result = applySchedulerToQueueSchedulerGroup(port, queue_ind, sai_scheduler_profile); - } - else if (op == DEL_COMMAND) - { - // NOTE: The map is un-bound from the port. But the map itself still exists. - result = applySchedulerToQueueSchedulerGroup(port, queue_ind, SAI_NULL_OBJECT_ID); - } - else - { - SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); - return task_process_status::task_invalid_entry; - } + result = applySchedulerToQueueSchedulerGroup(port, queue_ind, sai_scheduler_profile); + if (!result) { SWSS_LOG_ERROR("Failed setting field:%s to port:%s, queue:%zd, line:%d", scheduler_field_name.c_str(), port.m_alias.c_str(), queue_ind, __LINE__); @@ -1477,38 +1699,11 @@ task_process_status QosOrch::handleQueueTable(Consumer& consumer) } SWSS_LOG_DEBUG("Applied scheduler to port:%s", port_name.c_str()); } - else if (resolve_result != ref_resolve_status::field_not_found) - { - if(ref_resolve_status::not_resolved == resolve_result) - { - SWSS_LOG_INFO("Missing or invalid scheduler reference"); - return task_process_status::task_need_retry; - } - SWSS_LOG_ERROR("Resolving scheduler reference failed"); - return task_process_status::task_failed; - } - sai_object_id_t sai_wred_profile; - string wred_profile_name; - resolve_result = resolveFieldRefValue(m_qos_maps, wred_profile_field_name, - qos_to_ref_table_map.at(wred_profile_field_name), tuple, - sai_wred_profile, wred_profile_name); - if (ref_resolve_status::success == resolve_result) + if (!donotChangeWredProfile) { - if (op == SET_COMMAND) - { - result = applyWredProfileToQueue(port, queue_ind, sai_wred_profile); - } - else if (op == DEL_COMMAND) - { - // NOTE: The map is un-bound from the port. But the map itself still exists. - result = applyWredProfileToQueue(port, queue_ind, SAI_NULL_OBJECT_ID); - } - else - { - SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); - return task_process_status::task_invalid_entry; - } + result = applyWredProfileToQueue(port, queue_ind, sai_wred_profile); + if (!result) { SWSS_LOG_ERROR("Failed setting field:%s to port:%s, queue:%zd, line:%d", wred_profile_field_name.c_str(), port.m_alias.c_str(), queue_ind, __LINE__); @@ -1516,118 +1711,75 @@ task_process_status QosOrch::handleQueueTable(Consumer& consumer) } SWSS_LOG_DEBUG("Applied wred profile to port:%s", port_name.c_str()); } - else if (resolve_result != ref_resolve_status::field_not_found) - { - if (ref_resolve_status::empty == resolve_result) - { - SWSS_LOG_INFO("Missing wred reference. Unbind wred profile from queue"); - // NOTE: The wred profile is un-bound from the port. But the wred profile itself still exists - // and stays untouched. - result = applyWredProfileToQueue(port, queue_ind, SAI_NULL_OBJECT_ID); - if (!result) - { - SWSS_LOG_ERROR("Failed unbinding field:%s from port:%s, queue:%zd, line:%d", wred_profile_field_name.c_str(), port.m_alias.c_str(), queue_ind, __LINE__); - return task_process_status::task_failed; - } - } - else if (ref_resolve_status::not_resolved == resolve_result) - { - SWSS_LOG_INFO("Invalid wred reference"); - return task_process_status::task_need_retry; - } - else - { - SWSS_LOG_ERROR("Resolving wred reference failed"); - return task_process_status::task_failed; - } - } } } SWSS_LOG_DEBUG("finished"); return task_process_status::task_success; } -bool QosOrch::applyMapToPort(Port &port, sai_attr_id_t attr_id, sai_object_id_t map_id) +task_process_status QosOrch::handlePortQosMapTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple) { SWSS_LOG_ENTER(); - sai_attribute_t attr; - attr.id = attr_id; - attr.value.oid = map_id; + string key = kfvKey(tuple); + string op = kfvOp(tuple); + vector port_names = tokenize(key, list_item_delimiter); - sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &attr); - if (status != SAI_STATUS_SUCCESS) + if (op == DEL_COMMAND) { - SWSS_LOG_ERROR("Failed setting sai object:%" PRIx64 " for port:%s, status:%d", map_id, port.m_alias.c_str(), status); - task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); - if (handle_status != task_success) + /* Handle DEL command. Just set all the maps to oid:0x0 */ + for (string port_name : port_names) { - return parseHandleSaiStatusFailure(handle_status); - } - } - return true; -} + Port port; -task_process_status QosOrch::ResolveMapAndApplyToPort( - Port &port, - sai_port_attr_t port_attr, - string field_name, - KeyOpFieldsValuesTuple &tuple, - string op) -{ - SWSS_LOG_ENTER(); + /* Skip port which is not found */ + if (!gPortsOrch->getPort(port_name, port)) + { + SWSS_LOG_ERROR("Failed to apply QoS maps to port %s. Port is not found.", port_name.c_str()); + continue; + } - sai_object_id_t sai_object = SAI_NULL_OBJECT_ID; - string object_name; - bool result; - ref_resolve_status resolve_result = resolveFieldRefValue(m_qos_maps, field_name, - qos_to_ref_table_map.at(field_name), tuple, sai_object, object_name); - if (ref_resolve_status::success == resolve_result) - { - if (op == SET_COMMAND) - { - result = applyMapToPort(port, port_attr, sai_object); - } - else if (op == DEL_COMMAND) - { - // NOTE: The map is un-bound from the port. But the map itself still exists. - result = applyMapToPort(port, port_attr, SAI_NULL_OBJECT_ID); - } - else - { - SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); - return task_process_status::task_invalid_entry; - } - if (!result) - { - SWSS_LOG_ERROR("Failed setting field:%s to port:%s, line:%d", field_name.c_str(), port.m_alias.c_str(), __LINE__); - return task_process_status::task_failed; - } - SWSS_LOG_DEBUG("Applied field:%s to port:%s, line:%d", field_name.c_str(), port.m_alias.c_str(), __LINE__); - return task_process_status::task_success; - } - else if (resolve_result != ref_resolve_status::field_not_found) - { - if(ref_resolve_status::not_resolved == resolve_result) - { - SWSS_LOG_INFO("Missing or invalid %s reference", field_name.c_str()); - return task_process_status::task_need_retry; + for (auto &mapRef : qos_to_attr_map) + { + string referenced_obj; + if (!doesObjectExist(m_qos_maps, CFG_PORT_QOS_MAP_TABLE_NAME, key, mapRef.first, referenced_obj)) + { + continue; + } + + sai_attribute_t attr; + attr.id = mapRef.second; + attr.value.oid = SAI_NULL_OBJECT_ID; + + sai_status_t status = sai_port_api->set_port_attribute(port.m_port_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove %s on port %s, rv:%d", + mapRef.first.c_str(), port_name.c_str(), status); + task_process_status handle_status = handleSaiSetStatus(SAI_API_PORT, status); + if (handle_status != task_process_status::task_success) + { + return task_process_status::task_invalid_entry; + } + } + SWSS_LOG_INFO("Removed %s on port %s", mapRef.first.c_str(), port_name.c_str()); + } + + if (!gPortsOrch->setPortPfc(port.m_port_id, 0)) + { + SWSS_LOG_ERROR("Failed to disable PFC on port %s", port_name.c_str()); + } + + SWSS_LOG_INFO("Disabled PFC on port %s", port_name.c_str()); } - SWSS_LOG_ERROR("Resolving %s reference failed", field_name.c_str()); - return task_process_status::task_failed; - } - return task_process_status::task_success; -} -task_process_status QosOrch::handlePortQosMapTable(Consumer& consumer) -{ - SWSS_LOG_ENTER(); + removeObject(m_qos_maps, CFG_PORT_QOS_MAP_TABLE_NAME, key); - KeyOpFieldsValuesTuple tuple = consumer.m_toSync.begin()->second; - string key = kfvKey(tuple); - string op = kfvOp(tuple); + return task_process_status::task_success; + } sai_uint8_t pfc_enable = 0; + sai_uint8_t pfcwd_sw_enable = 0; map> update_list; for (auto it = kfvFieldsValues(tuple).begin(); it != kfvFieldsValues(tuple).end(); it++) { @@ -1636,7 +1788,7 @@ task_process_status QosOrch::handlePortQosMapTable(Consumer& consumer) { sai_object_id_t id; string object_name; - string map_type_name = fvField(*it), map_name = fvValue(*it); + string &map_type_name = fvField(*it), &map_name = fvValue(*it); ref_resolve_status status = resolveFieldRefValue(m_qos_maps, map_type_name, qos_to_ref_table_map.at(map_type_name), tuple, id, object_name); if (status != ref_resolve_status::success) @@ -1646,21 +1798,48 @@ task_process_status QosOrch::handlePortQosMapTable(Consumer& consumer) } update_list[qos_to_attr_map[map_type_name]] = make_pair(map_name, id); + setObjectReference(m_qos_maps, CFG_PORT_QOS_MAP_TABLE_NAME, key, map_type_name, object_name); } - if (fvField(*it) == pfc_enable_name) + else if (fvField(*it) == pfc_enable_name || fvField(*it) == pfcwd_sw_enable_name) { + sai_uint8_t bitmask = 0; vector queue_indexes; queue_indexes = tokenize(fvValue(*it), list_item_delimiter); for(string q_ind : queue_indexes) { sai_uint8_t q_val = (uint8_t)stoi(q_ind); - pfc_enable |= (uint8_t)(1 << q_val); + bitmask |= (uint8_t)(1 << q_val); + } + + if (fvField(*it) == pfc_enable_name) + { + pfc_enable = bitmask; + } + else + { + pfcwd_sw_enable = bitmask; } } } - vector port_names = tokenize(key, list_item_delimiter); + /* Remove any map that was configured but isn't there any longer. */ + for (auto &mapRef : qos_to_attr_map) + { + auto &sai_attribute = mapRef.second; + if (update_list.find(sai_attribute) == update_list.end()) + { + string referenced_obj; + if (!doesObjectExist(m_qos_maps, CFG_PORT_QOS_MAP_TABLE_NAME, key, mapRef.first, referenced_obj)) + { + continue; + } + SWSS_LOG_NOTICE("PORT_QOS_MAP|%s %s was configured but is not any more. Remove it", key.c_str(), mapRef.first.c_str()); + removeMeFromObjsReferencedByMe(m_qos_maps, CFG_PORT_QOS_MAP_TABLE_NAME, key, mapRef.first, referenced_obj); + update_list[mapRef.second] = make_pair("NULL", SAI_NULL_OBJECT_ID); + } + } + for (string port_name : port_names) { Port port; @@ -1708,6 +1887,9 @@ task_process_status QosOrch::handlePortQosMapTable(Consumer& consumer) SWSS_LOG_INFO("Applied PFC bits 0x%x to port %s", pfc_enable, port_name.c_str()); } + + // Save pfd_wd bitmask unconditionally + gPortsOrch->setPortPfcWatchdogStatus(port.m_port_id, pfcwd_sw_enable); } SWSS_LOG_NOTICE("Applied QoS maps to ports"); @@ -1756,7 +1938,7 @@ void QosOrch::doTask(Consumer &consumer) continue; } - auto task_status = (this->*(m_qos_handler_map[qos_map_type_name]))(consumer); + auto task_status = (this->*(m_qos_handler_map[qos_map_type_name]))(consumer, it->second); switch(task_status) { case task_process_status::task_success : @@ -1781,3 +1963,53 @@ void QosOrch::doTask(Consumer &consumer) } } } + +/** + * Function Description: + * @brief Resolve the id of QoS map that is referenced by tunnel + * + * Arguments: + * @param[in] referencing_table_name - The name of table that is referencing the QoS map + * @param[in] tunnle_name - The name of tunnel + * @param[in] map_type_name - The type of referenced QoS map + * @param[in] tuple - The KeyOpFieldsValuesTuple that contains keys - values + * + * Return Values: + * @return The sai_object_id of referenced map, or SAI_NULL_OBJECT_ID if there's an error + */ +sai_object_id_t QosOrch::resolveTunnelQosMap(std::string referencing_table_name, std::string tunnel_name, std::string map_type_name, KeyOpFieldsValuesTuple& tuple) +{ + sai_object_id_t id; + string object_name; + ref_resolve_status status = resolveFieldRefValue(m_qos_maps, map_type_name, qos_to_ref_table_map.at(map_type_name), tuple, id, object_name); + if (status == ref_resolve_status::success) + { + + setObjectReference(m_qos_maps, referencing_table_name, tunnel_name, map_type_name, object_name); + SWSS_LOG_INFO("Resolved QoS map for table %s tunnel %s type %s name %s", referencing_table_name.c_str(), tunnel_name.c_str(), map_type_name.c_str(), object_name.c_str()); + return id; + } + else + { + SWSS_LOG_ERROR("Failed to resolve QoS map for table %s tunnel %s type %s", referencing_table_name.c_str(), tunnel_name.c_str(), map_type_name.c_str()); + return SAI_NULL_OBJECT_ID; + } +} + +/** + * Function Description: + * @brief Remove the reference from tunnel object. Called after tunnel is removed + * + * Arguments: + * @param[in] referencing_table_name - The name of table that is referencing the QoS map + * @param[in] tunnle_name - The name of tunnel + * + * Return Values: + * @return no return + */ +void QosOrch::removeTunnelReference(std::string referencing_table_name, std::string tunnel_name) +{ + removeObject(m_qos_maps, referencing_table_name, tunnel_name); + SWSS_LOG_INFO("Freed QoS objects referenced by %s:%s", referencing_table_name.c_str(), tunnel_name.c_str()); +} + diff --git a/orchagent/qosorch.h b/orchagent/qosorch.h index cd265d59ec..2b348cc030 100644 --- a/orchagent/qosorch.h +++ b/orchagent/qosorch.h @@ -14,6 +14,7 @@ const string dot1p_to_tc_field_name = "dot1p_to_tc_map"; const string pfc_to_pg_map_name = "pfc_to_pg_map"; const string pfc_to_queue_map_name = "pfc_to_queue_map"; const string pfc_enable_name = "pfc_enable"; +const string pfcwd_sw_enable_name = "pfcwd_sw_enable"; const string tc_to_pg_map_field_name = "tc_to_pg_map"; const string tc_to_queue_field_name = "tc_to_queue_map"; const string scheduler_field_name = "scheduler"; @@ -28,6 +29,10 @@ const string yellow_drop_probability_field_name = "yellow_drop_probability"; const string green_drop_probability_field_name = "green_drop_probability"; const string dscp_to_fc_field_name = "dscp_to_fc_map"; const string exp_to_fc_field_name = "exp_to_fc_map"; +const string decap_dscp_to_tc_field_name = "decap_dscp_to_tc_map"; +const string decap_tc_to_pg_field_name = "decap_tc_to_pg_map"; +const string encap_tc_to_queue_field_name = "encap_tc_to_queue_map"; +const string encap_tc_to_dscp_field_name = "encap_tc_to_dscp_map"; const string wred_profile_field_name = "wred_profile"; const string wred_red_enable_field_name = "wred_red_enable"; @@ -59,7 +64,7 @@ const string ecn_all = "ecn_all"; class QosMapHandler { public: - task_process_status processWorkItem(Consumer& consumer); + task_process_status processWorkItem(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); virtual bool convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) = 0; virtual void freeAttribResources(vector &attributes); virtual bool modifyQosItem(sai_object_id_t, vector &attributes); @@ -72,6 +77,7 @@ class DscpToTcMapHandler : public QosMapHandler public: bool convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) override; sai_object_id_t addQosItem(const vector &attributes) override; + bool removeQosItem(sai_object_id_t sai_object); protected: void applyDscpToTcMapToSwitch(sai_attr_id_t attr_id, sai_object_id_t sai_dscp_to_tc_map); }; @@ -146,6 +152,14 @@ class ExpToFcMapHandler : public QosMapHandler sai_object_id_t addQosItem(const vector &attributes) override; }; +// Handler for TC_TO_DSCP_MAP +class TcToDscpMapHandler : public QosMapHandler +{ +public: + bool convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tuple, vector &attributes) override; + sai_object_id_t addQosItem(const vector &attributes) override; +}; + class QosOrch : public Orch { public: @@ -153,37 +167,38 @@ class QosOrch : public Orch static type_map& getTypeMap(); static type_map m_qos_maps; + + sai_object_id_t resolveTunnelQosMap(std::string referencing_table_name, std::string tunnel_name, std::string map_type_name, KeyOpFieldsValuesTuple& tuple); + void removeTunnelReference(std::string referencing_table_name, std::string tunnel_name); private: void doTask() override; virtual void doTask(Consumer& consumer); - typedef task_process_status (QosOrch::*qos_table_handler)(Consumer& consumer); + typedef task_process_status (QosOrch::*qos_table_handler)(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); typedef map qos_table_handler_map; typedef pair qos_handler_pair; void initTableHandlers(); - task_process_status handleDscpToTcTable(Consumer& consumer); - task_process_status handleMplsTcToTcTable(Consumer& consumer); - task_process_status handleDot1pToTcTable(Consumer& consumer); - task_process_status handlePfcPrioToPgTable(Consumer& consumer); - task_process_status handlePfcToQueueTable(Consumer& consumer); - task_process_status handlePortQosMapTable(Consumer& consumer); - task_process_status handleTcToPgTable(Consumer& consumer); - task_process_status handleTcToQueueTable(Consumer& consumer); - task_process_status handleSchedulerTable(Consumer& consumer); - task_process_status handleQueueTable(Consumer& consumer); - task_process_status handleWredProfileTable(Consumer& consumer); - task_process_status handleDscpToFcTable(Consumer& consumer); - task_process_status handleExpToFcTable(Consumer& consumer); + task_process_status handleDscpToTcTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handleMplsTcToTcTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handleDot1pToTcTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handlePfcPrioToPgTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handlePfcToQueueTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handlePortQosMapTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handleTcToPgTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handleTcToQueueTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handleSchedulerTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handleQueueTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handleWredProfileTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handleDscpToFcTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handleExpToFcTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); + task_process_status handleTcToDscpTable(Consumer& consumer, KeyOpFieldsValuesTuple &tuple); sai_object_id_t getSchedulerGroup(const Port &port, const sai_object_id_t queue_id); - bool applyMapToPort(Port &port, sai_attr_id_t attr_id, sai_object_id_t sai_dscp_to_tc_map); bool applySchedulerToQueueSchedulerGroup(Port &port, size_t queue_ind, sai_object_id_t scheduler_profile_id); bool applyWredProfileToQueue(Port &port, size_t queue_ind, sai_object_id_t sai_wred_profile); - task_process_status ResolveMapAndApplyToPort(Port &port,sai_port_attr_t port_attr, - string field_name, KeyOpFieldsValuesTuple &tuple, string op); private: qos_table_handler_map m_qos_handler_map; @@ -196,5 +211,11 @@ class QosOrch : public Orch }; std::unordered_map m_scheduler_group_port_info; + + // SAI OID of the global dscp to tc map + sai_object_id_t m_globalDscpToTcMap; + + friend QosMapHandler; + friend DscpToTcMapHandler; }; #endif /* SWSS_QOSORCH_H */ diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index e3c27b9818..a793ab8dcc 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -5,6 +5,7 @@ #include "nhgorch.h" #include "cbf/cbfnhgorch.h" #include "logger.h" +#include "flowcounterrouteorch.h" #include "swssnet.h" #include "crmorch.h" #include "directory.h" @@ -22,6 +23,7 @@ extern CrmOrch *gCrmOrch; extern Directory gDirectory; extern NhgOrch *gNhgOrch; extern CbfNhgOrch *gCbfNhgOrch; +extern FlowCounterRouteOrch *gFlowCounterRouteOrch; extern size_t gMaxBulkSize; @@ -145,7 +147,6 @@ RouteOrch::RouteOrch(DBConnector *db, vector &tableNames, addLinkLocalRouteToMe(gVirtualRouterId, default_link_local_prefix); SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu", default_link_local_prefix.to_string().c_str()); - } std::string RouteOrch::getLinkLocalEui64Addr(void) @@ -212,6 +213,8 @@ void RouteOrch::addLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + gFlowCounterRouteOrch->onAddMiscRouteEntry(vrf_id, linklocal_prefix.getSubnet()); + SWSS_LOG_NOTICE("Created link local ipv6 route %s to cpu", linklocal_prefix.to_string().c_str()); } @@ -233,6 +236,8 @@ void RouteOrch::delLinkLocalRouteToMe(sai_object_id_t vrf_id, IpPrefix linklocal gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + gFlowCounterRouteOrch->onRemoveMiscRouteEntry(vrf_id, linklocal_prefix.getSubnet()); + SWSS_LOG_NOTICE("Deleted link local ipv6 route %s to cpu", linklocal_prefix.to_string().c_str()); } @@ -2212,6 +2217,11 @@ bool RouteOrch::addRoutePost(const RouteBulkContext& ctx, const NextHopGroupKey updateDefRouteState(ipPrefix.to_string(), true); } + if (it_route == m_syncdRoutes.at(vrf_id).end()) + { + gFlowCounterRouteOrch->handleRouteAdd(vrf_id, ipPrefix); + } + m_syncdRoutes[vrf_id][ipPrefix] = RouteNhg(nextHops, ctx.nhg_index); notifyNextHopChangeObservers(vrf_id, ipPrefix, nextHops, true); @@ -2419,6 +2429,7 @@ bool RouteOrch::removeRoutePost(const RouteBulkContext& ctx) } else { + gFlowCounterRouteOrch->handleRouteRemove(vrf_id, ipPrefix); it_route_table->second.erase(ipPrefix); /* Notify about the route next hop removal */ diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index 2c8826ecf7..5f297c6a0e 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -214,10 +214,11 @@ class RouteOrch : public Orch, public Subject unsigned int getNhgCount() { return m_nextHopGroupCount; } unsigned int getMaxNhgCount() { return m_maxNextHopGroupCount; } - + void increaseNextHopGroupCount(); void decreaseNextHopGroupCount(); bool checkNextHopGroupCount(); + const RouteTables& getSyncdRoutes() const { return m_syncdRoutes; } private: SwitchOrch *m_switchOrch; diff --git a/orchagent/swssnet.h b/orchagent/swssnet.h index be49708d4f..82b5b6f94f 100644 --- a/orchagent/swssnet.h +++ b/orchagent/swssnet.h @@ -3,6 +3,7 @@ // #pragma once +#include #include #include #include @@ -76,6 +77,53 @@ inline static sai_ip_prefix_t& copy(sai_ip_prefix_t& dst, const IpAddress& src) return dst; } +static int getPrefixLenFromAddrMask(const uint8_t *addr, int len) +{ + int i = 0; + uint8_t non_zero = 0xFF; + for (i = len - 1; i >=0; i--) + { + if (addr[i] != 0) + { + non_zero = addr[i]; + break; + } + } + + if (non_zero == 0xFF) + { + return (i + 1) * 8; + } + else + { + int j = 2; + while(((non_zero >> j) & 0x1) == 0) + { + ++j; + } + return (i + 1) * 8 - (j + 1); + } + +} + +inline static IpPrefix getIpPrefixFromSaiPrefix(const sai_ip_prefix_t& src) +{ + ip_addr_t ip; + switch(src.addr_family) + { + case SAI_IP_ADDR_FAMILY_IPV4: + ip.family = AF_INET; + ip.ip_addr.ipv4_addr = src.addr.ip4; + return IpPrefix(ip, getPrefixLenFromAddrMask(reinterpret_cast(&src.mask.ip4), 4)); + case SAI_IP_ADDR_FAMILY_IPV6: + ip.family = AF_INET6; + memcpy(ip.ip_addr.ipv6_addr, src.addr.ip6, 16); + return IpPrefix(ip, getPrefixLenFromAddrMask(src.mask.ip6, 16)); + default: + throw std::logic_error("Invalid family"); + } +} + inline static sai_ip_prefix_t& subnet(sai_ip_prefix_t& dst, const sai_ip_prefix_t& src) { dst.addr_family = src.addr_family; diff --git a/orchagent/tunneldecaporch.cpp b/orchagent/tunneldecaporch.cpp index 6ef2c96f74..91744f3323 100644 --- a/orchagent/tunneldecaporch.cpp +++ b/orchagent/tunneldecaporch.cpp @@ -5,6 +5,7 @@ #include "crmorch.h" #include "logger.h" #include "swssnet.h" +#include "qosorch.h" #define OVERLAY_RIF_DEFAULT_MTU 9100 @@ -17,6 +18,7 @@ extern sai_object_id_t gUnderlayIfId; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; extern CrmOrch* gCrmOrch; +extern QosOrch* gQosOrch; TunnelDecapOrch::TunnelDecapOrch(DBConnector *db, string tableName) : Orch(db, tableName) { @@ -31,7 +33,7 @@ void TunnelDecapOrch::doTask(Consumer& consumer) { return; } - + string table_name = consumer.getTableName(); auto it = consumer.m_toSync.begin(); while (it != consumer.m_toSync.end()) { @@ -48,11 +50,24 @@ void TunnelDecapOrch::doTask(Consumer& consumer) string ecn_mode; string encap_ecn_mode; string ttl_mode; + sai_object_id_t dscp_to_tc_map_id = SAI_NULL_OBJECT_ID; + sai_object_id_t tc_to_pg_map_id = SAI_NULL_OBJECT_ID; + // The tc_to_dscp_map_id and tc_to_queue_map_id are parsed here for muxorch to retrieve + sai_object_id_t tc_to_dscp_map_id = SAI_NULL_OBJECT_ID; + sai_object_id_t tc_to_queue_map_id = SAI_NULL_OBJECT_ID; + bool valid = true; + task_process_status task_status = task_process_status::task_success; + + sai_object_id_t tunnel_id = SAI_NULL_OBJECT_ID; // checking to see if the tunnel already exists bool exists = (tunnelTable.find(key) != tunnelTable.end()); - + if (exists) + { + tunnel_id = tunnelTable[key].tunnel_id; + } + if (op == SET_COMMAND) { @@ -114,7 +129,8 @@ void TunnelDecapOrch::doTask(Consumer& consumer) } if (exists) { - setTunnelAttribute(fvField(i), dscp_mode, tunnelTable.find(key)->second.tunnel_id); + setTunnelAttribute(fvField(i), dscp_mode, tunnel_id); + tunnelTable[key].dscp_mode = dscp_mode; } } else if (fvField(i) == "ecn_mode") @@ -128,7 +144,7 @@ void TunnelDecapOrch::doTask(Consumer& consumer) } if (exists) { - setTunnelAttribute(fvField(i), ecn_mode, tunnelTable.find(key)->second.tunnel_id); + setTunnelAttribute(fvField(i), ecn_mode, tunnel_id); } } else if (fvField(i) == "encap_ecn_mode") @@ -142,7 +158,7 @@ void TunnelDecapOrch::doTask(Consumer& consumer) } if (exists) { - setTunnelAttribute(fvField(i), encap_ecn_mode, tunnelTable.find(key)->second.tunnel_id); + setTunnelAttribute(fvField(i), encap_ecn_mode, tunnel_id); } } else if (fvField(i) == "ttl_mode") @@ -156,16 +172,85 @@ void TunnelDecapOrch::doTask(Consumer& consumer) } if (exists) { - setTunnelAttribute(fvField(i), ttl_mode, tunnelTable.find(key)->second.tunnel_id); + setTunnelAttribute(fvField(i), ttl_mode, tunnel_id); + } + } + else if (fvField(i) == decap_dscp_to_tc_field_name) + { + dscp_to_tc_map_id = gQosOrch->resolveTunnelQosMap(table_name, key, decap_dscp_to_tc_field_name, t); + if (dscp_to_tc_map_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_NOTICE("QoS map %s is not ready yet", decap_dscp_to_tc_field_name.c_str()); + task_status = task_process_status::task_need_retry; + break; + } + if (exists) + { + setTunnelAttribute(fvField(i), dscp_to_tc_map_id, tunnel_id); + } + } + else if (fvField(i) == decap_tc_to_pg_field_name) + { + tc_to_pg_map_id = gQosOrch->resolveTunnelQosMap(table_name, key, decap_tc_to_pg_field_name, t); + if (tc_to_pg_map_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_NOTICE("QoS map %s is not ready yet", decap_tc_to_pg_field_name.c_str()); + task_status = task_process_status::task_need_retry; + break; + } + if (exists) + { + setTunnelAttribute(fvField(i), tc_to_pg_map_id, tunnel_id); + } + } + else if (fvField(i) == encap_tc_to_dscp_field_name) + { + tc_to_dscp_map_id = gQosOrch->resolveTunnelQosMap(table_name, key, encap_tc_to_dscp_field_name, t); + if (tc_to_dscp_map_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_NOTICE("QoS map %s is not ready yet", encap_tc_to_dscp_field_name.c_str()); + task_status = task_process_status::task_need_retry; + break; + } + if (exists) + { + // Record only + tunnelTable[key].encap_tc_to_dscp_map_id = tc_to_dscp_map_id; + } + } + else if (fvField(i) == encap_tc_to_queue_field_name) + { + tc_to_queue_map_id = gQosOrch->resolveTunnelQosMap(table_name, key, encap_tc_to_queue_field_name, t); + if (tc_to_queue_map_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_NOTICE("QoS map %s is not ready yet", encap_tc_to_queue_field_name.c_str()); + task_status = task_process_status::task_need_retry; + break; + } + if (exists) + { + // Record only + tunnelTable[key].encap_tc_to_queue_map_id = tc_to_queue_map_id; } } } - // create new tunnel if it doesn't exists already + if (task_status == task_process_status::task_need_retry) + { + ++it; + continue; + } + + //create new tunnel if it doesn't exists already if (valid && !exists) { - if (addDecapTunnel(key, tunnel_type, ip_addresses, p_src_ip, dscp_mode, ecn_mode, encap_ecn_mode, ttl_mode)) + + if (addDecapTunnel(key, tunnel_type, ip_addresses, p_src_ip, dscp_mode, ecn_mode, encap_ecn_mode, ttl_mode, + dscp_to_tc_map_id, tc_to_pg_map_id)) { + // Record only + tunnelTable[key].encap_tc_to_dscp_map_id = tc_to_dscp_map_id; + tunnelTable[key].encap_tc_to_queue_map_id = tc_to_queue_map_id; SWSS_LOG_NOTICE("Tunnel(s) added to ASIC_DB."); } else @@ -179,7 +264,7 @@ void TunnelDecapOrch::doTask(Consumer& consumer) { if (exists) { - removeDecapTunnel(key); + removeDecapTunnel(table_name, key); } else { @@ -202,21 +287,34 @@ void TunnelDecapOrch::doTask(Consumer& consumer) * @param[in] dscp - dscp mode (uniform/pipe) * @param[in] ecn - ecn mode (copy_from_outer/standard) * @param[in] ttl - ttl mode (uniform/pipe) + * @param[in] dscp_to_tc_map_id - Map ID for remapping DSCP to TC (decap) + * @param[in] tc_to_pg_map_id - Map ID for remapping TC to PG (decap) * * Return Values: * @return true on success and false if there's an error */ -bool TunnelDecapOrch::addDecapTunnel(string key, string type, IpAddresses dst_ip, IpAddress* p_src_ip, string dscp, string ecn, string encap_ecn, string ttl) +bool TunnelDecapOrch::addDecapTunnel( + string key, + string type, + IpAddresses dst_ip, + IpAddress* p_src_ip, + string dscp, + string ecn, + string encap_ecn, + string ttl, + sai_object_id_t dscp_to_tc_map_id, + sai_object_id_t tc_to_pg_map_id) { SWSS_LOG_ENTER(); sai_status_t status; - + IpAddress src_ip("0.0.0.0"); // adding tunnel attributes to array and writing to ASIC_DB sai_attribute_t attr; vector tunnel_attrs; sai_object_id_t overlayIfId; + TunnelTermType term_type = TUNNEL_TERM_TYPE_P2MP; // create the overlay router interface to create a LOOPBACK type router interface (decap) vector overlay_intf_attrs; @@ -264,6 +362,8 @@ bool TunnelDecapOrch::addDecapTunnel(string key, string type, IpAddresses dst_ip attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; copy(attr.value.ipaddr, p_src_ip->to_string()); tunnel_attrs.push_back(attr); + src_ip = *p_src_ip; + term_type = TUNNEL_TERM_TYPE_P2P; } // decap ecn mode (copy from outer/standard) @@ -312,6 +412,22 @@ bool TunnelDecapOrch::addDecapTunnel(string key, string type, IpAddresses dst_ip } tunnel_attrs.push_back(attr); + // DSCP_TO_TC_MAP + if (dscp_to_tc_map_id != SAI_NULL_OBJECT_ID) + { + attr.id = SAI_TUNNEL_ATTR_DECAP_QOS_DSCP_TO_TC_MAP; + attr.value.oid = dscp_to_tc_map_id; + tunnel_attrs.push_back(attr); + } + + //TC_TO_PG_MAP + if (tc_to_pg_map_id != SAI_NULL_OBJECT_ID) + { + attr.id = SAI_TUNNEL_ATTR_DECAP_QOS_TC_TO_PRIORITY_GROUP_MAP; + attr.value.oid = tc_to_pg_map_id; + tunnel_attrs.push_back(attr); + } + // write attributes to ASIC_DB sai_object_id_t tunnel_id; status = sai_tunnel_api->create_tunnel(&tunnel_id, gSwitchId, (uint32_t)tunnel_attrs.size(), tunnel_attrs.data()); @@ -325,10 +441,10 @@ bool TunnelDecapOrch::addDecapTunnel(string key, string type, IpAddresses dst_ip } } - tunnelTable[key] = { tunnel_id, overlayIfId, dst_ip, {} }; + tunnelTable[key] = { tunnel_id, overlayIfId, dst_ip, {}, dscp, SAI_NULL_OBJECT_ID, SAI_NULL_OBJECT_ID }; - // create a decap tunnel entry for every ip - if (!addDecapTunnelTermEntries(key, dst_ip, tunnel_id)) + // create a decap tunnel entry for every source_ip - dest_ip pair + if (!addDecapTunnelTermEntries(key, src_ip, dst_ip, tunnel_id, term_type)) { return false; } @@ -342,13 +458,15 @@ bool TunnelDecapOrch::addDecapTunnel(string key, string type, IpAddresses dst_ip * * Arguments: * @param[in] tunnelKey - key of the tunnel from APP_DB - * @param[in] dst_ip - destination ip addresses to decap + * @param[in] src_ip - source ip address of decap tunnel + * @param[in] dst_ips - destination ip addresses to decap * @param[in] tunnel_id - the id of the tunnel + * @param[in] term_type - P2P or P2MP. Other types (MP2P and MP2MP) not supported yet * * Return Values: * @return true on success and false if there's an error */ -bool TunnelDecapOrch::addDecapTunnelTermEntries(string tunnelKey, IpAddresses dst_ip, sai_object_id_t tunnel_id) +bool TunnelDecapOrch::addDecapTunnelTermEntries(string tunnelKey, swss::IpAddress src_ip, swss::IpAddresses dst_ips, sai_object_id_t tunnel_id, TunnelTermType tunnel_type) { SWSS_LOG_ENTER(); @@ -361,7 +479,14 @@ bool TunnelDecapOrch::addDecapTunnelTermEntries(string tunnelKey, IpAddresses ds tunnel_table_entry_attrs.push_back(attr); attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE; - attr.value.u32 = SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP; + if (tunnel_type == TUNNEL_TERM_TYPE_P2P) + { + attr.value.u32 = SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P; + } + else + { + attr.value.u32 = SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP; + } tunnel_table_entry_attrs.push_back(attr); attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE; @@ -372,19 +497,38 @@ bool TunnelDecapOrch::addDecapTunnelTermEntries(string tunnelKey, IpAddresses ds attr.value.oid = tunnel_id; tunnel_table_entry_attrs.push_back(attr); + if (tunnel_type == TUNNEL_TERM_TYPE_P2P) + { + // Set src ip for P2P only + attr.id = SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP; + copy(attr.value.ipaddr, src_ip); + tunnel_table_entry_attrs.push_back(attr); + } + TunnelEntry *tunnel_info = &tunnelTable.find(tunnelKey)->second; // loop through the IP list and create a new tunnel table entry for every IP (in network byte order) - set tunnel_ips = dst_ip.getIpAddresses(); + set tunnel_ips = dst_ips.getIpAddresses(); for (auto it = tunnel_ips.begin(); it != tunnel_ips.end(); ++it) { const IpAddress& ia = *it; - string ip = ia.to_string(); + string dst_ip = ia.to_string(); + // The key will be src_ip-dst_ip (like 10.1.1.1-20.2.2.2) if src_ip is not 0, + // or the key will contain dst_ip only + string key; + if (!src_ip.isZero()) + { + key = src_ip.to_string() + '-' + dst_ip; + } + else + { + key = dst_ip; + } - // check if the there's an entry already for the ip - if (existingIps.find(ip) != existingIps.end()) + // check if the there's an entry already for the key pair + if (existingIps.find(key) != existingIps.end()) { - SWSS_LOG_ERROR("%s already exists. Did not create entry.", ip.c_str()); + SWSS_LOG_NOTICE("%s already exists. Did not create entry.", key.c_str()); } else { @@ -397,7 +541,7 @@ bool TunnelDecapOrch::addDecapTunnelTermEntries(string tunnelKey, IpAddresses ds sai_status_t status = sai_tunnel_api->create_tunnel_term_table_entry(&tunnel_term_table_entry_id, gSwitchId, (uint32_t)tunnel_table_entry_attrs.size(), tunnel_table_entry_attrs.data()); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to create tunnel entry table for ip: %s", ip.c_str()); + SWSS_LOG_ERROR("Failed to create tunnel entry table for ip: %s", key.c_str()); task_process_status handle_status = handleSaiCreateStatus(SAI_API_TUNNEL, status); if (handle_status != task_success) { @@ -406,15 +550,15 @@ bool TunnelDecapOrch::addDecapTunnelTermEntries(string tunnelKey, IpAddresses ds } // insert into ip to entry mapping - existingIps.insert(ip); + existingIps.insert(key); // insert entry id and ip into tunnel mapping - tunnel_info->tunnel_term_info.push_back({ tunnel_term_table_entry_id, ip }); + tunnel_info->tunnel_term_info.push_back({ tunnel_term_table_entry_id, src_ip.to_string(), dst_ip, tunnel_type }); // pop the last element for the next loop tunnel_table_entry_attrs.pop_back(); - SWSS_LOG_NOTICE("Created tunnel entry for ip: %s", ip.c_str()); + SWSS_LOG_NOTICE("Created tunnel entry for ip: %s", dst_ip.c_str()); } } @@ -504,6 +648,51 @@ bool TunnelDecapOrch::setTunnelAttribute(string field, string value, sai_object_ return true; } +/** + * Function Description: + * @brief sets attributes for a tunnel (decap_dscp_to_tc_map and decap_tc_to_pg_map are supported) + * + * Arguments: + * @param[in] field - field to set the attribute for + * @param[in] value - value to set the attribute to (sai_object_id) + * @param[in] existing_tunnel_id - the id of the tunnel you want to set the attribute for + * + * Return Values: + * @return true on success and false if there's an error + */ +bool TunnelDecapOrch::setTunnelAttribute(string field, sai_object_id_t value, sai_object_id_t existing_tunnel_id) +{ + + sai_attribute_t attr; + + if (field == decap_dscp_to_tc_field_name) + { + // TC remapping. + attr.id = SAI_TUNNEL_ATTR_DECAP_QOS_DSCP_TO_TC_MAP; + attr.value.oid = value; + + } + else if (field == decap_tc_to_pg_field_name) + { + // TC to PG remapping + attr.id = SAI_TUNNEL_ATTR_DECAP_QOS_TC_TO_PRIORITY_GROUP_MAP; + attr.value.oid = value; + } + + sai_status_t status = sai_tunnel_api->set_tunnel_attribute(existing_tunnel_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set attribute %s with value %" PRIu64, field.c_str(), value); + task_process_status handle_status = handleSaiSetStatus(SAI_API_TUNNEL, status); + if (handle_status != task_success) + { + return parseHandleSaiStatusFailure(handle_status); + } + } + SWSS_LOG_NOTICE("Set attribute %s with value %" PRIu64, field.c_str(), value); + return true; +} + /** * Function Description: * @brief sets ips for a particular tunnel. deletes ips that are old and adds new ones @@ -530,7 +719,7 @@ bool TunnelDecapOrch::setIpAttribute(string key, IpAddresses new_ip_addresses, s for (auto it = tunnel_term_info_copy.begin(); it != tunnel_term_info_copy.end(); ++it) { TunnelTermEntry tunnel_entry_info = *it; - string ip = tunnel_entry_info.ip_address; + string ip = tunnel_entry_info.dst_ip; if (!new_ip_addresses.contains(ip)) { if (!removeDecapTunnelTermEntry(tunnel_entry_info.tunnel_term_id, ip)) @@ -541,12 +730,12 @@ bool TunnelDecapOrch::setIpAttribute(string key, IpAddresses new_ip_addresses, s else { // add the data into the tunnel_term_info - tunnel_info->tunnel_term_info.push_back({ tunnel_entry_info.tunnel_term_id, ip }); + tunnel_info->tunnel_term_info.push_back({ tunnel_entry_info.tunnel_term_id, "0.0.0.0", ip, TUNNEL_TERM_TYPE_P2MP }); } } // add all the new ip addresses - if(!addDecapTunnelTermEntries(key, new_ip_addresses, tunnel_id)) + if(!addDecapTunnelTermEntries(key, IpAddress(0), new_ip_addresses, tunnel_id, TUNNEL_TERM_TYPE_P2MP)) { return false; } @@ -559,12 +748,13 @@ bool TunnelDecapOrch::setIpAttribute(string key, IpAddresses new_ip_addresses, s * @brief remove decap tunnel * * Arguments: + * @param[in] table_name - name of the table in APP_DB * @param[in] key - key of the tunnel from APP_DB * * Return Values: * @return true on success and false if there's an error */ -bool TunnelDecapOrch::removeDecapTunnel(string key) +bool TunnelDecapOrch::removeDecapTunnel(string table_name, string key) { sai_status_t status; TunnelEntry *tunnel_info = &tunnelTable.find(key)->second; @@ -573,7 +763,8 @@ bool TunnelDecapOrch::removeDecapTunnel(string key) for (auto it = tunnel_info->tunnel_term_info.begin(); it != tunnel_info->tunnel_term_info.end(); ++it) { TunnelTermEntry tunnel_entry_info = *it; - if (!removeDecapTunnelTermEntry(tunnel_entry_info.tunnel_term_id, tunnel_entry_info.ip_address)) + string term_key = tunnel_entry_info.src_ip + '-' + tunnel_entry_info.dst_ip; + if (!removeDecapTunnelTermEntry(tunnel_entry_info.tunnel_term_id, term_key)) { return false; } @@ -605,6 +796,7 @@ bool TunnelDecapOrch::removeDecapTunnel(string key) } tunnelTable.erase(key); + gQosOrch->removeTunnelReference(table_name, key); return true; } @@ -618,7 +810,7 @@ bool TunnelDecapOrch::removeDecapTunnel(string key) * Return Values: * @return true on success and false if there's an error */ -bool TunnelDecapOrch::removeDecapTunnelTermEntry(sai_object_id_t tunnel_term_id, string ip) +bool TunnelDecapOrch::removeDecapTunnelTermEntry(sai_object_id_t tunnel_term_id, string key) { sai_status_t status; @@ -634,8 +826,8 @@ bool TunnelDecapOrch::removeDecapTunnelTermEntry(sai_object_id_t tunnel_term_id, } // making sure to remove all instances of the ip address - existingIps.erase(ip); - SWSS_LOG_NOTICE("Removed decap tunnel term entry with ip address: %s", ip.c_str()); + existingIps.erase(key); + SWSS_LOG_NOTICE("Removed decap tunnel term entry with ip address: %s", key.c_str()); return true; } @@ -803,3 +995,38 @@ IpAddresses TunnelDecapOrch::getDstIpAddresses(std::string tunnelKey) return tunnelTable[tunnelKey].dst_ip_addrs; } + +std::string TunnelDecapOrch::getDscpMode(const std::string &tunnelKey) const +{ + auto iter = tunnelTable.find(tunnelKey); + if (iter == tunnelTable.end()) + { + SWSS_LOG_INFO("Tunnel not found %s", tunnelKey.c_str()); + return ""; + } + return iter->second.dscp_mode; +} + +bool TunnelDecapOrch::getQosMapId(const std::string &tunnelKey, const std::string &qos_table_type, sai_object_id_t &oid) const +{ + auto iter = tunnelTable.find(tunnelKey); + if (iter == tunnelTable.end()) + { + SWSS_LOG_INFO("Tunnel not found %s", tunnelKey.c_str()); + return false; + } + if (qos_table_type == encap_tc_to_dscp_field_name) + { + oid = iter->second.encap_tc_to_dscp_map_id; + } + else if (qos_table_type == encap_tc_to_queue_field_name) + { + oid = iter->second.encap_tc_to_queue_map_id; + } + else + { + SWSS_LOG_ERROR("Unsupported qos type %s", qos_table_type.c_str()); + return false; + } + return true; +} diff --git a/orchagent/tunneldecaporch.h b/orchagent/tunneldecaporch.h index f7b5f923d9..18cf4f8856 100644 --- a/orchagent/tunneldecaporch.h +++ b/orchagent/tunneldecaporch.h @@ -9,18 +9,34 @@ #include "ipaddress.h" #include "ipaddresses.h" + +enum TunnelTermType +{ + TUNNEL_TERM_TYPE_P2P, + TUNNEL_TERM_TYPE_P2MP +}; + +/* Constants */ +#define MUX_TUNNEL "MuxTunnel0" + + struct TunnelTermEntry { sai_object_id_t tunnel_term_id; - std::string ip_address; + std::string src_ip; + std::string dst_ip; + TunnelTermType term_type; }; struct TunnelEntry { - sai_object_id_t tunnel_id; // tunnel id - sai_object_id_t overlay_intf_id; // overlay interface id - swss::IpAddresses dst_ip_addrs; // destination ip addresses - std::vector tunnel_term_info; // tunnel_entry ids related to the tunnel abd ips related to the tunnel (all ips for tunnel entries that refer to this tunnel) + sai_object_id_t tunnel_id; // tunnel id + sai_object_id_t overlay_intf_id; // overlay interface id + swss::IpAddresses dst_ip_addrs; // destination ip addresses + std::vector tunnel_term_info; // tunnel_entry ids related to the tunnel abd ips related to the tunnel (all ips for tunnel entries that refer to this tunnel) + std::string dscp_mode; // dscp_mode, will be used in muxorch + sai_object_id_t encap_tc_to_dscp_map_id; // TC_TO_DSCP map id, will be used in muxorch + sai_object_id_t encap_tc_to_queue_map_id; // TC_TO_QUEUE map id, will be used in muxorch }; struct NexthopTunnel @@ -32,7 +48,10 @@ struct NexthopTunnel /* TunnelTable: key string, tunnel object id */ typedef std::map TunnelTable; -/* ExistingIps: ips that currently have term entries */ +/* + ExistingIps: ips that currently have term entries, + Key in ExistingIps is src_ip-dst_ip +*/ typedef std::unordered_set ExistingIps; /* Nexthop IP to refcount map */ @@ -49,20 +68,23 @@ class TunnelDecapOrch : public Orch sai_object_id_t createNextHopTunnel(std::string tunnelKey, swss::IpAddress& ipAddr); bool removeNextHopTunnel(std::string tunnelKey, swss::IpAddress& ipAddr); swss::IpAddresses getDstIpAddresses(std::string tunnelKey); - + std::string getDscpMode(const std::string &tunnelKey) const; + bool getQosMapId(const std::string &tunnelKey, const std::string &qos_table_type, sai_object_id_t &oid) const; private: TunnelTable tunnelTable; ExistingIps existingIps; TunnelNhs tunnelNhs; bool addDecapTunnel(std::string key, std::string type, swss::IpAddresses dst_ip, swss::IpAddress* p_src_ip, - std::string dscp, std::string ecn, std::string encap_ecn, std::string ttl); - bool removeDecapTunnel(std::string key); + std::string dscp, std::string ecn, std::string encap_ecn, std::string ttl, + sai_object_id_t dscp_to_tc_map_id, sai_object_id_t tc_to_pg_map_id); + bool removeDecapTunnel(std::string table_name, std::string key); - bool addDecapTunnelTermEntries(std::string tunnelKey, swss::IpAddresses dst_ip, sai_object_id_t tunnel_id); + bool addDecapTunnelTermEntries(std::string tunnelKey, swss::IpAddress src_ip, swss::IpAddresses dst_ip, sai_object_id_t tunnel_id, TunnelTermType type); bool removeDecapTunnelTermEntry(sai_object_id_t tunnel_term_id, std::string ip); bool setTunnelAttribute(std::string field, std::string value, sai_object_id_t existing_tunnel_id); + bool setTunnelAttribute(std::string field, sai_object_id_t value, sai_object_id_t existing_tunnel_id); bool setIpAttribute(std::string key, swss::IpAddresses new_ip_addresses, sai_object_id_t tunnel_id); sai_object_id_t getNextHopTunnel(std::string tunnelKey, swss::IpAddress& ipAddr); diff --git a/orchagent/vnetorch.cpp b/orchagent/vnetorch.cpp index 9640e0ee3a..45ba120ee6 100644 --- a/orchagent/vnetorch.cpp +++ b/orchagent/vnetorch.cpp @@ -21,6 +21,7 @@ #include "neighorch.h" #include "crmorch.h" #include "routeorch.h" +#include "flowcounterrouteorch.h" extern sai_virtual_router_api_t* sai_virtual_router_api; extern sai_route_api_t* sai_route_api; @@ -37,6 +38,7 @@ extern PortsOrch *gPortsOrch; extern IntfsOrch *gIntfsOrch; extern NeighOrch *gNeighOrch; extern CrmOrch *gCrmOrch; +extern FlowCounterRouteOrch *gFlowCounterRouteOrch; extern RouteOrch *gRouteOrch; extern MacAddress gVxlanMacAddress; extern BfdOrch *gBfdOrch; @@ -97,6 +99,7 @@ bool VNetVrfObject::createObj(vector& attrs) vnet_name_.c_str(), status); throw std::runtime_error("Failed to create VR object"); } + gFlowCounterRouteOrch->onAddVR(router_id); return true; }; @@ -309,12 +312,16 @@ VNetVrfObject::~VNetVrfObject() set vr_ent = getVRids(); for (auto it : vr_ent) { - sai_status_t status = sai_virtual_router_api->remove_virtual_router(it); - if (status != SAI_STATUS_SUCCESS) + if (it != gVirtualRouterId) { - SWSS_LOG_ERROR("Failed to remove virtual router name: %s, rv:%d", - vnet_name_.c_str(), status); + sai_status_t status = sai_virtual_router_api->remove_virtual_router(it); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove virtual router name: %s, rv:%d", + vnet_name_.c_str(), status); + } } + gFlowCounterRouteOrch->onRemoveVR(it); } SWSS_LOG_INFO("VNET '%s' deleted ", vnet_name_.c_str()); @@ -551,6 +558,40 @@ bool VNetOrch::delOperation(const Request& request) return true; } +bool VNetOrch::getVrfIdByVnetName(const std::string& vnet_name, sai_object_id_t &vrf_id) +{ + if (!isVnetExists(vnet_name)) + { + return false; + } + + auto *vrf_obj = getTypePtr(vnet_name); + // Now we only support ingress VR for VNET, so just get ingress VR ID + // Once we support egress VR, need revisit here. + vrf_id = vrf_obj->getVRidIngress(); + return vrf_id != SAI_NULL_OBJECT_ID; +} + +bool VNetOrch::getVnetNameByVrfId(sai_object_id_t vrf_id, std::string& vnet_name) +{ + for (auto &entry : vnet_table_) + { + auto *vrf_obj = dynamic_cast(entry.second.get()); + if (!vrf_obj) + { + continue; + } + + if (vrf_obj->getVRidIngress() == vrf_id) + { + vnet_name = entry.first; + return true; + } + } + + return false; +} + /* * Vnet Route Handling */ @@ -583,6 +624,8 @@ static bool del_route(sai_object_id_t vr_id, sai_ip_prefix_t& ip_pfx) gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); } + gFlowCounterRouteOrch->onRemoveMiscRouteEntry(vr_id, ip_pfx, false); + return true; } @@ -614,6 +657,8 @@ static bool add_route(sai_object_id_t vr_id, sai_ip_prefix_t& ip_pfx, sai_object gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); } + gFlowCounterRouteOrch->onAddMiscRouteEntry(vr_id, ip_pfx, false); + return true; } @@ -827,7 +872,7 @@ bool VNetRouteOrch::removeNextHopGroup(const string& vnet, const NextHopGroupKey template<> bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipPrefix, - NextHopGroupKey& nexthops, string& op, + NextHopGroupKey& nexthops, string& op, const map& monitors) { SWSS_LOG_ENTER(); @@ -923,8 +968,8 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP if (syncd_nexthop_groups_[vnet][nhg].active_members.empty()) { route_status = add_route(vr_id, pfx, nh_id); - } - else + } + else { route_status = update_route(vr_id, pfx, nh_id); } @@ -949,7 +994,7 @@ bool VNetRouteOrch::doRouteTask(const string& vnet, IpPrefix& ipP NextHopGroupKey nhg = it_route->second; if(--syncd_nexthop_groups_[vnet][nhg].ref_count == 0) { - if (nexthops.getSize() > 1) + if (nhg.getSize() > 1) { removeNextHopGroup(vnet, nhg, vrf_obj); } @@ -1267,7 +1312,7 @@ bool VNetRouteOrch::handleRoutes(const Request& request) SWSS_LOG_INFO("VNET-RT '%s' op '%s' for ip %s", vnet_name.c_str(), op.c_str(), ip_pfx.to_string().c_str()); - + if (op == SET_COMMAND) { addRoute(vnet_name, ip_pfx, nh); @@ -1321,7 +1366,7 @@ void VNetRouteOrch::attach(Observer* observer, const IpAddress& dstAddr) dstAddr.to_string().c_str()); for (auto vnetEntry : bestRoute->second) { - VNetNextHopUpdate update = + VNetNextHopUpdate update = { SET_COMMAND, vnetEntry.first, // vnet name @@ -1362,7 +1407,7 @@ void VNetRouteOrch::detach(Observer* observer, const IpAddress& dstAddr) { for (auto vnetEntry : bestRoute->second) { - VNetNextHopUpdate update = + VNetNextHopUpdate update = { DEL_COMMAND, vnetEntry.first, // vnet name @@ -1383,12 +1428,12 @@ void VNetRouteOrch::addRoute(const std::string& vnet, const IpPrefix& ipPrefix, { if (ipPrefix.isAddressInSubnet(next_hop_observer.first)) { - auto route_insert_result = next_hop_observer.second.routeTable.emplace(ipPrefix, VNetEntry()); + auto route_insert_result = next_hop_observer.second.routeTable.emplace(ipPrefix, VNetEntry()); auto vnet_result_result = route_insert_result.first->second.emplace(vnet, nh); if (!vnet_result_result.second) { - if (vnet_result_result.first->second.ips == nh.ips + if (vnet_result_result.first->second.ips == nh.ips && vnet_result_result.first->second.ifname == nh.ifname) { continue; @@ -1399,7 +1444,7 @@ void VNetRouteOrch::addRoute(const std::string& vnet, const IpPrefix& ipPrefix, // If the inserted route is the best route. (Table should not be empty. Because we inserted a new entry above) if (route_insert_result.first == --next_hop_observer.second.routeTable.end()) { - VNetNextHopUpdate update = + VNetNextHopUpdate update = { SET_COMMAND, vnet, // vnet name @@ -1437,7 +1482,7 @@ void VNetRouteOrch::delRoute(const IpPrefix& ipPrefix) if ( itr == next_hop_observer->second.routeTable.end()) { SWSS_LOG_ERROR( - "Failed to find any ip(%s) belong to this route(%s).", + "Failed to find any ip(%s) belong to this route(%s).", next_hop_observer->first.to_string().c_str(), ipPrefix.to_string().c_str()); assert(false); @@ -1494,6 +1539,8 @@ void VNetRouteOrch::createBfdSession(const string& vnet, const NextHopKey& endpo FieldValueTuple fvTuple("local_addr", src_ip.to_string()); data.push_back(fvTuple); + data.emplace_back("multihop", "true"); + bfd_session_producer_.set(key, data); bfd_sessions_[monitor_addr].bfd_state = SAI_BFD_SESSION_STATE_DOWN; @@ -1875,7 +1922,7 @@ bool VNetRouteOrch::handleTunnel(const Request& request) SWSS_LOG_ERROR("Peer monitor size of %zu does not match endpoint size of %zu", monitor_list.size(), ip_list.size()); return false; } - + const std::string& vnet_name = request.getKeyString(0); auto ip_pfx = request.getKeyIpPrefix(1); auto op = request.getOperation(); diff --git a/orchagent/vnetorch.h b/orchagent/vnetorch.h index 77c2785371..26e0733337 100644 --- a/orchagent/vnetorch.h +++ b/orchagent/vnetorch.h @@ -190,6 +190,9 @@ class VNetVrfObject : public VNetObject void increaseNextHopRefCount(const nextHop&); void decreaseNextHopRefCount(const nextHop&); + const RouteMap &getRouteMap() const { return routes_; } + const TunnelRoutes &getTunnelRoutes() const { return tunnels_; } + ~VNetVrfObject(); private: @@ -247,6 +250,9 @@ class VNetOrch : public Orch2 return (vnet_exec_ == VNET_EXEC::VNET_EXEC_BRIDGE); } + bool getVrfIdByVnetName(const std::string& vnet_name, sai_object_id_t &vrf_id); + bool getVnetNameByVrfId(sai_object_id_t vrf_id, std::string& vnet_name); + private: virtual bool addOperation(const Request& request); virtual bool delOperation(const Request& request); diff --git a/orchagent/vrforch.cpp b/orchagent/vrforch.cpp index 19ca5c0fd8..776cf1eb0f 100644 --- a/orchagent/vrforch.cpp +++ b/orchagent/vrforch.cpp @@ -11,6 +11,7 @@ #include "request_parser.h" #include "vrforch.h" #include "vxlanorch.h" +#include "flowcounterrouteorch.h" #include "directory.h" using namespace std; @@ -18,8 +19,10 @@ using namespace swss; extern sai_virtual_router_api_t* sai_virtual_router_api; extern sai_object_id_t gSwitchId; -extern Directory gDirectory; -extern PortsOrch* gPortsOrch; + +extern Directory gDirectory; +extern PortsOrch* gPortsOrch; +extern FlowCounterRouteOrch* gFlowCounterRouteOrch; bool VRFOrch::addOperation(const Request& request) { @@ -104,6 +107,7 @@ bool VRFOrch::addOperation(const Request& request) vrf_table_[vrf_name].vrf_id = router_id; vrf_table_[vrf_name].ref_count = 0; vrf_id_table_[router_id] = vrf_name; + gFlowCounterRouteOrch->onAddVR(router_id); if (vni != 0) { SWSS_LOG_INFO("VRF '%s' vni %d add", vrf_name.c_str(), vni); @@ -176,6 +180,8 @@ bool VRFOrch::delOperation(const Request& request) } } + gFlowCounterRouteOrch->onRemoveVR(router_id); + vrf_table_.erase(vrf_name); vrf_id_table_.erase(router_id); error = delVrfVNIMap(vrf_name, 0); diff --git a/orchagent/vxlanorch.cpp b/orchagent/vxlanorch.cpp index fc6a505a1f..8fce069631 100644 --- a/orchagent/vxlanorch.cpp +++ b/orchagent/vxlanorch.cpp @@ -1542,28 +1542,12 @@ bool VxlanTunnelOrch::removeVxlanTunnelMap(string tunnelName, uint32_t vni) tunnel_obj->vlan_vrf_vni_count--; if (tunnel_obj->vlan_vrf_vni_count == 0) { - auto tunnel_term_id = vxlan_tunnel_table_[tunnelName].get()->getTunnelTermId(); - try - { - remove_tunnel_termination(tunnel_term_id); - } - catch(const std::runtime_error& error) - { - SWSS_LOG_ERROR("Error removing tunnel term entry. Tunnel: %s. Error: %s", tunnelName.c_str(), error.what()); - return false; - } - - auto tunnel_id = vxlan_tunnel_table_[tunnelName].get()->getTunnelId(); - try - { - removeTunnelFromFlexCounter(tunnel_id, tunnelName); - remove_tunnel(tunnel_id); - } - catch(const std::runtime_error& error) - { - SWSS_LOG_ERROR("Error removing tunnel entry. Tunnel: %s. Error: %s", tunnelName.c_str(), error.what()); - return false; - } + uint8_t mapper_list = 0; + + TUNNELMAP_SET_VLAN(mapper_list); + TUNNELMAP_SET_VRF(mapper_list); + + tunnel_obj->deleteTunnelHw(mapper_list, TUNNEL_MAP_USE_DEDICATED_ENCAP_DECAP); } SWSS_LOG_NOTICE("Vxlan map entry deleted for tunnel '%s' with vni '%d'", tunnelName.c_str(), vni); diff --git a/portsyncd/Makefile.am b/portsyncd/Makefile.am index 5bba269ab2..3db6187059 100644 --- a/portsyncd/Makefile.am +++ b/portsyncd/Makefile.am @@ -10,10 +10,15 @@ endif portsyncd_SOURCES = $(top_srcdir)/lib/gearboxutils.cpp portsyncd.cpp linksync.cpp $(top_srcdir)/cfgmgr/shellcmd.h -portsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -portsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -portsyncd_LDADD = -lnl-3 -lnl-route-3 -lswsscommon +portsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +portsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +portsyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lswsscommon if GCOV_ENABLED portsyncd_LDADD += -lgcovpreload endif + +if ASAN_ENABLED +portsyncd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/portsyncd/linksync.cpp b/portsyncd/linksync.cpp index 4a2b351ee0..fc28411613 100644 --- a/portsyncd/linksync.cpp +++ b/portsyncd/linksync.cpp @@ -2,7 +2,7 @@ #include #include #include -#include +#include #include #include "logger.h" #include "netmsg.h" @@ -34,23 +34,16 @@ const string LAG_PREFIX = "PortChannel"; extern set g_portSet; extern bool g_init; -struct if_nameindex -{ - unsigned int if_index; - char *if_name; -}; -extern "C" { extern struct if_nameindex *if_nameindex (void) __THROW; } - LinkSync::LinkSync(DBConnector *appl_db, DBConnector *state_db) : m_portTableProducer(appl_db, APP_PORT_TABLE_NAME), m_portTable(appl_db, APP_PORT_TABLE_NAME), m_statePortTable(state_db, STATE_PORT_TABLE_NAME), m_stateMgmtPortTable(state_db, STATE_MGMT_PORT_TABLE_NAME) { - struct if_nameindex *if_ni, *idx_p; - if_ni = if_nameindex(); + std::shared_ptr if_ni(if_nameindex(), if_freenameindex); + struct if_nameindex *idx_p; - for (idx_p = if_ni; + for (idx_p = if_ni.get(); idx_p != NULL && idx_p->if_index != 0 && idx_p->if_name != NULL; idx_p++) { @@ -121,7 +114,7 @@ LinkSync::LinkSync(DBConnector *appl_db, DBConnector *state_db) : } } - for (idx_p = if_ni; + for (idx_p = if_ni.get(); idx_p != NULL && idx_p->if_index != 0 && idx_p->if_name != NULL; idx_p++) { @@ -212,10 +205,9 @@ void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj) return; } - /* If netlink for this port has master, we ignore that for now - * This could be the case where the port was removed from VLAN bridge - */ - if (master) + /* Ignore DELLINK message if port has master, this is applicable to + * the case where port was part of VLAN bridge or LAG */ + if (master && nlmsg_type == RTM_DELLINK) { return; } diff --git a/portsyncd/portsyncd.cpp b/portsyncd/portsyncd.cpp index c55c1685af..37e0c4232f 100644 --- a/portsyncd/portsyncd.cpp +++ b/portsyncd/portsyncd.cpp @@ -228,11 +228,6 @@ void 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()) { @@ -247,54 +242,7 @@ void handlePortConfig(ProducerStateTable &p, map /* No support for port delete yet */ if (op == SET_COMMAND) { - - 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(); + p.set(key, values); } it = port_cfg_map.erase(it); diff --git a/swssconfig/Makefile.am b/swssconfig/Makefile.am index 590e7d9f56..3cfc0b9629 100644 --- a/swssconfig/Makefile.am +++ b/swssconfig/Makefile.am @@ -10,17 +10,23 @@ endif swssconfig_SOURCES = swssconfig.cpp -swssconfig_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -swssconfig_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -swssconfig_LDADD = -lswsscommon +swssconfig_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +swssconfig_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +swssconfig_LDADD = $(LDFLAGS_ASAN) -lswsscommon swssplayer_SOURCES = swssplayer.cpp -swssplayer_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -swssplayer_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -swssplayer_LDADD = -lswsscommon +swssplayer_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +swssplayer_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +swssplayer_LDADD = $(LDFLAGS_ASAN) -lswsscommon if GCOV_ENABLED swssconfig_LDADD += -lgcovpreload swssplayer_LDADD += -lgcovpreload endif + +if ASAN_ENABLED +swssconfig_SOURCES += $(top_srcdir)/lib/asan.cpp +swssplayer_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/teamsyncd/Makefile.am b/teamsyncd/Makefile.am index 2939a52f2a..a13573bf25 100644 --- a/teamsyncd/Makefile.am +++ b/teamsyncd/Makefile.am @@ -10,10 +10,15 @@ endif teamsyncd_SOURCES = teamsyncd.cpp teamsync.cpp -teamsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -teamsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -teamsyncd_LDADD = -lnl-3 -lnl-route-3 -lhiredis -lswsscommon -lteam +teamsyncd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +teamsyncd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +teamsyncd_LDADD = $(LDFLAGS_ASAN) -lnl-3 -lnl-route-3 -lhiredis -lswsscommon -lteam if GCOV_ENABLED teamsyncd_LDADD += -lgcovpreload endif + +if ASAN_ENABLED +teamsyncd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif + diff --git a/teamsyncd/teamsyncd.cpp b/teamsyncd/teamsyncd.cpp index c5190f46b1..95890be406 100644 --- a/teamsyncd/teamsyncd.cpp +++ b/teamsyncd/teamsyncd.cpp @@ -11,9 +11,16 @@ using namespace std; using namespace swss; bool received_sigterm = false; +static struct sigaction old_sigaction; void sig_handler(int signo) { + SWSS_LOG_ENTER(); + + if (old_sigaction.sa_handler != SIG_IGN && old_sigaction.sa_handler != SIG_DFL) { + old_sigaction.sa_handler(signo); + } + received_sigterm = true; return; } @@ -30,7 +37,13 @@ int main(int argc, char **argv) NetDispatcher::getInstance().registerMessageHandler(RTM_DELLINK, &sync); /* Register the signal handler for SIGTERM */ - signal(SIGTERM, sig_handler); + struct sigaction sigact = {}; + sigact.sa_handler = sig_handler; + if (sigaction(SIGTERM, &sigact, &old_sigaction)) + { + SWSS_LOG_ERROR("failed to setup SIGTERM action handler"); + exit(EXIT_FAILURE); + } try { diff --git a/tests/conftest.py b/tests/conftest.py index a460239d3c..0cf6320346 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -22,6 +22,7 @@ from dvslib.dvs_pbh import DVSPbh from dvslib.dvs_route import DVSRoute from dvslib import dvs_vlan +from dvslib import dvs_port from dvslib import dvs_lag from dvslib import dvs_mirror from dvslib import dvs_policer @@ -1157,6 +1158,132 @@ def getCrmCounterValue(self, key, counter): if k[0] == counter: return int(k[1]) + def port_field_set(self, port, field, value): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "PORT") + fvs = swsscommon.FieldValuePairs([(field, value)]) + tbl.set(port, fvs) + time.sleep(1) + + def port_admin_set(self, port, status): + self.port_field_set(port, "admin_status", status) + + def interface_ip_add(self, port, ip_address): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + tbl.set(port, fvs) + tbl.set(port + "|" + ip_address, fvs) + time.sleep(1) + + def crm_poll_set(self, value): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "CRM") + fvs = swsscommon.FieldValuePairs([("polling_interval", value)]) + tbl.set("Config", fvs) + time.sleep(1) + + def clear_fdb(self): + adb = swsscommon.DBConnector(0, self.redis_sock, 0) + opdata = ["ALL", "ALL"] + msg = json.dumps(opdata,separators=(',',':')) + adb.publish('FLUSHFDBREQUEST', msg) + + def warm_restart_swss(self, enable): + db = swsscommon.DBConnector(6, self.redis_sock, 0) + + tbl = swsscommon.Table(db, "WARM_RESTART_ENABLE_TABLE") + fvs = swsscommon.FieldValuePairs([("enable",enable)]) + tbl.set("swss", fvs) + + # nat + def nat_mode_set(self, value): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "NAT_GLOBAL") + fvs = swsscommon.FieldValuePairs([("admin_mode", value)]) + tbl.set("Values", fvs) + time.sleep(1) + + def nat_timeout_set(self, value): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "NAT_GLOBAL") + fvs = swsscommon.FieldValuePairs([("nat_timeout", value)]) + tbl.set("Values", fvs) + time.sleep(1) + + def nat_udp_timeout_set(self, value): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "NAT_GLOBAL") + fvs = swsscommon.FieldValuePairs([("nat_udp_timeout", value)]) + tbl.set("Values", fvs) + time.sleep(1) + + def nat_tcp_timeout_set(self, value): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "NAT_GLOBAL") + fvs = swsscommon.FieldValuePairs([("nat_tcp_timeout", value)]) + tbl.set("Values", fvs) + time.sleep(1) + + def add_nat_basic_entry(self, external, internal): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "STATIC_NAT") + fvs = swsscommon.FieldValuePairs([("local_ip", internal)]) + tbl.set(external, fvs) + time.sleep(1) + + def del_nat_basic_entry(self, external): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "STATIC_NAT") + tbl._del(external) + time.sleep(1) + + def add_nat_udp_entry(self, external, extport, internal, intport): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "STATIC_NAPT") + fvs = swsscommon.FieldValuePairs([("local_ip", internal), ("local_port", intport)]) + tbl.set(external + "|UDP|" + extport, fvs) + time.sleep(1) + + def del_nat_udp_entry(self, external, extport): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "STATIC_NAPT") + tbl._del(external + "|UDP|" + extport) + time.sleep(1) + + def add_twice_nat_basic_entry(self, external, internal, nat_type, twice_nat_id): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "STATIC_NAT") + fvs = swsscommon.FieldValuePairs([("local_ip", internal), ("nat_type", nat_type), ("twice_nat_id", twice_nat_id)]) + tbl.set(external, fvs) + time.sleep(1) + + def del_twice_nat_basic_entry(self, external): + self.del_nat_basic_entry(external) + + def add_twice_nat_udp_entry(self, external, extport, internal, intport, nat_type, twice_nat_id): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + tbl = swsscommon.Table(cdb, "STATIC_NAPT") + fvs = swsscommon.FieldValuePairs([("local_ip", internal), ("local_port", intport), ("nat_type", nat_type), ("twice_nat_id", twice_nat_id)]) + tbl.set(external + "|UDP|" + extport, fvs) + time.sleep(1) + + def del_twice_nat_udp_entry(self, external, extport): + self.del_nat_udp_entry(external, extport) + + def set_nat_zone(self, interface, nat_zone): + cdb = swsscommon.DBConnector(4, self.redis_sock, 0) + if interface.startswith("PortChannel"): + tbl_name = "PORTCHANNEL_INTERFACE" + elif interface.startswith("Vlan"): + tbl_name = "VLAN_INTERFACE" + else: + tbl_name = "INTERFACE" + tbl = swsscommon.Table(cdb, tbl_name) + fvs = swsscommon.FieldValuePairs([("nat_zone", nat_zone)]) + tbl.set(interface, fvs) + time.sleep(1) + # deps: acl, crm, fdb def setReadOnlyAttr(self, obj, attr, val): db = swsscommon.DBConnector(swsscommon.ASIC_DB, self.redis_sock, 0) @@ -1729,6 +1856,12 @@ def dvs_vlan_manager(request, dvs): dvs.get_app_db()) +@pytest.fixture(scope="class") +def dvs_port_manager(request, dvs): + request.cls.dvs_port = dvs_port.DVSPort(dvs.get_asic_db(), + dvs.get_config_db()) + + @pytest.fixture(scope="class") def dvs_mirror_manager(request, dvs): request.cls.dvs_mirror = dvs_mirror.DVSMirror(dvs.get_asic_db(), diff --git a/tests/dvslib/dvs_acl.py b/tests/dvslib/dvs_acl.py index 9111de7a8e..266761c568 100644 --- a/tests/dvslib/dvs_acl.py +++ b/tests/dvslib/dvs_acl.py @@ -16,6 +16,7 @@ class DVSAcl: ADB_ACL_TABLE_NAME = "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE" ADB_ACL_GROUP_TABLE_NAME = "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP" ADB_ACL_GROUP_MEMBER_TABLE_NAME = "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER" + ADB_ACL_COUNTER_TABLE_NAME = "ASIC_STATE:SAI_OBJECT_TYPE_ACL_COUNTER" ADB_ACL_STAGE_LOOKUP = { "ingress": "SAI_ACL_STAGE_INGRESS", @@ -140,6 +141,19 @@ def remove_acl_table_type(self, name: str) -> None: """ self.config_db.delete_entry(self.CDB_ACL_TABLE_TYPE_NAME, name) + def get_acl_counter_ids(self, expected: int) -> List[str]: + """Get all of the ACL counter IDs in ASIC DB. + + This method will wait for the expected number of counters to exist, or fail. + + Args: + expected: The number of counters that are expected to be present in ASIC DB. + + Returns: + The list of ACL counter IDs in ASIC DB. + """ + return self.asic_db.wait_for_n_keys(self.ADB_ACL_COUNTER_TABLE_NAME, expected) + def get_acl_table_ids(self, expected: int) -> List[str]: """Get all of the ACL table IDs in ASIC DB. @@ -530,6 +544,39 @@ def verify_mirror_acl_rule( self._check_acl_entry_mirror_action(fvs, session_oid, stage) self._check_acl_entry_counters_map(acl_rule_id) + def verify_acl_rule_generic( + self, + sai_qualifiers: Dict[str, str], + acl_table_id: str = None, + acl_rule_id: str = None + ) -> None: + """Verify that an ACL rule has the correct ASIC DB representation. + + Args: + sai_qualifiers: The expected set of SAI qualifiers to be found in ASIC DB. + acl_table_id: A specific OID to check in ASIC DB. If left empty, this method + assumes that only one table exists in ASIC DB. + acl_rule_id: A specific OID to check in ASIC DB. If left empty, this method + assumes that only one rule exists in ASIC DB. + """ + if not acl_table_id: + acl_table_id = self.get_acl_table_ids(1)[0] + + if not acl_rule_id: + acl_rule_id = self._get_acl_rule_id() + + entry = self.asic_db.wait_for_entry("ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY", acl_rule_id) + + for k, v in entry.items(): + if k == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert v == acl_table_id + elif k == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert v == "true" + elif k in sai_qualifiers: + assert sai_qualifiers[k](v) + else: + assert False, "Unknown SAI qualifier: key={}, value={}".format(k, v) + def verify_acl_rule_set( self, priorities: List[str], diff --git a/tests/dvslib/dvs_common.py b/tests/dvslib/dvs_common.py index b2a09d5da7..0d81b4cf2e 100644 --- a/tests/dvslib/dvs_common.py +++ b/tests/dvslib/dvs_common.py @@ -17,7 +17,7 @@ class PollingConfig: """ polling_interval: float = 0.01 - timeout: float = 5.00 + timeout: float = 20.00 strict: bool = True def iterations(self) -> int: diff --git a/tests/dvslib/dvs_database.py b/tests/dvslib/dvs_database.py index f2657f7516..371b7f61e9 100644 --- a/tests/dvslib/dvs_database.py +++ b/tests/dvslib/dvs_database.py @@ -34,6 +34,24 @@ def create_entry(self, table_name: str, key: str, entry: Dict[str, str]) -> None formatted_entry = swsscommon.FieldValuePairs(list(entry.items())) table.set(key, formatted_entry) + def set_entry(self, table_name: str, key: str, entry: Dict[str, str]) -> None: + """Set entry of an existing key in the specified table. + + Args: + table_name: The name of the table. + key: The key that needs to be updated. + entry: A set of key-value pairs to be updated. + """ + table = swsscommon.Table(self.db_connection, table_name) + (status, fv_pairs) = table.get(key) + + formatted_entry = swsscommon.FieldValuePairs(list(entry.items())) + table.set(key, formatted_entry) + + if status: + for f in [ k for k, v in dict(fv_pairs).items() if k not in entry.keys() ]: + table.hdel(key, f) + def update_entry(self, table_name: str, key: str, entry: Dict[str, str]) -> None: """Update entry of an existing key in the specified table. @@ -74,6 +92,17 @@ def delete_entry(self, table_name: str, key: str) -> None: table = swsscommon.Table(self.db_connection, table_name) table._del(key) # pylint: disable=protected-access + def delete_field(self, table_name: str, key: str, field: str) -> None: + """Remove a field from an entry stored at `key` in the specified table. + + Args: + table_name: The name of the table where the entry is being removed. + key: The key that maps to the entry being removed. + field: The field that needs to be removed + """ + table = swsscommon.Table(self.db_connection, table_name) + table.hdel(key, field) + def get_keys(self, table_name: str) -> List[str]: """Get all of the keys stored in the specified table. diff --git a/tests/dvslib/dvs_pbh.py b/tests/dvslib/dvs_pbh.py index 79a58681a9..df612638ea 100644 --- a/tests/dvslib/dvs_pbh.py +++ b/tests/dvslib/dvs_pbh.py @@ -10,6 +10,8 @@ class DVSPbh: CDB_PBH_HASH = "PBH_HASH" CDB_PBH_HASH_FIELD = "PBH_HASH_FIELD" + ADB_PBH_HASH = "ASIC_STATE:SAI_OBJECT_TYPE_HASH" + def __init__(self, asic_db, config_db): """Create a new DVS PBH Manager.""" self.asic_db = asic_db @@ -60,6 +62,27 @@ def create_pbh_rule( self.config_db.create_entry(self.CDB_PBH_RULE, "{}|{}".format(table_name, rule_name), attr_dict) + def update_pbh_rule( + self, + table_name: str, + rule_name: str, + priority: str, + qualifiers: Dict[str, str], + hash_name: str, + packet_action: str = "SET_ECMP_HASH", + flow_counter: str = "DISABLED" + ) -> None: + """Update PBH rule in Config DB.""" + attr_dict = { + "priority": priority, + "hash": hash_name, + "packet_action": packet_action, + "flow_counter": flow_counter, + **qualifiers + } + + self.config_db.set_entry(self.CDB_PBH_RULE, "{}|{}".format(table_name, rule_name), attr_dict) + def remove_pbh_rule( self, table_name: str, @@ -125,3 +148,10 @@ def verify_pbh_hash_field_count( ) -> None: """Verify that there are N hash field objects in ASIC DB.""" self.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_FINE_GRAINED_HASH_FIELD", expected) + + def get_pbh_hash_ids( + self, + expected: int + ) -> List[str]: + """Get all of the PBH hash IDs in ASIC DB.""" + return self.asic_db.wait_for_n_keys(self.ADB_PBH_HASH, expected) diff --git a/tests/dvslib/dvs_port.py b/tests/dvslib/dvs_port.py new file mode 100644 index 0000000000..8c53994242 --- /dev/null +++ b/tests/dvslib/dvs_port.py @@ -0,0 +1,20 @@ + +class DVSPort(object): + def __init__(self, adb, cdb): + self.asic_db = adb + self.config_db = cdb + + def remove_port(self, port_name): + self.config_db.delete_field("CABLE_LENGTH", "AZURE", port_name) + + port_bufferpg_keys = self.config_db.get_keys("BUFFER_PG|%s" % port_name) + for key in port_bufferpg_keys: + self.config_db.delete_entry("BUFFER_PG|%s|%s" % (port_name, key), "") + + port_bufferqueue_keys = self.config_db.get_keys("BUFFER_QUEUE|%s" % port_name) + for key in port_bufferqueue_keys: + self.config_db.delete_entry("BUFFER_QUEUE|%s|%s" % (port_name, key), "") + + self.config_db.delete_entry("BREAKOUT_CFG|%s" % port_name, "") + self.config_db.delete_entry("INTERFACE|%s" % port_name, "") + self.config_db.delete_entry("PORT", port_name) diff --git a/tests/gcov_support.sh b/tests/gcov_support.sh index 4200e20813..d96ee1c250 100755 --- a/tests/gcov_support.sh +++ b/tests/gcov_support.sh @@ -146,7 +146,8 @@ lcov_merge_all() cp $1/lcov_cobertura.py $1/common_work/gcov/ python $1/common_work/gcov/lcov_cobertura.py total.info -o coverage.xml - sed -i "s#common_work/#$1/common_work/#" coverage.xml + sed -i "s#common_work/gcov/##" coverage.xml + sed -i "s#common_work.gcov.##" coverage.xml cd gcov_output/ if [ ! -d ${ALLMERGE_DIR} ]; then diff --git a/tests/mock_tests/Makefile.am b/tests/mock_tests/Makefile.am index f82b556e47..2a6dade254 100644 --- a/tests/mock_tests/Makefile.am +++ b/tests/mock_tests/Makefile.am @@ -6,9 +6,9 @@ INCLUDES = -I $(FLEX_CTR_DIR) -I $(DEBUG_CTR_DIR) -I $(top_srcdir)/lib CFLAGS_SAI = -I /usr/include/sai -TESTS = tests +TESTS = tests tests_intfmgrd -noinst_PROGRAMS = tests +noinst_PROGRAMS = tests tests_intfmgrd LDADD_SAI = -lsaimeta -lsaimetadata -lsaivs -lsairedis @@ -24,6 +24,10 @@ LDADD_GTEST = -L/usr/src/gtest tests_SOURCES = aclorch_ut.cpp \ portsorch_ut.cpp \ routeorch_ut.cpp \ + qosorch_ut.cpp \ + bufferorch_ut.cpp \ + fdborch/flush_syncd_notif_ut.cpp \ + copporch_ut.cpp \ saispy_ut.cpp \ consumer_ut.cpp \ ut_saihelper.cpp \ @@ -35,6 +39,8 @@ tests_SOURCES = aclorch_ut.cpp \ mock_redisreply.cpp \ bulker_ut.cpp \ fake_response_publisher.cpp \ + swssnet_ut.cpp \ + flowcounterrouteorch_ut.cpp \ $(top_srcdir)/lib/gearboxutils.cpp \ $(top_srcdir)/lib/subintf.cpp \ $(top_srcdir)/orchagent/orchdaemon.cpp \ @@ -58,6 +64,7 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/mirrororch.cpp \ $(top_srcdir)/orchagent/fdborch.cpp \ $(top_srcdir)/orchagent/aclorch.cpp \ + $(top_srcdir)/orchagent/pbh/pbhcap.cpp \ $(top_srcdir)/orchagent/pbh/pbhcnt.cpp \ $(top_srcdir)/orchagent/pbh/pbhmgr.cpp \ $(top_srcdir)/orchagent/pbh/pbhrule.cpp \ @@ -87,9 +94,10 @@ tests_SOURCES = aclorch_ut.cpp \ $(top_srcdir)/orchagent/macsecorch.cpp \ $(top_srcdir)/orchagent/lagid.cpp \ $(top_srcdir)/orchagent/bfdorch.cpp \ - $(top_srcdir)/orchagent/srv6orch.cpp + $(top_srcdir)/orchagent/srv6orch.cpp \ + $(top_srcdir)/orchagent/nvgreorch.cpp -tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp $(FLEX_CTR_DIR)/flow_counter_handler.cpp +tests_SOURCES += $(FLEX_CTR_DIR)/flex_counter_manager.cpp $(FLEX_CTR_DIR)/flex_counter_stat_manager.cpp $(FLEX_CTR_DIR)/flow_counter_handler.cpp $(FLEX_CTR_DIR)/flowcounterrouteorch.cpp tests_SOURCES += $(DEBUG_CTR_DIR)/debug_counter.cpp $(DEBUG_CTR_DIR)/drop_counter.cpp tests_SOURCES += $(P4_ORCH_DIR)/p4orch.cpp \ $(P4_ORCH_DIR)/p4orch_util.cpp \ @@ -108,3 +116,22 @@ tests_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAG tests_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) -I$(top_srcdir)/orchagent tests_LDADD = $(LDADD_GTEST) $(LDADD_SAI) -lnl-genl-3 -lhiredis -lhiredis -lpthread \ -lswsscommon -lswsscommon -lgtest -lgtest_main -lzmq -lnl-3 -lnl-route-3 + +## intfmgrd unit tests + +tests_intfmgrd_SOURCES = intfmgrd/add_ipv6_prefix_ut.cpp \ + $(top_srcdir)/cfgmgr/intfmgr.cpp \ + $(top_srcdir)/orchagent/orch.cpp \ + $(top_srcdir)/orchagent/request_parser.cpp \ + $(top_srcdir)/lib/subintf.cpp \ + mock_orchagent_main.cpp \ + mock_dbconnector.cpp \ + mock_table.cpp \ + mock_hiredis.cpp \ + fake_response_publisher.cpp \ + mock_redisreply.cpp + +tests_intfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) +tests_intfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) -I $(top_srcdir)/cfgmgr -I $(top_srcdir)/orchagent/ +tests_intfmgrd_LDADD = $(LDADD_GTEST) $(LDADD_SAI) -lnl-genl-3 -lhiredis -lhiredis \ + -lswsscommon -lswsscommon -lgtest -lgtest_main -lzmq -lnl-3 -lnl-route-3 -lpthread diff --git a/tests/mock_tests/aclorch_ut.cpp b/tests/mock_tests/aclorch_ut.cpp index 295fed20ba..0d81c93f69 100644 --- a/tests/mock_tests/aclorch_ut.cpp +++ b/tests/mock_tests/aclorch_ut.cpp @@ -1,4 +1,5 @@ #include "ut_helper.h" +#include "flowcounterrouteorch.h" extern sai_object_id_t gSwitchId; @@ -6,6 +7,7 @@ extern SwitchOrch *gSwitchOrch; extern CrmOrch *gCrmOrch; extern PortsOrch *gPortsOrch; extern RouteOrch *gRouteOrch; +extern FlowCounterRouteOrch *gFlowCounterRouteOrch; extern IntfsOrch *gIntfsOrch; extern NeighOrch *gNeighOrch; extern FgNhgOrch *gFgNhgOrch; @@ -372,6 +374,11 @@ namespace aclorch_test ASSERT_EQ(gPortsOrch, nullptr); gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + static const vector route_pattern_tables = { + CFG_FLOW_COUNTER_ROUTE_PATTERN_TABLE_NAME, + }; + gFlowCounterRouteOrch = new FlowCounterRouteOrch(m_config_db.get(), route_pattern_tables); + ASSERT_EQ(gVrfOrch, nullptr); gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); @@ -419,7 +426,12 @@ namespace aclorch_test }; gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); - PolicerOrch *policer_orch = new PolicerOrch(m_config_db.get(), "POLICER"); + vector policer_tables = { + TableConnector(m_config_db.get(), CFG_POLICER_TABLE_NAME), + TableConnector(m_config_db.get(), CFG_PORT_STORM_CONTROL_TABLE_NAME) + }; + TableConnector stateDbStorm(m_state_db.get(), "BUM_STORM_CAPABILITY"); + PolicerOrch *policer_orch = new PolicerOrch(policer_tables, gPortsOrch); TableConnector stateDbMirrorSession(m_state_db.get(), STATE_MIRROR_SESSION_TABLE_NAME); TableConnector confDbMirrorSession(m_config_db.get(), CFG_MIRROR_SESSION_TABLE_NAME); @@ -1719,4 +1731,28 @@ namespace aclorch_test ASSERT_TRUE(orch->m_aclOrch->removeAclRule(rule->getTableId(), rule->getId())); } + TEST_F(AclOrchTest, deleteNonExistingRule) + { + string tableId = "acl_table"; + string ruleId = "acl_rule"; + + auto orch = createAclOrch(); + + // add acl table + auto kvfAclTable = deque({{ + tableId, + SET_COMMAND, + { + { ACL_TABLE_DESCRIPTION, "L3 table" }, + { ACL_TABLE_TYPE, TABLE_TYPE_L3 }, + { ACL_TABLE_STAGE, STAGE_INGRESS }, + { ACL_TABLE_PORTS, "1,2" } + } + }}); + + orch->doAclTableTask(kvfAclTable); + + // try to delete non existing acl rule + ASSERT_TRUE(orch->m_aclOrch->removeAclRule(tableId, ruleId)); + } } // namespace nsAclOrchTest diff --git a/tests/mock_tests/bufferorch_ut.cpp b/tests/mock_tests/bufferorch_ut.cpp new file mode 100644 index 0000000000..845fe77d68 --- /dev/null +++ b/tests/mock_tests/bufferorch_ut.cpp @@ -0,0 +1,409 @@ +#define private public // make Directory::m_values available to clean it. +#include "directory.h" +#undef private +#define protected public +#include "orch.h" +#undef protected +#include "ut_helper.h" +#include "mock_orchagent_main.h" +#include "mock_table.h" + +extern string gMySwitchType; + + +namespace bufferorch_test +{ + using namespace std; + + shared_ptr m_app_db; + shared_ptr m_config_db; + shared_ptr m_state_db; + shared_ptr m_chassis_app_db; + + struct BufferOrchTest : public ::testing::Test + { + BufferOrchTest() + { + } + + void CheckDependency(const string &referencingTableName, const string &referencingObjectName, const string &field, const string &dependentTableName, const string &dependentObjectNames="") + { + auto &bufferTypeMaps = BufferOrch::m_buffer_type_maps; + auto &referencingTable = (*bufferTypeMaps[referencingTableName]); + auto &dependentTable = (*bufferTypeMaps[dependentTableName]); + + if (dependentObjectNames.empty()) + { + ASSERT_TRUE(referencingTable[referencingObjectName].m_objsReferencingByMe[field].empty()); + } + else + { + auto objects = tokenize(dependentObjectNames, ','); + string reference; + for (auto &object : objects) + { + reference += dependentTableName + ":" + object + ","; + ASSERT_EQ(dependentTable[object].m_objsDependingOnMe.count(referencingObjectName), 1); + } + //reference.pop(); + ASSERT_EQ(referencingTable[referencingObjectName].m_objsReferencingByMe[field] + ",", reference); + } + } + + void RemoveItem(const string &table, const string &key) + { + std::deque entries; + entries.push_back({key, "DEL", {}}); + auto consumer = dynamic_cast(gBufferOrch->getExecutor(table)); + consumer->addToSync(entries); + } + + void SetUp() override + { + ASSERT_EQ(sai_route_api, nullptr); + map profile = { + { "SAI_VS_SWITCH_TYPE", "SAI_VS_SWITCH_TYPE_BCM56850" }, + { "KV_DEVICE_MAC_ADDRESS", "20:03:04:05:06:00" } + }; + + ut_helper::initSaiApi(profile); + + // Init switch and create dependencies + m_app_db = make_shared("APPL_DB", 0); + m_config_db = make_shared("CONFIG_DB", 0); + m_state_db = make_shared("STATE_DB", 0); + if(gMySwitchType == "voq") + m_chassis_app_db = make_shared("CHASSIS_APP_DB", 0); + + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + + auto status = sai_switch_api->create_switch(&gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // Get switch source MAC address + attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gMacAddress = attr.value.mac; + + // Get the default virtual router ID + attr.id = SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID; + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gVirtualRouterId = attr.value.oid; + + ASSERT_EQ(gCrmOrch, nullptr); + gCrmOrch = new CrmOrch(m_config_db.get(), CFG_CRM_TABLE_NAME); + + TableConnector stateDbSwitchTable(m_state_db.get(), "SWITCH_CAPABILITY"); + TableConnector conf_asic_sensors(m_config_db.get(), CFG_ASIC_SENSORS_TABLE_NAME); + TableConnector app_switch_table(m_app_db.get(), APP_SWITCH_TABLE_NAME); + + vector switch_tables = { + conf_asic_sensors, + app_switch_table + }; + + ASSERT_EQ(gSwitchOrch, nullptr); + gSwitchOrch = new SwitchOrch(m_app_db.get(), switch_tables, stateDbSwitchTable); + + // 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 } + }; + + vector flex_counter_tables = { + CFG_FLEX_COUNTER_TABLE_NAME + }; + auto* flexCounterOrch = new FlexCounterOrch(m_config_db.get(), flex_counter_tables); + gDirectory.set(flexCounterOrch); + + ASSERT_EQ(gPortsOrch, nullptr); + gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + + ASSERT_EQ(gVrfOrch, nullptr); + gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); + + ASSERT_EQ(gIntfsOrch, nullptr); + gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); + + const int fdborch_pri = 20; + + vector app_fdb_tables = { + { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_MCLAG_FDB_TABLE_NAME, fdborch_pri} + }; + + TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); + TableConnector stateMclagDbFdb(m_state_db.get(), STATE_MCLAG_REMOTE_FDB_TABLE_NAME); + ASSERT_EQ(gFdbOrch, nullptr); + gFdbOrch = new FdbOrch(m_app_db.get(), app_fdb_tables, stateDbFdb, stateMclagDbFdb, gPortsOrch); + + ASSERT_EQ(gNeighOrch, nullptr); + gNeighOrch = new NeighOrch(m_app_db.get(), APP_NEIGH_TABLE_NAME, gIntfsOrch, gFdbOrch, gPortsOrch, m_chassis_app_db.get()); + + vector qos_tables = { + CFG_TC_TO_QUEUE_MAP_TABLE_NAME, + CFG_SCHEDULER_TABLE_NAME, + CFG_DSCP_TO_TC_MAP_TABLE_NAME, + CFG_MPLS_TC_TO_TC_MAP_TABLE_NAME, + CFG_DOT1P_TO_TC_MAP_TABLE_NAME, + CFG_QUEUE_TABLE_NAME, + CFG_PORT_QOS_MAP_TABLE_NAME, + CFG_WRED_PROFILE_TABLE_NAME, + CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, + CFG_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_TABLE_NAME, + CFG_PFC_PRIORITY_TO_QUEUE_MAP_TABLE_NAME, + CFG_DSCP_TO_FC_MAP_TABLE_NAME, + CFG_EXP_TO_FC_MAP_TABLE_NAME + }; + gQosOrch = new QosOrch(m_config_db.get(), qos_tables); + + // 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, + APP_BUFFER_PG_TABLE_NAME, + APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, + APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; + + gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); + + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + // 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()) } }); + gPortsOrch->addExistingData(&portTable); + static_cast(gPortsOrch)->doTask(); + + portTable.set("PortInitDone", { { "lanes", "0" } }); + gPortsOrch->addExistingData(&portTable); + static_cast(gPortsOrch)->doTask(); + + Table bufferPoolTable = Table(m_app_db.get(), APP_BUFFER_POOL_TABLE_NAME); + Table bufferProfileTable = Table(m_app_db.get(), APP_BUFFER_PROFILE_TABLE_NAME); + + bufferPoolTable.set("ingress_lossless_pool", + { + {"size", "1024000"}, + {"mode", "dynamic"}, + {"type", "egress"} + }); + bufferPoolTable.set("ingress_lossy_pool", + { + {"size", "1024000"}, + {"mode", "dynamic"}, + {"type", "egress"} + }); + bufferProfileTable.set("ingress_lossless_profile", + { + {"pool", "ingress_lossless_pool"}, + {"size", "0"}, + {"dynamic_th", "0"} + }); + bufferProfileTable.set("ingress_lossy_profile", + { + {"pool", "ingress_lossy_pool"}, + {"size", "0"}, + {"dynamic_th", "0"} + }); + + gBufferOrch->addExistingData(&bufferPoolTable); + gBufferOrch->addExistingData(&bufferProfileTable); + + static_cast(gBufferOrch)->doTask(); + } + + void TearDown() override + { + auto buffer_maps = BufferOrch::m_buffer_type_maps; + for (auto &i : buffer_maps) + { + i.second->clear(); + } + + gDirectory.m_values.clear(); + + delete gCrmOrch; + gCrmOrch = nullptr; + + delete gSwitchOrch; + gSwitchOrch = nullptr; + + delete gVrfOrch; + gVrfOrch = nullptr; + + delete gIntfsOrch; + gIntfsOrch = nullptr; + + delete gNeighOrch; + gNeighOrch = nullptr; + + delete gFdbOrch; + gFdbOrch = nullptr; + + delete gPortsOrch; + gPortsOrch = nullptr; + + delete gQosOrch; + gQosOrch = nullptr; + + ut_helper::uninitSaiApi(); + } + }; + + TEST_F(BufferOrchTest, BufferOrchTestBufferPgReferencingObjRemoveThenAdd) + { + vector ts; + std::deque entries; + Table bufferPgTable = Table(m_app_db.get(), APP_BUFFER_PG_TABLE_NAME); + + bufferPgTable.set("Ethernet0:0", + { + {"profile", "ingress_lossy_profile"} + }); + gBufferOrch->addExistingData(&bufferPgTable); + static_cast(gBufferOrch)->doTask(); + CheckDependency(APP_BUFFER_PG_TABLE_NAME, "Ethernet0:0", "profile", APP_BUFFER_PROFILE_TABLE_NAME, "ingress_lossy_profile"); + + // Remove referenced obj + entries.push_back({"ingress_lossy_profile", "DEL", {}}); + auto bufferProfileConsumer = dynamic_cast(gBufferOrch->getExecutor(APP_BUFFER_PROFILE_TABLE_NAME)); + bufferProfileConsumer->addToSync(entries); + entries.clear(); + // Drain BUFFER_PROFILE_TABLE + static_cast(gBufferOrch)->doTask(); + // Make sure the dependency remains + CheckDependency(APP_BUFFER_PG_TABLE_NAME, "Ethernet0:0", "profile", APP_BUFFER_PROFILE_TABLE_NAME, "ingress_lossy_profile"); + // Make sure the notification isn't drained + static_cast(gBufferOrch)->dumpPendingTasks(ts); + ASSERT_EQ(ts.size(), 1); + ASSERT_EQ(ts[0], "BUFFER_PROFILE_TABLE:ingress_lossy_profile|DEL"); + ts.clear(); + + // Remove and readd referencing obj + entries.push_back({"Ethernet0:0", "DEL", {}}); + entries.push_back({"Ethernet0:0", "SET", + { + {"profile", "ingress_lossy_profile"} + }}); + auto bufferPgConsumer = dynamic_cast(gBufferOrch->getExecutor(APP_BUFFER_PG_TABLE_NAME)); + bufferPgConsumer->addToSync(entries); + entries.clear(); + // Drain the BUFFER_PG_TABLE + static_cast(gBufferOrch)->doTask(); + // Drain the BUFFER_PROFILE_TABLE which contains items need to retry + static_cast(gBufferOrch)->doTask(); + // The dependency should be removed + CheckDependency(APP_BUFFER_PG_TABLE_NAME, "Ethernet0:0", "profile", APP_BUFFER_PROFILE_TABLE_NAME); + static_cast(gBufferOrch)->dumpPendingTasks(ts); + ASSERT_EQ(ts.size(), 1); + ASSERT_EQ(ts[0], "BUFFER_PG_TABLE:Ethernet0:0|SET|profile:ingress_lossy_profile"); + ts.clear(); + + // Re-create referenced obj + entries.push_back({"ingress_lossy_profile", "SET", + { + {"pool", "ingress_lossy_pool"}, + {"size", "0"}, + {"dynamic_th", "0"} + }}); + bufferProfileConsumer->addToSync(entries); + entries.clear(); + // Drain BUFFER_PROFILE_TABLE table + static_cast(gBufferOrch)->doTask(); + // Make sure the dependency recovers + CheckDependency(APP_BUFFER_PG_TABLE_NAME, "Ethernet0:0", "profile", APP_BUFFER_PROFILE_TABLE_NAME, "ingress_lossy_profile"); + + // All items have been drained + static_cast(gBufferOrch)->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + } + + TEST_F(BufferOrchTest, BufferOrchTestReferencingObjRemoveThenAdd) + { + vector ts; + std::deque entries; + Table bufferProfileListTable = Table(m_app_db.get(), APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME); + bufferProfileListTable.set("Ethernet0", + { + {"profile_list", "ingress_lossy_profile,ingress_lossless_profile"} + }); + gBufferOrch->addExistingData(&bufferProfileListTable); + static_cast(gBufferOrch)->doTask(); + CheckDependency(APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, "Ethernet0", "profile_list", + APP_BUFFER_PROFILE_TABLE_NAME, "ingress_lossy_profile,ingress_lossless_profile"); + + // Remove and recreate the referenced profile + entries.push_back({"ingress_lossy_profile", "DEL", {}}); + entries.push_back({"ingress_lossy_profile", "SET", + { + {"pool", "ingress_lossy_pool"}, + {"size", "0"}, + {"dynamic_th", "0"} + }}); + auto consumer = dynamic_cast(gBufferOrch->getExecutor(APP_BUFFER_PROFILE_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + // Drain BUFFER_PROFILE_TABLE table + static_cast(gBufferOrch)->doTask(); + // Make sure the dependency recovers + CheckDependency(APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, "Ethernet0", "profile_list", + APP_BUFFER_PROFILE_TABLE_NAME, "ingress_lossy_profile,ingress_lossless_profile"); + // Make sure the notification isn't drained + static_cast(gBufferOrch)->dumpPendingTasks(ts); + ASSERT_EQ(ts.size(), 2); + ASSERT_EQ(ts[0], "BUFFER_PROFILE_TABLE:ingress_lossy_profile|DEL"); + ASSERT_EQ(ts[1], "BUFFER_PROFILE_TABLE:ingress_lossy_profile|SET|pool:ingress_lossy_pool|size:0|dynamic_th:0"); + ts.clear(); + + // Remove and recreate the referenced pool + entries.push_back({"ingress_lossy_pool", "DEL", {}}); + entries.push_back({"ingress_lossy_pool", "SET", + { + {"type", "ingress"}, + {"size", "1024000"}, + {"mode", "dynamic"} + }}); + consumer = dynamic_cast(gBufferOrch->getExecutor(APP_BUFFER_POOL_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + // Drain BUFFER_POOL_TABLE table + static_cast(gBufferOrch)->doTask(); + // Make sure the dependency recovers + CheckDependency(APP_BUFFER_PROFILE_TABLE_NAME, "ingress_lossy_profile", "pool", + APP_BUFFER_POOL_TABLE_NAME, "ingress_lossy_pool"); + // Make sure the notification isn't drained + static_cast(gBufferOrch)->dumpPendingTasks(ts); + ASSERT_EQ(ts.size(), 4); + ASSERT_EQ(ts[0], "BUFFER_POOL_TABLE:ingress_lossy_pool|DEL"); + ASSERT_EQ(ts[1], "BUFFER_POOL_TABLE:ingress_lossy_pool|SET|type:ingress|size:1024000|mode:dynamic"); + ASSERT_EQ(ts[2], "BUFFER_PROFILE_TABLE:ingress_lossy_profile|DEL"); + ASSERT_EQ(ts[3], "BUFFER_PROFILE_TABLE:ingress_lossy_profile|SET|pool:ingress_lossy_pool|size:0|dynamic_th:0"); + ts.clear(); + } +} diff --git a/tests/mock_tests/copporch_ut.cpp b/tests/mock_tests/copporch_ut.cpp new file mode 100644 index 0000000000..fa7c360f01 --- /dev/null +++ b/tests/mock_tests/copporch_ut.cpp @@ -0,0 +1,505 @@ +#include +#include +#include +#include +#include + +#include "ut_helper.h" +#include "mock_orchagent_main.h" + +using namespace swss; + +namespace copporch_test +{ + class MockCoppOrch final + { + public: + MockCoppOrch() + { + this->appDb = std::make_shared("APPL_DB", 0); + this->coppOrch = std::make_shared(this->appDb.get(), APP_COPP_TABLE_NAME); + } + ~MockCoppOrch() = default; + + void doCoppTableTask(const std::deque &entries) + { + // ConsumerStateTable is used for APP DB + auto consumer = std::unique_ptr(new Consumer( + new ConsumerStateTable(this->appDb.get(), APP_COPP_TABLE_NAME, 1, 1), + this->coppOrch.get(), APP_COPP_TABLE_NAME + )); + + consumer->addToSync(entries); + static_cast(this->coppOrch.get())->doTask(*consumer); + } + + CoppOrch& get() + { + return *coppOrch; + } + + private: + std::shared_ptr coppOrch; + std::shared_ptr appDb; + }; + + class CoppOrchTest : public ::testing::Test + { + public: + CoppOrchTest() + { + this->initDb(); + } + virtual ~CoppOrchTest() = default; + + void SetUp() override + { + this->initSaiApi(); + this->initSwitch(); + this->initOrch(); + this->initPorts(); + } + + void TearDown() override + { + this->deinitOrch(); + this->deinitSwitch(); + this->deinitSaiApi(); + } + + private: + void initSaiApi() + { + std::map profileMap = { + { "SAI_VS_SWITCH_TYPE", "SAI_VS_SWITCH_TYPE_BCM56850" }, + { "KV_DEVICE_MAC_ADDRESS", "20:03:04:05:06:00" } + }; + auto status = ut_helper::initSaiApi(profileMap); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + } + + void deinitSaiApi() + { + auto status = ut_helper::uninitSaiApi(); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + } + + void initSwitch() + { + sai_status_t status; + sai_attribute_t attr; + + // Create switch + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + + status = sai_switch_api->create_switch(&gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // Get switch source MAC address + attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; + + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gMacAddress = attr.value.mac; + + // Get switch default virtual router ID + attr.id = SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID; + + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gVirtualRouterId = attr.value.oid; + } + + void deinitSwitch() + { + // Remove switch + auto status = sai_switch_api->remove_switch(gSwitchId); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gSwitchId = SAI_NULL_OBJECT_ID; + gVirtualRouterId = SAI_NULL_OBJECT_ID; + } + + void initOrch() + { + // + // SwitchOrch + // + + TableConnector switchCapTableStateDb(this->stateDb.get(), "SWITCH_CAPABILITY"); + TableConnector asicSensorsTableCfgDb(this->configDb.get(), CFG_ASIC_SENSORS_TABLE_NAME); + TableConnector switchTableAppDb(this->appDb.get(), APP_SWITCH_TABLE_NAME); + + std::vector switchTableList = { + asicSensorsTableCfgDb, + switchTableAppDb + }; + + gSwitchOrch = new SwitchOrch(this->appDb.get(), switchTableList, switchCapTableStateDb); + gDirectory.set(gSwitchOrch); + resourcesList.push_back(gSwitchOrch); + + // + // PortsOrch + // + + const int portsorchBasePri = 40; + + std::vector portTableList = { + { APP_PORT_TABLE_NAME, portsorchBasePri + 5 }, + { APP_VLAN_TABLE_NAME, portsorchBasePri + 2 }, + { APP_VLAN_MEMBER_TABLE_NAME, portsorchBasePri }, + { APP_LAG_TABLE_NAME, portsorchBasePri + 4 }, + { APP_LAG_MEMBER_TABLE_NAME, portsorchBasePri } + }; + + gPortsOrch = new PortsOrch(this->appDb.get(), this->stateDb.get(), portTableList, this->chassisAppDb.get()); + gDirectory.set(gPortsOrch); + resourcesList.push_back(gPortsOrch); + + // + // QosOrch + // + + std::vector qosTableList = { + CFG_TC_TO_QUEUE_MAP_TABLE_NAME, + CFG_SCHEDULER_TABLE_NAME, + CFG_DSCP_TO_TC_MAP_TABLE_NAME, + CFG_MPLS_TC_TO_TC_MAP_TABLE_NAME, + CFG_DOT1P_TO_TC_MAP_TABLE_NAME, + CFG_QUEUE_TABLE_NAME, + CFG_PORT_QOS_MAP_TABLE_NAME, + CFG_WRED_PROFILE_TABLE_NAME, + CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, + CFG_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_TABLE_NAME, + CFG_PFC_PRIORITY_TO_QUEUE_MAP_TABLE_NAME, + CFG_DSCP_TO_FC_MAP_TABLE_NAME, + CFG_EXP_TO_FC_MAP_TABLE_NAME + }; + gQosOrch = new QosOrch(this->configDb.get(), qosTableList); + gDirectory.set(gQosOrch); + resourcesList.push_back(gQosOrch); + + // + // BufferOrch + // + + std::vector bufferTableList = { + 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 + }; + gBufferOrch = new BufferOrch(this->appDb.get(), this->configDb.get(), this->stateDb.get(), bufferTableList); + gDirectory.set(gBufferOrch); + resourcesList.push_back(gBufferOrch); + + // + // PolicerOrch + // + + vector policer_tables = { + TableConnector(this->configDb.get(), CFG_POLICER_TABLE_NAME), + TableConnector(this->configDb.get(), CFG_PORT_STORM_CONTROL_TABLE_NAME) + }; + auto policerOrch = new PolicerOrch(policer_tables, gPortsOrch); + gDirectory.set(policerOrch); + resourcesList.push_back(policerOrch); + + // + // FlexCounterOrch + // + + std::vector flexCounterTableList = { + CFG_FLEX_COUNTER_TABLE_NAME + }; + + auto flexCounterOrch = new FlexCounterOrch(this->configDb.get(), flexCounterTableList); + gDirectory.set(flexCounterOrch); + resourcesList.push_back(flexCounterOrch); + } + + void deinitOrch() + { + std::reverse(this->resourcesList.begin(), this->resourcesList.end()); + for (auto &it : this->resourcesList) + { + delete it; + } + + gSwitchOrch = nullptr; + gPortsOrch = nullptr; + gQosOrch = nullptr; + gBufferOrch = nullptr; + + Portal::DirectoryInternal::clear(gDirectory); + EXPECT_TRUE(Portal::DirectoryInternal::empty(gDirectory)); + } + + void initPorts() + { + auto portTable = Table(this->appDb.get(), APP_PORT_TABLE_NAME); + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + // Populate port table with SAI ports + for (const auto &cit : ports) + { + portTable.set(cit.first, cit.second); + } + + // Set PortConfigDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + gPortsOrch->addExistingData(&portTable); + static_cast(gPortsOrch)->doTask(); + + // Set PortInitDone + portTable.set("PortInitDone", { { "lanes", "0" } }); + gPortsOrch->addExistingData(&portTable); + static_cast(gPortsOrch)->doTask(); + } + + void initDb() + { + this->appDb = std::make_shared("APPL_DB", 0); + this->configDb = std::make_shared("CONFIG_DB", 0); + this->stateDb = std::make_shared("STATE_DB", 0); + this->chassisAppDb = std::make_shared("CHASSIS_APP_DB", 0); + } + + std::shared_ptr appDb; + std::shared_ptr configDb; + std::shared_ptr stateDb; + std::shared_ptr chassisAppDb; + + std::vector resourcesList; + }; + + TEST_F(CoppOrchTest, TrapGroup_AddRemove) + { + const std::string trapGroupName = "queue4_group1"; + + MockCoppOrch coppOrch; + + // Create CoPP Trap Group + { + auto tableKofvt = std::deque( + { + { + trapGroupName, + SET_COMMAND, + { + { copp_trap_action_field, "trap" }, + { copp_trap_priority_field, "4" }, + { copp_queue_field, "4" } + } + } + } + ); + coppOrch.doCoppTableTask(tableKofvt); + + const auto &trapGroupMap = coppOrch.get().getTrapGroupMap(); + const auto &cit = trapGroupMap.find(trapGroupName); + EXPECT_TRUE(cit != trapGroupMap.end()); + } + + // Delete CoPP Trap Group + { + auto tableKofvt = std::deque( + { { trapGroupName, DEL_COMMAND, { } } } + ); + coppOrch.doCoppTableTask(tableKofvt); + + const auto &trapGroupMap = coppOrch.get().getTrapGroupMap(); + const auto &cit = trapGroupMap.find(trapGroupName); + EXPECT_TRUE(cit == trapGroupMap.end()); + } + } + + TEST_F(CoppOrchTest, TrapGroupWithPolicer_AddRemove) + { + const std::string trapGroupName = "queue4_group2"; + + MockCoppOrch coppOrch; + + // Create CoPP Trap Group + { + auto tableKofvt = std::deque( + { + { + trapGroupName, + SET_COMMAND, + { + { copp_trap_action_field, "copy" }, + { copp_trap_priority_field, "4" }, + { copp_queue_field, "4" }, + { copp_policer_meter_type_field, "packets" }, + { copp_policer_mode_field, "sr_tcm" }, + { copp_policer_cir_field, "600" }, + { copp_policer_cbs_field, "600" }, + { copp_policer_action_red_field, "drop" } + } + } + } + ); + coppOrch.doCoppTableTask(tableKofvt); + + const auto &trapGroupMap = coppOrch.get().getTrapGroupMap(); + const auto &cit1 = trapGroupMap.find(trapGroupName); + EXPECT_TRUE(cit1 != trapGroupMap.end()); + + const auto &trapGroupPolicerMap = Portal::CoppOrchInternal::getTrapGroupPolicerMap(coppOrch.get()); + const auto &trapGroupOid = cit1->second; + const auto &cit2 = trapGroupPolicerMap.find(trapGroupOid); + EXPECT_TRUE(cit2 != trapGroupPolicerMap.end()); + } + + // Delete CoPP Trap Group + { + auto tableKofvt = std::deque( + { { trapGroupName, DEL_COMMAND, { } } } + ); + coppOrch.doCoppTableTask(tableKofvt); + + const auto &trapGroupMap = coppOrch.get().getTrapGroupMap(); + const auto &cit = trapGroupMap.find(trapGroupName); + EXPECT_TRUE(cit == trapGroupMap.end()); + + const auto &trapGroupPolicerMap = Portal::CoppOrchInternal::getTrapGroupPolicerMap(coppOrch.get()); + EXPECT_TRUE(trapGroupPolicerMap.empty()); + } + } + + TEST_F(CoppOrchTest, Trap_AddRemove) + { + const std::string trapGroupName = "queue4_group1"; + const std::string trapNameList = "bgp,bgpv6"; + const std::set trapIDSet = { + SAI_HOSTIF_TRAP_TYPE_BGP, + SAI_HOSTIF_TRAP_TYPE_BGPV6 + }; + + MockCoppOrch coppOrch; + + // Create CoPP Trap + { + auto tableKofvt = std::deque( + { + { + trapGroupName, + SET_COMMAND, + { + { copp_trap_action_field, "trap" }, + { copp_trap_priority_field, "4" }, + { copp_queue_field, "4" }, + { copp_trap_id_list, trapNameList } + } + } + } + ); + coppOrch.doCoppTableTask(tableKofvt); + + const auto &trapGroupMap = coppOrch.get().getTrapGroupMap(); + const auto &cit = trapGroupMap.find(trapGroupName); + EXPECT_TRUE(cit != trapGroupMap.end()); + + const auto &tgOid = cit->second; + const auto &tidList = Portal::CoppOrchInternal::getTrapIdsFromTrapGroup(coppOrch.get(), tgOid); + const auto &tidSet = std::set(tidList.begin(), tidList.end()); + EXPECT_TRUE(trapIDSet == tidSet); + } + + // Delete CoPP Trap + { + auto tableKofvt = std::deque( + { { trapGroupName, DEL_COMMAND, { } } } + ); + coppOrch.doCoppTableTask(tableKofvt); + + const auto &trapGroupMap = coppOrch.get().getTrapGroupMap(); + const auto &cit1 = trapGroupMap.find(trapGroupName); + EXPECT_TRUE(cit1 == trapGroupMap.end()); + + const auto &trapGroupIdMap = Portal::CoppOrchInternal::getTrapGroupIdMap(coppOrch.get()); + const auto &cit2 = trapGroupIdMap.find(SAI_HOSTIF_TRAP_TYPE_TTL_ERROR); + EXPECT_TRUE(cit2 != trapGroupIdMap.end()); + ASSERT_EQ(trapGroupIdMap.size(), 1); + } + } + + TEST_F(CoppOrchTest, TrapWithPolicer_AddRemove) + { + const std::string trapGroupName = "queue4_group2"; + const std::string trapNameList = "arp_req,arp_resp,neigh_discovery"; + const std::set trapIDSet = { + SAI_HOSTIF_TRAP_TYPE_ARP_REQUEST, + SAI_HOSTIF_TRAP_TYPE_ARP_RESPONSE, + SAI_HOSTIF_TRAP_TYPE_IPV6_NEIGHBOR_DISCOVERY + }; + + MockCoppOrch coppOrch; + + // Create CoPP Trap + { + auto tableKofvt = std::deque( + { + { + trapGroupName, + SET_COMMAND, + { + { copp_trap_action_field, "copy" }, + { copp_trap_priority_field, "4" }, + { copp_queue_field, "4" }, + { copp_policer_meter_type_field, "packets" }, + { copp_policer_mode_field, "sr_tcm" }, + { copp_policer_cir_field, "600" }, + { copp_policer_cbs_field, "600" }, + { copp_policer_action_red_field, "drop" }, + { copp_trap_id_list, trapNameList } + } + } + } + ); + coppOrch.doCoppTableTask(tableKofvt); + + const auto &trapGroupMap = coppOrch.get().getTrapGroupMap(); + const auto &cit1 = trapGroupMap.find(trapGroupName); + EXPECT_TRUE(cit1 != trapGroupMap.end()); + + const auto &trapGroupPolicerMap = Portal::CoppOrchInternal::getTrapGroupPolicerMap(coppOrch.get()); + const auto &trapGroupOid = cit1->second; + const auto &cit2 = trapGroupPolicerMap.find(trapGroupOid); + EXPECT_TRUE(cit2 != trapGroupPolicerMap.end()); + + const auto &tidList = Portal::CoppOrchInternal::getTrapIdsFromTrapGroup(coppOrch.get(), trapGroupOid); + const auto &tidSet = std::set(tidList.begin(), tidList.end()); + EXPECT_TRUE(trapIDSet == tidSet); + } + + // Delete CoPP Trap + { + auto tableKofvt = std::deque( + { { trapGroupName, DEL_COMMAND, { } } } + ); + coppOrch.doCoppTableTask(tableKofvt); + + const auto &trapGroupMap = coppOrch.get().getTrapGroupMap(); + const auto &cit1 = trapGroupMap.find(trapGroupName); + EXPECT_TRUE(cit1 == trapGroupMap.end()); + + const auto &trapGroupPolicerMap = Portal::CoppOrchInternal::getTrapGroupPolicerMap(coppOrch.get()); + EXPECT_TRUE(trapGroupPolicerMap.empty()); + + const auto &trapGroupIdMap = Portal::CoppOrchInternal::getTrapGroupIdMap(coppOrch.get()); + const auto &cit2 = trapGroupIdMap.find(SAI_HOSTIF_TRAP_TYPE_TTL_ERROR); + EXPECT_TRUE(cit2 != trapGroupIdMap.end()); + ASSERT_EQ(trapGroupIdMap.size(), 1); + } + } +} diff --git a/tests/mock_tests/fdborch/flush_syncd_notif_ut.cpp b/tests/mock_tests/fdborch/flush_syncd_notif_ut.cpp new file mode 100644 index 0000000000..7bf22373c1 --- /dev/null +++ b/tests/mock_tests/fdborch/flush_syncd_notif_ut.cpp @@ -0,0 +1,424 @@ +#include "../ut_helper.h" +#include "../mock_orchagent_main.h" +#include "../mock_table.h" +#include "port.h" +#define private public // Need to modify internal cache +#include "portsorch.h" +#include "fdborch.h" +#include "crmorch.h" +#undef private + +#define ETH0 "Ethernet0" +#define VLAN40 "Vlan40" + +extern redisReply *mockReply; +extern CrmOrch* gCrmOrch; + +/* +Test Fixture +*/ +namespace fdb_syncd_flush_test +{ + struct FdbOrchTest : public ::testing::Test + { + std::shared_ptr m_config_db; + std::shared_ptr m_app_db; + std::shared_ptr m_state_db; + std::shared_ptr m_asic_db; + std::shared_ptr m_chassis_app_db; + std::shared_ptr m_portsOrch; + std::shared_ptr m_fdborch; + + virtual void SetUp() override + { + + testing_db::reset(); + + map profile = { + { "SAI_VS_SWITCH_TYPE", "SAI_VS_SWITCH_TYPE_BCM56850" }, + { "KV_DEVICE_MAC_ADDRESS", "20:03:04:05:06:00" } + }; + + ut_helper::initSaiApi(profile); + + /* Create Switch */ + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + auto status = sai_switch_api->create_switch(&gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + m_config_db = std::make_shared("CONFIG_DB", 0); + m_app_db = std::make_shared("APPL_DB", 0); + m_state_db = make_shared("STATE_DB", 0); + m_asic_db = std::make_shared("ASIC_DB", 0); + + // Construct dependencies + // 1) Portsorch + 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 } + }; + + m_portsOrch = std::make_shared(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + + // 2) Crmorch + ASSERT_EQ(gCrmOrch, nullptr); + gCrmOrch = new CrmOrch(m_config_db.get(), CFG_CRM_TABLE_NAME); + + // Construct fdborch + vector app_fdb_tables = { + { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_MCLAG_FDB_TABLE_NAME, FdbOrch::fdborch_pri} + }; + + TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); + TableConnector stateMclagDbFdb(m_state_db.get(), STATE_MCLAG_REMOTE_FDB_TABLE_NAME); + + m_fdborch = std::make_shared(m_app_db.get(), + app_fdb_tables, + stateDbFdb, + stateMclagDbFdb, + m_portsOrch.get()); + } + + virtual void TearDown() override { + delete gCrmOrch; + gCrmOrch = nullptr; + + ut_helper::uninitSaiApi(); + } + }; + + /* Helper Methods */ + void setUpVlan(PortsOrch* m_portsOrch){ + /* Updates portsOrch internal cache for Vlan40 */ + std::string alias = VLAN40; + sai_object_id_t oid = 0x26000000000796; + + Port vlan(alias, Port::VLAN); + vlan.m_vlan_info.vlan_oid = oid; + vlan.m_vlan_info.vlan_id = 40; + vlan.m_members = set(); + + m_portsOrch->m_portList[alias] = vlan; + m_portsOrch->m_port_ref_count[alias] = 0; + m_portsOrch->saiOidToAlias[oid] = alias; + } + + void setUpPort(PortsOrch* m_portsOrch){ + /* Updates portsOrch internal cache for Ethernet0 */ + std::string alias = ETH0; + sai_object_id_t oid = 0x10000000004a4; + + Port port(alias, Port::PHY); + port.m_index = 1; + port.m_port_id = oid; + port.m_hif_id = 0xd00000000056e; + + m_portsOrch->m_portList[alias] = port; + m_portsOrch->saiOidToAlias[oid] = alias; + } + + void setUpVlanMember(PortsOrch* m_portsOrch){ + /* Updates portsOrch internal cache for adding Ethernet0 into Vlan40 */ + sai_object_id_t bridge_port_id = 0x3a000000002c33; + + /* Add Bridge Port */ + m_portsOrch->m_portList[ETH0].m_bridge_port_id = bridge_port_id; + m_portsOrch->saiOidToAlias[bridge_port_id] = ETH0; + m_portsOrch->m_portList[VLAN40].m_members.insert(ETH0); + } + + void triggerUpdate(FdbOrch* m_fdborch, + sai_fdb_event_t type, + vector mac_addr, + sai_object_id_t bridge_port_id, + sai_object_id_t bv_id){ + sai_fdb_entry_t entry; + for (int i = 0; i < (int)mac_addr.size(); i++){ + *(entry.mac_address+i) = mac_addr[i]; + } + entry.bv_id = bv_id; + m_fdborch->update(type, &entry, bridge_port_id); + } +} + +namespace fdb_syncd_flush_test +{ + /* Test Consolidated Flush Per Vlan and Per Port */ + TEST_F(FdbOrchTest, ConsolidatedFlushVlanandPort) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + /* Event 2: Generate a FDB Flush per port and per vlan */ + vector flush_mac_addr = {0, 0, 0, 0, 0, 0}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + /* make sure fdb_counters are decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } + + /* Test Consolidated Flush All */ + TEST_F(FdbOrchTest, ConsolidatedFlushAll) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + /* Event2: Send a Consolidated Flush response from syncd */ + vector flush_mac_addr = {0, 0, 0, 0, 0, 0}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, SAI_NULL_OBJECT_ID, + SAI_NULL_OBJECT_ID); + + /* make sure fdb_counters are decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } + + /* Test Consolidated Flush per VLAN BV_ID */ + TEST_F(FdbOrchTest, ConsolidatedFlushVlan) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + /* Event2: Send a Consolidated Flush response from syncd for vlan */ + vector flush_mac_addr = {0, 0, 0, 0, 0, 0}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, SAI_NULL_OBJECT_ID, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + /* make sure fdb_counters are decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } + + /* Test Consolidated Flush per bridge port id */ + TEST_F(FdbOrchTest, ConsolidatedFlushPort) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + /* Event2: Send a Consolidated Flush response from syncd for a port */ + vector flush_mac_addr = {0, 0, 0, 0, 0, 0}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + SAI_NULL_OBJECT_ID); + + /* make sure fdb_counters are decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } + + //* Test Consolidated Flush Per Vlan and Per Port, but the bridge_port_id from the internal cache is already deleted */ + TEST_F(FdbOrchTest, ConsolidatedFlushVlanandPortBridgeportDeleted) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + auto bridge_port_oid = m_portsOrch->m_portList[ETH0].m_bridge_port_id; + + /* Delete the bridge_port_oid in the internal OA cache */ + m_portsOrch->m_portList[ETH0].m_bridge_port_id = SAI_NULL_OBJECT_ID; + m_portsOrch->saiOidToAlias.erase(bridge_port_oid); + + /* Event 2: Generate a FDB Flush per port and per vlan */ + vector flush_mac_addr = {0, 0, 0, 0, 0, 0}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, bridge_port_oid, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + /* make sure fdb_counter for Vlan is decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } + + /* Test Flush Per Vlan and Per Port */ + TEST_F(FdbOrchTest, NonConsolidatedFlushVlanandPort) + { + ASSERT_NE(m_portsOrch, nullptr); + setUpVlan(m_portsOrch.get()); + setUpPort(m_portsOrch.get()); + ASSERT_NE(m_portsOrch->m_portList.find(VLAN40), m_portsOrch->m_portList.end()); + ASSERT_NE(m_portsOrch->m_portList.find(ETH0), m_portsOrch->m_portList.end()); + setUpVlanMember(m_portsOrch.get()); + + /* Event 1: Learn a dynamic FDB Entry */ + // 7c:fe:90:12:22:ec + vector mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_LEARNED, mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + string port; + string entry_type; + + /* Make sure fdb_count is incremented as expected */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 1); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 1); + + /* Make sure state db is updated as expected */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), true); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), true); + + ASSERT_EQ(port, "Ethernet0"); + ASSERT_EQ(entry_type, "dynamic"); + + /* Event 2: Generate a non-consilidated FDB Flush per port and per vlan */ + vector flush_mac_addr = {124, 254, 144, 18, 34, 236}; + triggerUpdate(m_fdborch.get(), SAI_FDB_EVENT_FLUSHED, flush_mac_addr, m_portsOrch->m_portList[ETH0].m_bridge_port_id, + m_portsOrch->m_portList[VLAN40].m_vlan_info.vlan_oid); + + /* make sure fdb_counters are decremented */ + ASSERT_EQ(m_portsOrch->m_portList[VLAN40].m_fdb_count, 0); + ASSERT_EQ(m_portsOrch->m_portList[ETH0].m_fdb_count, 0); + + /* Make sure state db is cleared */ + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "port", port), false); + ASSERT_EQ(m_fdborch->m_fdbStateTable.hget("Vlan40:7c:fe:90:12:22:ec", "type", entry_type), false); + } +} diff --git a/tests/mock_tests/flowcounterrouteorch_ut.cpp b/tests/mock_tests/flowcounterrouteorch_ut.cpp new file mode 100644 index 0000000000..25ed95cb1e --- /dev/null +++ b/tests/mock_tests/flowcounterrouteorch_ut.cpp @@ -0,0 +1,361 @@ +#define private public +#include "directory.h" +#undef private +#define protected public +#include "orch.h" +#undef protected +#include "ut_helper.h" +#include "mock_orchagent_main.h" +#include "mock_table.h" + +extern string gMySwitchType; + +namespace flowcounterrouteorch_test +{ + using namespace std; + shared_ptr m_app_db; + shared_ptr m_config_db; + shared_ptr m_state_db; + shared_ptr m_chassis_app_db; + + int num_created_counter; + sai_counter_api_t ut_sai_counter_api; + sai_counter_api_t *pold_sai_counter_api; + sai_create_counter_fn old_create_counter; + sai_remove_counter_fn old_remove_counter; + + sai_status_t _ut_stub_create_counter( + _Out_ sai_object_id_t *counter_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) + { + num_created_counter ++; + return old_create_counter(counter_id, switch_id, attr_count, attr_list); + } + + sai_status_t _ut_stub_remove_counter(_In_ sai_object_id_t counter_id) + { + num_created_counter --; + return old_remove_counter(counter_id); + } + + struct FlowcounterRouteOrchTest : public ::testing::Test + { + FlowcounterRouteOrchTest() + { + return; + } + + void SetUp() override + { + ASSERT_EQ(sai_route_api, nullptr); + map profile = { + { "SAI_VS_SWITCH_TYPE", "SAI_VS_SWITCH_TYPE_BCM56850" }, + { "KV_DEVICE_MAC_ADDRESS", "20:03:04:05:06:00" } + }; + + ut_helper::initSaiApi(profile); + + old_create_counter = sai_counter_api->create_counter; + old_remove_counter = sai_counter_api->remove_counter; + + pold_sai_counter_api = sai_counter_api; + ut_sai_counter_api = *sai_counter_api; + sai_counter_api = &ut_sai_counter_api; + + // Mock sai API + sai_counter_api->create_counter = _ut_stub_create_counter; + sai_counter_api->remove_counter = _ut_stub_remove_counter; + + // Init switch and create dependencies + m_app_db = make_shared("APPL_DB", 0); + m_config_db = make_shared("CONFIG_DB", 0); + m_state_db = make_shared("STATE_DB", 0); + if(gMySwitchType == "voq") + m_chassis_app_db = make_shared("CHASSIS_APP_DB", 0); + + sai_attribute_t attr; + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + + auto status = sai_switch_api->create_switch(&gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // Get switch source MAC address + attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gMacAddress = attr.value.mac; + + // Get the default virtual router ID + attr.id = SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID; + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gVirtualRouterId = attr.value.oid; + + + ASSERT_EQ(gCrmOrch, nullptr); + gCrmOrch = new CrmOrch(m_config_db.get(), CFG_CRM_TABLE_NAME); + + TableConnector stateDbSwitchTable(m_state_db.get(), "SWITCH_CAPABILITY"); + TableConnector conf_asic_sensors(m_config_db.get(), CFG_ASIC_SENSORS_TABLE_NAME); + TableConnector app_switch_table(m_app_db.get(), APP_SWITCH_TABLE_NAME); + + vector switch_tables = { + conf_asic_sensors, + app_switch_table + }; + + ASSERT_EQ(gSwitchOrch, nullptr); + gSwitchOrch = new SwitchOrch(m_app_db.get(), switch_tables, stateDbSwitchTable); + + // Create dependencies ... + TableConnector stateDbBfdSessionTable(m_state_db.get(), STATE_BFD_SESSION_TABLE_NAME); + gBfdOrch = new BfdOrch(m_app_db.get(), APP_BFD_SESSION_TABLE_NAME, stateDbBfdSessionTable); + + 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 } + }; + + vector flex_counter_tables = { + CFG_FLEX_COUNTER_TABLE_NAME + }; + auto* flexCounterOrch = new FlexCounterOrch(m_config_db.get(), flex_counter_tables); + gDirectory.set(flexCounterOrch); + + ASSERT_EQ(gPortsOrch, nullptr); + gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + + vector vnet_tables = { + APP_VNET_RT_TABLE_NAME, + APP_VNET_RT_TUNNEL_TABLE_NAME + }; + + vector cfg_vnet_tables = { + CFG_VNET_RT_TABLE_NAME, + CFG_VNET_RT_TUNNEL_TABLE_NAME + }; + + auto* vnet_orch = new VNetOrch(m_app_db.get(), APP_VNET_TABLE_NAME); + gDirectory.set(vnet_orch); + auto* cfg_vnet_rt_orch = new VNetCfgRouteOrch(m_config_db.get(), m_app_db.get(), cfg_vnet_tables); + gDirectory.set(cfg_vnet_rt_orch); + auto* vnet_rt_orch = new VNetRouteOrch(m_app_db.get(), vnet_tables, vnet_orch); + gDirectory.set(vnet_rt_orch); + ASSERT_EQ(gVrfOrch, nullptr); + gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); + gDirectory.set(gVrfOrch); + + ASSERT_EQ(gIntfsOrch, nullptr); + gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); + + const int fdborch_pri = 20; + + vector app_fdb_tables = { + { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_MCLAG_FDB_TABLE_NAME, fdborch_pri} + }; + + TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); + TableConnector stateMclagDbFdb(m_state_db.get(), STATE_MCLAG_REMOTE_FDB_TABLE_NAME); + ASSERT_EQ(gFdbOrch, nullptr); + gFdbOrch = new FdbOrch(m_app_db.get(), app_fdb_tables, stateDbFdb, stateMclagDbFdb, gPortsOrch); + + ASSERT_EQ(gNeighOrch, nullptr); + gNeighOrch = new NeighOrch(m_app_db.get(), APP_NEIGH_TABLE_NAME, gIntfsOrch, gFdbOrch, gPortsOrch, m_chassis_app_db.get()); + + auto* tunnel_decap_orch = new TunnelDecapOrch(m_app_db.get(), APP_TUNNEL_DECAP_TABLE_NAME); + vector mux_tables = { + CFG_MUX_CABLE_TABLE_NAME, + CFG_PEER_SWITCH_TABLE_NAME + }; + auto* mux_orch = new MuxOrch(m_config_db.get(), mux_tables, tunnel_decap_orch, gNeighOrch, gFdbOrch); + gDirectory.set(mux_orch); + + ASSERT_EQ(gFgNhgOrch, nullptr); + const int fgnhgorch_pri = 15; + + vector fgnhg_tables = { + { CFG_FG_NHG, fgnhgorch_pri }, + { CFG_FG_NHG_PREFIX, fgnhgorch_pri }, + { CFG_FG_NHG_MEMBER, fgnhgorch_pri } + }; + gFgNhgOrch = new FgNhgOrch(m_config_db.get(), m_app_db.get(), m_state_db.get(), fgnhg_tables, gNeighOrch, gIntfsOrch, gVrfOrch); + + ASSERT_EQ(gSrv6Orch, nullptr); + vector srv6_tables = { + APP_SRV6_SID_LIST_TABLE_NAME, + APP_SRV6_MY_SID_TABLE_NAME + }; + gSrv6Orch = new Srv6Orch(m_app_db.get(), srv6_tables, gSwitchOrch, gVrfOrch, gNeighOrch); + + ASSERT_EQ(gRouteOrch, nullptr); + const int routeorch_pri = 5; + vector route_tables = { + { APP_ROUTE_TABLE_NAME, routeorch_pri }, + { APP_LABEL_ROUTE_TABLE_NAME, routeorch_pri } + }; + gRouteOrch = new RouteOrch(m_app_db.get(), route_tables, gSwitchOrch, gNeighOrch, gIntfsOrch, gVrfOrch, gFgNhgOrch, gSrv6Orch); + gNhgOrch = new NhgOrch(m_app_db.get(), APP_NEXTHOP_GROUP_TABLE_NAME); + + // 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, + APP_BUFFER_PG_TABLE_NAME, + APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, + APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; + + gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); + + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + // 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()) } }); + gPortsOrch->addExistingData(&portTable); + static_cast(gPortsOrch)->doTask(); + + portTable.set("PortInitDone", { { "lanes", "0" } }); + gPortsOrch->addExistingData(&portTable); + static_cast(gPortsOrch)->doTask(); + + // Prepare interface table + Table intfTable = Table(m_app_db.get(), APP_INTF_TABLE_NAME); + intfTable.set("Ethernet0", { {"NULL", "NULL" }, + {"mac_addr", "00:00:00:00:00:00" }}); + intfTable.set("Ethernet0:10.0.0.1/24", { { "scope", "global" }, + { "family", "IPv4" }}); + gIntfsOrch->addExistingData(&intfTable); + static_cast(gIntfsOrch)->doTask(); + + // Prepare neighbor table + Table neighborTable = Table(m_app_db.get(), APP_NEIGH_TABLE_NAME); + + map neighborIp2Mac = {{"10.0.0.2", "00:00:0a:00:00:02" }, + {"10.0.0.3", "00:00:0a:00:00:03" } }; + neighborTable.set("Ethernet0:10.0.0.2", { {"neigh", neighborIp2Mac["10.0.0.2"]}, + {"family", "IPv4" }}); + neighborTable.set("Ethernet0:10.0.0.3", { {"neigh", neighborIp2Mac["10.0.0.3"]}, + {"family", "IPv4" }}); + gNeighOrch->addExistingData(&neighborTable); + static_cast(gNeighOrch)->doTask(); + + //Prepare route table + Table routeTable = Table(m_app_db.get(), APP_ROUTE_TABLE_NAME); + routeTable.set("1.1.1.1/32", { {"ifname", "Ethernet0" }, + {"nexthop", "10.0.0.2" }}); + routeTable.set("0.0.0.0/0", { {"ifname", "Ethernet0" }, + {"nexthop", "10.0.0.2" }}); + gRouteOrch->addExistingData(&routeTable); + static_cast(gRouteOrch)->doTask(); + + // Enable flow counter + std::deque entries; + entries.push_back({"FLOW_CNT_ROUTE", "SET", { {"FLEX_COUNTER_STATUS", "enable"}, {"POLL_INTERVAL", "10000"}}}); + auto consumer = dynamic_cast(flexCounterOrch->getExecutor(CFG_FLEX_COUNTER_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(flexCounterOrch)->doTask(); + + // Start FlowCounterRouteOrch + static const vector route_pattern_tables = { + CFG_FLOW_COUNTER_ROUTE_PATTERN_TABLE_NAME, + }; + gFlowCounterRouteOrch = new FlowCounterRouteOrch(m_config_db.get(), route_pattern_tables); + + static_cast(gFlowCounterRouteOrch)->doTask(); + + return; + } + + void TearDown() override + { + gDirectory.m_values.clear(); + + delete gCrmOrch; + gCrmOrch = nullptr; + + delete gSwitchOrch; + gSwitchOrch = nullptr; + + delete gBfdOrch; + gBfdOrch = nullptr; + + delete gNeighOrch; + gNeighOrch = nullptr; + + delete gFdbOrch; + gFdbOrch = nullptr; + + delete gPortsOrch; + gPortsOrch = nullptr; + + delete gIntfsOrch; + gIntfsOrch = nullptr; + + delete gFgNhgOrch; + gFgNhgOrch = nullptr; + + delete gSrv6Orch; + gSrv6Orch = nullptr; + + delete gRouteOrch; + gRouteOrch = nullptr; + + delete gNhgOrch; + gNhgOrch = nullptr; + + delete gBufferOrch; + gBufferOrch = nullptr; + + delete gVrfOrch; + gVrfOrch = nullptr; + + delete gFlowCounterRouteOrch; + gFlowCounterRouteOrch = nullptr; + + sai_counter_api = pold_sai_counter_api; + ut_helper::uninitSaiApi(); + return; + } + }; + + TEST_F(FlowcounterRouteOrchTest, FlowcounterRouteOrchTestPatternAddDel) + { + std::deque entries; + // Setting route pattern + auto current_counter_num = num_created_counter; + entries.push_back({"1.1.1.0/24", "SET", { {"max_match_count", "10"}}}); + auto consumer = dynamic_cast(gFlowCounterRouteOrch->getExecutor(CFG_FLOW_COUNTER_ROUTE_PATTERN_TABLE_NAME)); + consumer->addToSync(entries); + static_cast(gFlowCounterRouteOrch)->doTask(); + ASSERT_TRUE(num_created_counter - current_counter_num == 1); + + // Deleting route pattern + current_counter_num = num_created_counter; + entries.push_back({"1.1.1.0/24", "DEL", { {"max_match_count", "10"}}}); + consumer->addToSync(entries); + static_cast(gFlowCounterRouteOrch)->doTask(); + ASSERT_TRUE(current_counter_num - num_created_counter == 1); + + } +} \ No newline at end of file diff --git a/tests/mock_tests/intfmgrd/add_ipv6_prefix_ut.cpp b/tests/mock_tests/intfmgrd/add_ipv6_prefix_ut.cpp new file mode 100644 index 0000000000..f07fa9ccbd --- /dev/null +++ b/tests/mock_tests/intfmgrd/add_ipv6_prefix_ut.cpp @@ -0,0 +1,117 @@ +#include "gtest/gtest.h" +#include +#include +#include +#include +#include "../mock_table.h" +#include "warm_restart.h" +#define private public +#include "intfmgr.h" +#undef private + +/* Override this pointer for custom behavior */ +int (*callback)(const std::string &cmd, std::string &stdout) = nullptr; +std::vector mockCallArgs; + +namespace swss { + int exec(const std::string &cmd, std::string &stdout) + { + mockCallArgs.push_back(cmd); + return callback(cmd, stdout); + } +} + +bool Ethernet0IPv6Set = false; + +int cb(const std::string &cmd, std::string &stdout){ + if (cmd == "sysctl -w net.ipv6.conf.\"Ethernet0\".disable_ipv6=0") Ethernet0IPv6Set = true; + else if (cmd.find("/sbin/ip -6 address \"add\"") == 0) { + return Ethernet0IPv6Set ? 0 : 2; + } + else { + return 0; + } + return 0; +} + +// Test Fixture +namespace add_ipv6_prefix_ut +{ + struct IntfMgrTest : public ::testing::Test + { + std::shared_ptr m_config_db; + std::shared_ptr m_app_db; + std::shared_ptr m_state_db; + std::vector cfg_intf_tables; + + virtual void SetUp() override + { + testing_db::reset(); + m_config_db = std::make_shared("CONFIG_DB", 0); + m_app_db = std::make_shared("APPL_DB", 0); + m_state_db = std::make_shared("STATE_DB", 0); + + swss::WarmStart::initialize("intfmgrd", "swss"); + + std::vector tables = { + CFG_INTF_TABLE_NAME, + CFG_LAG_INTF_TABLE_NAME, + CFG_VLAN_INTF_TABLE_NAME, + CFG_LOOPBACK_INTERFACE_TABLE_NAME, + CFG_VLAN_SUB_INTF_TABLE_NAME, + CFG_VOQ_INBAND_INTERFACE_TABLE_NAME, + }; + cfg_intf_tables = tables; + mockCallArgs.clear(); + callback = cb; + } + }; + + TEST_F(IntfMgrTest, testSettingIpv6Flag){ + Ethernet0IPv6Set = false; + swss::IntfMgr intfmgr(m_config_db.get(), m_app_db.get(), m_state_db.get(), cfg_intf_tables); + /* Set portStateTable */ + std::vector values; + values.emplace_back("state", "ok"); + intfmgr.m_statePortTable.set("Ethernet0", values, "SET", ""); + /* Set m_stateIntfTable */ + values.clear(); + values.emplace_back("vrf", ""); + intfmgr.m_stateIntfTable.set("Ethernet0", values, "SET", ""); + /* Set Ipv6 prefix */ + const std::vector& keys = {"Ethernet0", "2001::8/64"}; + const std::vector data; + intfmgr.doIntfAddrTask(keys, data, "SET"); + int ip_cmd_called = 0; + for (auto cmd : mockCallArgs){ + if (cmd.find("/sbin/ip -6 address \"add\"") == 0){ + ip_cmd_called++; + } + } + ASSERT_EQ(ip_cmd_called, 2); + } + + TEST_F(IntfMgrTest, testNoSettingIpv6Flag){ + Ethernet0IPv6Set = true; // Assuming it is already set by SDK + swss::IntfMgr intfmgr(m_config_db.get(), m_app_db.get(), m_state_db.get(), cfg_intf_tables); + /* Set portStateTable */ + std::vector values; + values.emplace_back("state", "ok"); + intfmgr.m_statePortTable.set("Ethernet0", values, "SET", ""); + /* Set m_stateIntfTable */ + values.clear(); + values.emplace_back("vrf", ""); + intfmgr.m_stateIntfTable.set("Ethernet0", values, "SET", ""); + /* Set Ipv6 prefix */ + const std::vector& keys = {"Ethernet0", "2001::8/64"}; + const std::vector data; + intfmgr.doIntfAddrTask(keys, data, "SET"); + int ip_cmd_called = 0; + for (auto cmd : mockCallArgs){ + if (cmd.find("/sbin/ip -6 address \"add\"") == 0){ + ip_cmd_called++; + } + } + ASSERT_EQ(ip_cmd_called, 1); + } +} diff --git a/tests/mock_tests/mock_orchagent_main.h b/tests/mock_tests/mock_orchagent_main.h index 04e82e74eb..57df931cb5 100644 --- a/tests/mock_tests/mock_orchagent_main.h +++ b/tests/mock_tests/mock_orchagent_main.h @@ -5,11 +5,15 @@ #include "crmorch.h" #include "portsorch.h" #include "routeorch.h" +#include "flowcounterrouteorch.h" #include "intfsorch.h" #include "neighorch.h" #include "fdborch.h" #include "mirrororch.h" +#define private public #include "bufferorch.h" +#undef private +#include "qosorch.h" #include "vrforch.h" #include "vnetorch.h" #include "vxlanorch.h" @@ -19,6 +23,7 @@ #include "tunneldecaporch.h" #include "muxorch.h" #include "nhgorch.h" +#include "copporch.h" #include "directory.h" extern int gBatchSize; @@ -41,14 +46,17 @@ extern CrmOrch *gCrmOrch; extern PortsOrch *gPortsOrch; extern FgNhgOrch *gFgNhgOrch; extern RouteOrch *gRouteOrch; +extern FlowCounterRouteOrch *gFlowCounterRouteOrch; extern IntfsOrch *gIntfsOrch; extern NeighOrch *gNeighOrch; extern FdbOrch *gFdbOrch; extern MirrorOrch *gMirrorOrch; extern BufferOrch *gBufferOrch; +extern QosOrch *gQosOrch; extern VRFOrch *gVrfOrch; extern NhgOrch *gNhgOrch; extern Srv6Orch *gSrv6Orch; +extern BfdOrch *gBfdOrch; extern Directory gDirectory; extern sai_acl_api_t *sai_acl_api; @@ -64,7 +72,13 @@ extern sai_neighbor_api_t *sai_neighbor_api; extern sai_tunnel_api_t *sai_tunnel_api; extern sai_next_hop_api_t *sai_next_hop_api; extern sai_hostif_api_t *sai_hostif_api; +extern sai_policer_api_t *sai_policer_api; extern sai_buffer_api_t *sai_buffer_api; +extern sai_qos_map_api_t *sai_qos_map_api; +extern sai_scheduler_api_t *sai_scheduler_api; +extern sai_scheduler_group_api_t *sai_scheduler_group_api; +extern sai_wred_api_t *sai_wred_api; extern sai_queue_api_t *sai_queue_api; extern sai_udf_api_t* sai_udf_api; extern sai_mpls_api_t* sai_mpls_api; +extern sai_counter_api_t* sai_counter_api; diff --git a/tests/mock_tests/mock_table.cpp b/tests/mock_tests/mock_table.cpp index 29011c30a0..0af0cb372f 100644 --- a/tests/mock_tests/mock_table.cpp +++ b/tests/mock_tests/mock_table.cpp @@ -1,4 +1,6 @@ #include "table.h" +#include "producerstatetable.h" +#include using TableDataT = std::map>; using TablesT = std::map; @@ -71,4 +73,45 @@ namespace swss keys.push_back(it.first); } } + + + void Table::del(const std::string &key, const std::string& /* op */, const std::string& /*prefix*/) + { + auto table = gDB[m_pipe->getDbId()].find(getTableName()); + if (table != gDB[m_pipe->getDbId()].end()){ + table->second.erase(key); + } + } + + void ProducerStateTable::set(const std::string &key, + const std::vector &values, + const std::string &op, + const std::string &prefix) + { + auto &table = gDB[m_pipe->getDbId()][getTableName()]; + auto iter = table.find(key); + if (iter == table.end()) + { + table[key] = values; + } + else + { + std::vector new_values(values); + std::set field_set; + for (auto &value : values) + { + field_set.insert(fvField(value)); + } + for (auto &value : iter->second) + { + auto &field = fvField(value); + if (field_set.find(field) != field_set.end()) + { + continue; + } + new_values.push_back(value); + } + iter->second.swap(new_values); + } + } } diff --git a/tests/mock_tests/portal.h b/tests/mock_tests/portal.h index c2438e8e1c..94b2051211 100644 --- a/tests/mock_tests/portal.h +++ b/tests/mock_tests/portal.h @@ -5,6 +5,8 @@ #include "aclorch.h" #include "crmorch.h" +#include "copporch.h" +#include "directory.h" #undef protected #undef private @@ -59,4 +61,39 @@ struct Portal crmOrch->getResAvailableCounters(); } }; + + struct CoppOrchInternal + { + static TrapGroupPolicerTable getTrapGroupPolicerMap(CoppOrch &obj) + { + return obj.m_trap_group_policer_map; + } + + static TrapIdTrapObjectsTable getTrapGroupIdMap(CoppOrch &obj) + { + return obj.m_syncdTrapIds; + } + + static std::vector getTrapIdsFromTrapGroup(CoppOrch &obj, sai_object_id_t trapGroupOid) + { + std::vector trapIdList; + obj.getTrapIdsFromTrapGroup(trapGroupOid, trapIdList); + return trapIdList; + } + }; + + struct DirectoryInternal + { + template + static void clear(Directory &obj) + { + obj.m_values.clear(); + } + + template + static bool empty(Directory &obj) + { + return obj.m_values.empty(); + } + }; }; diff --git a/tests/mock_tests/portsorch_ut.cpp b/tests/mock_tests/portsorch_ut.cpp index 853fdbfb69..28df6610fd 100644 --- a/tests/mock_tests/portsorch_ut.cpp +++ b/tests/mock_tests/portsorch_ut.cpp @@ -7,7 +7,9 @@ #include "mock_orchagent_main.h" #include "mock_table.h" #include "notifier.h" +#define private public #include "pfcactionhandler.h" +#undef private #include @@ -18,6 +20,105 @@ namespace portsorch_test using namespace std; + sai_queue_api_t ut_sai_queue_api; + sai_queue_api_t *pold_sai_queue_api; + sai_buffer_api_t ut_sai_buffer_api; + sai_buffer_api_t *pold_sai_buffer_api; + + string _ut_stub_queue_key; + sai_status_t _ut_stub_sai_get_queue_attribute( + _In_ sai_object_id_t queue_id, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) + { + if (attr_count == 1 && attr_list[0].id == SAI_QUEUE_ATTR_BUFFER_PROFILE_ID) + { + auto &typemapQueue = (*gBufferOrch->m_buffer_type_maps[APP_BUFFER_QUEUE_TABLE_NAME]); + auto &profileName = typemapQueue["Ethernet0:3-4"].m_objsReferencingByMe["profile"]; + auto profileNameVec = tokenize(profileName, ':'); + auto &typemapProfile = (*gBufferOrch->m_buffer_type_maps[APP_BUFFER_PROFILE_TABLE_NAME]); + attr_list[0].value.oid = typemapProfile[profileNameVec[1]].m_saiObjectId; + return SAI_STATUS_SUCCESS; + } + else + { + return pold_sai_queue_api->get_queue_attribute(queue_id, attr_count, attr_list); + } + } + + sai_status_t _ut_stub_sai_get_ingress_priority_group_attribute( + _In_ sai_object_id_t ingress_priority_group_id, + _In_ uint32_t attr_count, + _Inout_ sai_attribute_t *attr_list) + { + if (attr_count == 1 && attr_list[0].id == SAI_INGRESS_PRIORITY_GROUP_ATTR_BUFFER_PROFILE) + { + auto &typemapPg = (*gBufferOrch->m_buffer_type_maps[APP_BUFFER_PG_TABLE_NAME]); + auto &profileName = typemapPg["Ethernet0:3-4"].m_objsReferencingByMe["profile"]; + auto profileNameVec = tokenize(profileName, ':'); + auto &typemapProfile = (*gBufferOrch->m_buffer_type_maps[APP_BUFFER_PROFILE_TABLE_NAME]); + attr_list[0].value.oid = typemapProfile[profileNameVec[1]].m_saiObjectId; + return SAI_STATUS_SUCCESS; + } + else + { + return pold_sai_buffer_api->get_ingress_priority_group_attribute(ingress_priority_group_id, attr_count, attr_list); + } + } + + int _sai_create_buffer_pool_count = 0; + sai_status_t _ut_stub_sai_create_buffer_pool( + _Out_ sai_object_id_t *buffer_pool_id, + _In_ sai_object_id_t switch_id, + _In_ uint32_t attr_count, + _In_ const sai_attribute_t *attr_list) + { + auto status = pold_sai_buffer_api->create_buffer_pool(buffer_pool_id, switch_id, attr_count, attr_list); + if (SAI_STATUS_SUCCESS == status) + _sai_create_buffer_pool_count++; + return status; + } + + int _sai_remove_buffer_pool_count = 0; + sai_status_t _ut_stub_sai_remove_buffer_pool( + _In_ sai_object_id_t buffer_pool_id) + { + auto status = pold_sai_buffer_api->remove_buffer_pool(buffer_pool_id); + if (SAI_STATUS_SUCCESS == status) + _sai_remove_buffer_pool_count++; + return status; + } + + void _hook_sai_buffer_and_queue_api() + { + ut_sai_buffer_api = *sai_buffer_api; + pold_sai_buffer_api = sai_buffer_api; + ut_sai_buffer_api.create_buffer_pool = _ut_stub_sai_create_buffer_pool; + ut_sai_buffer_api.remove_buffer_pool = _ut_stub_sai_remove_buffer_pool; + ut_sai_buffer_api.get_ingress_priority_group_attribute = _ut_stub_sai_get_ingress_priority_group_attribute; + sai_buffer_api = &ut_sai_buffer_api; + + ut_sai_queue_api = *sai_queue_api; + pold_sai_queue_api = sai_queue_api; + ut_sai_queue_api.get_queue_attribute = _ut_stub_sai_get_queue_attribute; + sai_queue_api = &ut_sai_queue_api; + } + + void _unhook_sai_buffer_and_queue_api() + { + sai_buffer_api = pold_sai_buffer_api; + sai_queue_api = pold_sai_queue_api; + } + + void clear_pfcwd_zero_buffer_handler() + { + auto &zeroProfile = PfcWdZeroBufferHandler::ZeroBufferProfile::getInstance(); + zeroProfile.m_zeroIngressBufferPool = SAI_NULL_OBJECT_ID; + zeroProfile.m_zeroEgressBufferPool = SAI_NULL_OBJECT_ID; + zeroProfile.m_zeroIngressBufferProfile = SAI_NULL_OBJECT_ID; + zeroProfile.m_zeroEgressBufferProfile = SAI_NULL_OBJECT_ID; + } + struct PortsOrchTest : public ::testing::Test { shared_ptr m_app_db; @@ -103,6 +204,12 @@ namespace portsorch_test { ::testing_db::reset(); + auto buffer_maps = BufferOrch::m_buffer_type_maps; + for (auto &i : buffer_maps) + { + i.second->clear(); + } + delete gNeighOrch; gNeighOrch = nullptr; delete gFdbOrch; @@ -355,10 +462,12 @@ namespace portsorch_test TEST_F(PortsOrchTest, PfcZeroBufferHandlerLocksPortPgAndQueue) { + _hook_sai_buffer_and_queue_api(); 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); Table poolTable = Table(m_app_db.get(), APP_BUFFER_POOL_TABLE_NAME); + Table queueTable = Table(m_app_db.get(), APP_BUFFER_QUEUE_TABLE_NAME); // Get SAI default ports to populate DB auto ports = ut_helper::getInitialSaiPorts(); @@ -397,39 +506,71 @@ namespace portsorch_test Port port; gPortsOrch->getPort("Ethernet0", port); - auto countersTable = make_shared
(m_counters_db.get(), COUNTERS_TABLE); - auto dropHandler = make_unique(port.m_port_id, port.m_queue_ids[3], 3, countersTable); - // Create test buffer pool poolTable.set( - "test_pool", + "ingress_pool", { { "type", "ingress" }, { "mode", "dynamic" }, { "size", "4200000" }, }); + poolTable.set( + "egress_pool", + { + { "type", "egress" }, + { "mode", "dynamic" }, + { "size", "4200000" }, + }); // Create test buffer profile - profileTable.set("test_profile", { { "pool", "test_pool" }, + profileTable.set("test_profile", { { "pool", "ingress_pool" }, { "xon", "14832" }, { "xoff", "14832" }, { "size", "35000" }, { "dynamic_th", "0" } }); + profileTable.set("ingress_profile", { { "pool", "ingress_pool" }, + { "xon", "14832" }, + { "xoff", "14832" }, + { "size", "35000" }, + { "dynamic_th", "0" } }); + profileTable.set("egress_profile", { { "pool", "egress_pool" }, + { "size", "0" }, + { "dynamic_th", "0" } }); // Apply profile on PGs 3-4 all ports for (const auto &it : ports) { std::ostringstream oss; oss << it.first << ":3-4"; - pgTable.set(oss.str(), { { "profile", "test_profile" } }); + pgTable.set(oss.str(), { { "profile", "ingress_profile" } }); + queueTable.set(oss.str(), { {"profile", "egress_profile" } }); } gBufferOrch->addExistingData(&pgTable); gBufferOrch->addExistingData(&poolTable); gBufferOrch->addExistingData(&profileTable); + gBufferOrch->addExistingData(&queueTable); // process pool, profile and PGs static_cast(gBufferOrch)->doTask(); + auto countersTable = make_shared
(m_counters_db.get(), COUNTERS_TABLE); + auto current_create_buffer_pool_count = _sai_create_buffer_pool_count; + auto dropHandler = make_unique(port.m_port_id, port.m_queue_ids[3], 3, countersTable); + + current_create_buffer_pool_count += 2; + ASSERT_TRUE(current_create_buffer_pool_count == _sai_create_buffer_pool_count); + ASSERT_TRUE(PfcWdZeroBufferHandler::ZeroBufferProfile::getInstance().getPool(true) == gBufferOrch->m_ingressZeroBufferPool); + ASSERT_TRUE(PfcWdZeroBufferHandler::ZeroBufferProfile::getInstance().getPool(false) == gBufferOrch->m_egressZeroBufferPool); + ASSERT_TRUE(gBufferOrch->m_ingressZeroPoolRefCount == 1); + ASSERT_TRUE(gBufferOrch->m_egressZeroPoolRefCount == 1); + + std::deque entries; + entries.push_back({"Ethernet0:3-4", "SET", {{ "profile", "test_profile"}}}); + auto pgConsumer = static_cast(gBufferOrch->getExecutor(APP_BUFFER_PG_TABLE_NAME)); + pgConsumer->addToSync(entries); + entries.clear(); + static_cast(gBufferOrch)->doTask(); + // Port should have been updated by BufferOrch->doTask gPortsOrch->getPort("Ethernet0", port); auto profile_id = (*BufferOrch::m_buffer_type_maps["BUFFER_PROFILE_TABLE"])[string("test_profile")].m_saiObjectId; @@ -437,11 +578,32 @@ namespace portsorch_test ASSERT_TRUE(port.m_priority_group_pending_profile[3] == profile_id); ASSERT_TRUE(port.m_priority_group_pending_profile[4] == SAI_NULL_OBJECT_ID); - auto pgConsumer = static_cast(gBufferOrch->getExecutor(APP_BUFFER_PG_TABLE_NAME)); + pgConsumer = static_cast(gBufferOrch->getExecutor(APP_BUFFER_PG_TABLE_NAME)); pgConsumer->dumpPendingTasks(ts); ASSERT_TRUE(ts.empty()); // PG is stored in m_priority_group_pending_profile ts.clear(); + // Create a zero buffer pool after PFC storm + entries.push_back({"ingress_zero_pool", "SET", {{ "type", "ingress" }, + { "mode", "static" }, + { "size", "0" }}}); + auto poolConsumer = static_cast(gBufferOrch->getExecutor(APP_BUFFER_POOL_TABLE_NAME)); + poolConsumer->addToSync(entries); + entries.clear(); + static_cast(gBufferOrch)->doTask(); + // Reference increased + ASSERT_TRUE(gBufferOrch->m_ingressZeroPoolRefCount == 2); + // Didn't create buffer pool again + ASSERT_TRUE(_sai_create_buffer_pool_count == current_create_buffer_pool_count); + + entries.push_back({"ingress_zero_pool", "DEL", {}}); + poolConsumer->addToSync(entries); + entries.clear(); + auto current_remove_buffer_pool_count = _sai_remove_buffer_pool_count; + static_cast(gBufferOrch)->doTask(); + ASSERT_TRUE(gBufferOrch->m_ingressZeroPoolRefCount == 1); + ASSERT_TRUE(_sai_remove_buffer_pool_count == current_remove_buffer_pool_count); + // release zero buffer drop handler dropHandler.reset(); @@ -459,6 +621,139 @@ namespace portsorch_test pgConsumer->dumpPendingTasks(ts); ASSERT_TRUE(ts.empty()); // PG should be processed now ts.clear(); + clear_pfcwd_zero_buffer_handler(); + _unhook_sai_buffer_and_queue_api(); + } + + TEST_F(PortsOrchTest, PfcZeroBufferHandlerLocksPortWithZeroPoolCreated) + { + _hook_sai_buffer_and_queue_api(); + 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); + Table poolTable = Table(m_app_db.get(), APP_BUFFER_POOL_TABLE_NAME); + Table queueTable = Table(m_app_db.get(), APP_BUFFER_QUEUE_TABLE_NAME); + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + // Populate port table with SAI ports + for (const auto &it : ports) + { + portTable.set(it.first, it.second); + } + + // Set PortConfigDone, PortInitDone + portTable.set("PortConfigDone", { { "count", to_string(ports.size()) } }); + portTable.set("PortInitDone", { { "lanes", "0" } }); + + // refill consumer + gPortsOrch->addExistingData(&portTable); + + // Apply configuration : + // create ports + + static_cast(gPortsOrch)->doTask(); + + // Apply configuration + // ports + static_cast(gPortsOrch)->doTask(); + + ASSERT_TRUE(gPortsOrch->allPortsReady()); + + // No more tasks + vector ts; + gPortsOrch->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + ts.clear(); + + // Simulate storm drop handler started on Ethernet0 TC 3 + Port port; + gPortsOrch->getPort("Ethernet0", port); + + // Create test buffer pool + poolTable.set("ingress_pool", + { + { "type", "ingress" }, + { "mode", "dynamic" }, + { "size", "4200000" }, + }); + poolTable.set("egress_pool", + { + { "type", "egress" }, + { "mode", "dynamic" }, + { "size", "4200000" }, + }); + poolTable.set("ingress_zero_pool", + { + { "type", "ingress" }, + { "mode", "static" }, + { "size", "0" } + }); + auto poolConsumer = static_cast(gBufferOrch->getExecutor(APP_BUFFER_POOL_TABLE_NAME)); + + // Create test buffer profile + profileTable.set("ingress_profile", { { "pool", "ingress_pool" }, + { "xon", "14832" }, + { "xoff", "14832" }, + { "size", "35000" }, + { "dynamic_th", "0" } }); + profileTable.set("egress_profile", { { "pool", "egress_pool" }, + { "size", "0" }, + { "dynamic_th", "0" } }); + + // Apply profile on PGs 3-4 all ports + for (const auto &it : ports) + { + std::ostringstream oss; + oss << it.first << ":3-4"; + pgTable.set(oss.str(), { { "profile", "ingress_profile" } }); + queueTable.set(oss.str(), { {"profile", "egress_profile" } }); + } + + gBufferOrch->addExistingData(&poolTable); + gBufferOrch->addExistingData(&profileTable); + gBufferOrch->addExistingData(&pgTable); + gBufferOrch->addExistingData(&queueTable); + + auto current_create_buffer_pool_count = _sai_create_buffer_pool_count + 3; // call SAI API create_buffer_pool for each pool + ASSERT_TRUE(gBufferOrch->m_ingressZeroPoolRefCount == 0); + ASSERT_TRUE(gBufferOrch->m_egressZeroPoolRefCount == 0); + ASSERT_TRUE(gBufferOrch->m_ingressZeroBufferPool == SAI_NULL_OBJECT_ID); + ASSERT_TRUE(gBufferOrch->m_egressZeroBufferPool == SAI_NULL_OBJECT_ID); + + // process pool, profile and PGs + static_cast(gBufferOrch)->doTask(); + + ASSERT_TRUE(current_create_buffer_pool_count == _sai_create_buffer_pool_count); + ASSERT_TRUE(gBufferOrch->m_ingressZeroPoolRefCount == 1); + ASSERT_TRUE(gBufferOrch->m_egressZeroPoolRefCount == 0); + ASSERT_TRUE(gBufferOrch->m_ingressZeroBufferPool != SAI_NULL_OBJECT_ID); + ASSERT_TRUE(gBufferOrch->m_egressZeroBufferPool == SAI_NULL_OBJECT_ID); + + auto countersTable = make_shared
(m_counters_db.get(), COUNTERS_TABLE); + auto dropHandler = make_unique(port.m_port_id, port.m_queue_ids[3], 3, countersTable); + + current_create_buffer_pool_count++; // Increased for egress zero pool + ASSERT_TRUE(current_create_buffer_pool_count == _sai_create_buffer_pool_count); + ASSERT_TRUE(PfcWdZeroBufferHandler::ZeroBufferProfile::getInstance().getPool(true) == gBufferOrch->m_ingressZeroBufferPool); + ASSERT_TRUE(PfcWdZeroBufferHandler::ZeroBufferProfile::getInstance().getPool(false) == gBufferOrch->m_egressZeroBufferPool); + ASSERT_TRUE(gBufferOrch->m_ingressZeroPoolRefCount == 2); + ASSERT_TRUE(gBufferOrch->m_egressZeroPoolRefCount == 1); + + std::deque entries; + entries.push_back({"ingress_zero_pool", "DEL", {}}); + poolConsumer->addToSync(entries); + entries.clear(); + auto current_remove_buffer_pool_count = _sai_remove_buffer_pool_count; + static_cast(gBufferOrch)->doTask(); + ASSERT_TRUE(gBufferOrch->m_ingressZeroPoolRefCount == 1); + ASSERT_TRUE(_sai_remove_buffer_pool_count == current_remove_buffer_pool_count); + + // release zero buffer drop handler + dropHandler.reset(); + clear_pfcwd_zero_buffer_handler(); + _unhook_sai_buffer_and_queue_api(); } /* This test checks that a LAG member validation happens on orchagent level diff --git a/tests/mock_tests/qosorch_ut.cpp b/tests/mock_tests/qosorch_ut.cpp new file mode 100644 index 0000000000..a2152f8d7c --- /dev/null +++ b/tests/mock_tests/qosorch_ut.cpp @@ -0,0 +1,1052 @@ +#define private public // make Directory::m_values available to clean it. +#include "directory.h" +#undef private +#define protected public +#include "orch.h" +#undef protected +#include "ut_helper.h" +#include "mock_orchagent_main.h" +#include "mock_table.h" + +extern string gMySwitchType; + + +namespace qosorch_test +{ + using namespace std; + + shared_ptr m_app_db; + shared_ptr m_config_db; + shared_ptr m_state_db; + shared_ptr m_chassis_app_db; + + int sai_remove_qos_map_count; + int sai_remove_wred_profile_count; + int sai_remove_scheduler_count; + sai_object_id_t switch_dscp_to_tc_map_id; + + sai_remove_scheduler_fn old_remove_scheduler; + sai_scheduler_api_t ut_sai_scheduler_api, *pold_sai_scheduler_api; + sai_remove_wred_fn old_remove_wred; + sai_wred_api_t ut_sai_wred_api, *pold_sai_wred_api; + sai_remove_qos_map_fn old_remove_qos_map; + sai_qos_map_api_t ut_sai_qos_map_api, *pold_sai_qos_map_api; + sai_set_switch_attribute_fn old_set_switch_attribute_fn; + sai_switch_api_t ut_sai_switch_api, *pold_sai_switch_api; + + sai_status_t _ut_stub_sai_set_switch_attribute(sai_object_id_t switch_id, const sai_attribute_t *attr) + { + auto rc = old_set_switch_attribute_fn(switch_id, attr); + if (rc == SAI_STATUS_SUCCESS && attr->id == SAI_SWITCH_ATTR_QOS_DSCP_TO_TC_MAP) + switch_dscp_to_tc_map_id = attr->value.oid; + return rc; + } + + sai_status_t _ut_stub_sai_remove_qos_map(sai_object_id_t qos_map_id) + { + auto rc = old_remove_qos_map(qos_map_id); + if (rc == SAI_STATUS_SUCCESS) + sai_remove_qos_map_count++; + return rc; + } + + sai_status_t _ut_stub_sai_remove_wred(sai_object_id_t wred_id) + { + auto rc = old_remove_wred(wred_id); + if (rc == SAI_STATUS_SUCCESS) + sai_remove_wred_profile_count++; + return rc; + } + + sai_status_t _ut_stub_sai_remove_scheduler(sai_object_id_t scheduler_id) + { + auto rc = old_remove_scheduler(scheduler_id); + if (rc == SAI_STATUS_SUCCESS) + sai_remove_scheduler_count++; + return rc; + } + + struct QosOrchTest : public ::testing::Test + { + QosOrchTest() + { + } + + void CheckDependency(const string &referencingTableName, const string &referencingObjectName, const string &field, const string &dependentTableName, const string &dependentObjectName="") + { + auto &qosTypeMaps = QosOrch::getTypeMap(); + auto &referencingTable = (*qosTypeMaps[referencingTableName]); + auto &dependentTable = (*qosTypeMaps[dependentTableName]); + + if (dependentObjectName.empty()) + { + ASSERT_TRUE(referencingTable[referencingObjectName].m_objsReferencingByMe[field].empty()); + ASSERT_EQ(dependentTable[dependentObjectName].m_objsDependingOnMe.count(referencingObjectName), 0); + } + else + { + ASSERT_EQ(referencingTable[referencingObjectName].m_objsReferencingByMe[field], dependentTableName + ":" + dependentObjectName); + ASSERT_EQ(dependentTable[dependentObjectName].m_objsDependingOnMe.count(referencingObjectName), 1); + } + } + + void RemoveItem(const string &table, const string &key) + { + std::deque entries; + entries.push_back({key, "DEL", {}}); + auto consumer = dynamic_cast(gQosOrch->getExecutor(table)); + consumer->addToSync(entries); + } + + template void ReplaceSaiRemoveApi(sai_api_t* &sai_api, + sai_api_t &ut_sai_api, + sai_api_t* &pold_sai_api, + sai_remove_func ut_remove, + sai_remove_func &sai_remove, + sai_remove_func &old_remove, + sai_remove_func &put_remove) + { + old_remove = sai_remove; + pold_sai_api = sai_api; + ut_sai_api = *pold_sai_api; + sai_api = &ut_sai_api; + put_remove = ut_remove; + } + + void SetUp() override + { + ASSERT_EQ(sai_route_api, nullptr); + map profile = { + { "SAI_VS_SWITCH_TYPE", "SAI_VS_SWITCH_TYPE_BCM56850" }, + { "KV_DEVICE_MAC_ADDRESS", "20:03:04:05:06:00" } + }; + + ut_helper::initSaiApi(profile); + + // Hack SAI APIs + ReplaceSaiRemoveApi(sai_qos_map_api, ut_sai_qos_map_api, pold_sai_qos_map_api, + _ut_stub_sai_remove_qos_map, sai_qos_map_api->remove_qos_map, + old_remove_qos_map, ut_sai_qos_map_api.remove_qos_map); + ReplaceSaiRemoveApi(sai_scheduler_api, ut_sai_scheduler_api, pold_sai_scheduler_api, + _ut_stub_sai_remove_scheduler, sai_scheduler_api->remove_scheduler, + old_remove_scheduler, ut_sai_scheduler_api.remove_scheduler); + ReplaceSaiRemoveApi(sai_wred_api, ut_sai_wred_api, pold_sai_wred_api, + _ut_stub_sai_remove_wred, sai_wred_api->remove_wred, + old_remove_wred, ut_sai_wred_api.remove_wred); + pold_sai_switch_api = sai_switch_api; + ut_sai_switch_api = *pold_sai_switch_api; + old_set_switch_attribute_fn = pold_sai_switch_api->set_switch_attribute; + sai_switch_api = &ut_sai_switch_api; + ut_sai_switch_api.set_switch_attribute = _ut_stub_sai_set_switch_attribute; + + // Init switch and create dependencies + m_app_db = make_shared("APPL_DB", 0); + m_config_db = make_shared("CONFIG_DB", 0); + m_state_db = make_shared("STATE_DB", 0); + if(gMySwitchType == "voq") + m_chassis_app_db = make_shared("CHASSIS_APP_DB", 0); + + sai_attribute_t attr; + + attr.id = SAI_SWITCH_ATTR_INIT_SWITCH; + attr.value.booldata = true; + + auto status = sai_switch_api->create_switch(&gSwitchId, 1, &attr); + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + // Get switch source MAC address + attr.id = SAI_SWITCH_ATTR_SRC_MAC_ADDRESS; + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gMacAddress = attr.value.mac; + + // Get the default virtual router ID + attr.id = SAI_SWITCH_ATTR_DEFAULT_VIRTUAL_ROUTER_ID; + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + + ASSERT_EQ(status, SAI_STATUS_SUCCESS); + + gVirtualRouterId = attr.value.oid; + + ASSERT_EQ(gCrmOrch, nullptr); + gCrmOrch = new CrmOrch(m_config_db.get(), CFG_CRM_TABLE_NAME); + + TableConnector stateDbSwitchTable(m_state_db.get(), "SWITCH_CAPABILITY"); + TableConnector conf_asic_sensors(m_config_db.get(), CFG_ASIC_SENSORS_TABLE_NAME); + TableConnector app_switch_table(m_app_db.get(), APP_SWITCH_TABLE_NAME); + + vector switch_tables = { + conf_asic_sensors, + app_switch_table + }; + + ASSERT_EQ(gSwitchOrch, nullptr); + gSwitchOrch = new SwitchOrch(m_app_db.get(), switch_tables, stateDbSwitchTable); + + // 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 } + }; + + vector flex_counter_tables = { + CFG_FLEX_COUNTER_TABLE_NAME + }; + auto* flexCounterOrch = new FlexCounterOrch(m_config_db.get(), flex_counter_tables); + gDirectory.set(flexCounterOrch); + + ASSERT_EQ(gPortsOrch, nullptr); + gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + + ASSERT_EQ(gVrfOrch, nullptr); + gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); + + ASSERT_EQ(gIntfsOrch, nullptr); + gIntfsOrch = new IntfsOrch(m_app_db.get(), APP_INTF_TABLE_NAME, gVrfOrch, m_chassis_app_db.get()); + + const int fdborch_pri = 20; + + vector app_fdb_tables = { + { APP_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_VXLAN_FDB_TABLE_NAME, FdbOrch::fdborch_pri}, + { APP_MCLAG_FDB_TABLE_NAME, fdborch_pri} + }; + + TableConnector stateDbFdb(m_state_db.get(), STATE_FDB_TABLE_NAME); + TableConnector stateMclagDbFdb(m_state_db.get(), STATE_MCLAG_REMOTE_FDB_TABLE_NAME); + ASSERT_EQ(gFdbOrch, nullptr); + gFdbOrch = new FdbOrch(m_app_db.get(), app_fdb_tables, stateDbFdb, stateMclagDbFdb, gPortsOrch); + + ASSERT_EQ(gNeighOrch, nullptr); + gNeighOrch = new NeighOrch(m_app_db.get(), APP_NEIGH_TABLE_NAME, gIntfsOrch, gFdbOrch, gPortsOrch, m_chassis_app_db.get()); + + vector qos_tables = { + CFG_TC_TO_QUEUE_MAP_TABLE_NAME, + CFG_SCHEDULER_TABLE_NAME, + CFG_DSCP_TO_TC_MAP_TABLE_NAME, + CFG_MPLS_TC_TO_TC_MAP_TABLE_NAME, + CFG_DOT1P_TO_TC_MAP_TABLE_NAME, + CFG_QUEUE_TABLE_NAME, + CFG_PORT_QOS_MAP_TABLE_NAME, + CFG_WRED_PROFILE_TABLE_NAME, + CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, + CFG_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_TABLE_NAME, + CFG_PFC_PRIORITY_TO_QUEUE_MAP_TABLE_NAME, + CFG_DSCP_TO_FC_MAP_TABLE_NAME, + CFG_EXP_TO_FC_MAP_TABLE_NAME + }; + gQosOrch = new QosOrch(m_config_db.get(), qos_tables); + + // 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, + APP_BUFFER_PG_TABLE_NAME, + APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, + APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; + + gBufferOrch = new BufferOrch(m_app_db.get(), m_config_db.get(), m_state_db.get(), buffer_tables); + + Table portTable = Table(m_app_db.get(), APP_PORT_TABLE_NAME); + + // Get SAI default ports to populate DB + auto ports = ut_helper::getInitialSaiPorts(); + + // 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()) } }); + gPortsOrch->addExistingData(&portTable); + static_cast(gPortsOrch)->doTask(); + + portTable.set("PortInitDone", { { "lanes", "0" } }); + gPortsOrch->addExistingData(&portTable); + static_cast(gPortsOrch)->doTask(); + + Table tcToQueueMapTable = Table(m_config_db.get(), CFG_TC_TO_QUEUE_MAP_TABLE_NAME); + Table scheduleTable = Table(m_config_db.get(), CFG_SCHEDULER_TABLE_NAME); + Table dscpToTcMapTable = Table(m_config_db.get(), CFG_DSCP_TO_TC_MAP_TABLE_NAME); + Table dot1pToTcMapTable = Table(m_config_db.get(), CFG_DOT1P_TO_TC_MAP_TABLE_NAME); + Table queueTable = Table(m_config_db.get(), CFG_QUEUE_TABLE_NAME); + Table portQosMapTable = Table(m_config_db.get(), CFG_PORT_QOS_MAP_TABLE_NAME); + Table wredProfileTable = Table(m_config_db.get(), CFG_WRED_PROFILE_TABLE_NAME); + Table tcToPgMapTable = Table(m_config_db.get(), CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME); + Table pfcPriorityToPgMapTable = Table(m_config_db.get(), CFG_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_TABLE_NAME); + Table pfcPriorityToQueueMapTable = Table(m_config_db.get(), CFG_PFC_PRIORITY_TO_QUEUE_MAP_TABLE_NAME); + Table dscpToFcMapTable = Table(m_config_db.get(), CFG_DSCP_TO_FC_MAP_TABLE_NAME); + Table expToFcMapTable = Table(m_config_db.get(), CFG_EXP_TO_FC_MAP_TABLE_NAME); + + scheduleTable.set("scheduler.1", + { + {"type", "DWRR"}, + {"weight", "15"} + }); + + scheduleTable.set("scheduler.0", + { + {"type", "DWRR"}, + {"weight", "14"} + }); + + wredProfileTable.set("AZURE_LOSSLESS", + { + {"ecn", "ecn_all"}, + {"green_drop_probability", "5"}, + {"green_max_threshold", "2097152"}, + {"green_min_threshold", "1048576"}, + {"wred_green_enable", "true"}, + {"yellow_drop_probability", "5"}, + {"yellow_max_threshold", "2097152"}, + {"yellow_min_threshold", "1048576"}, + {"wred_yellow_enable", "true"}, + {"red_drop_probability", "5"}, + {"red_max_threshold", "2097152"}, + {"red_min_threshold", "1048576"}, + {"wred_red_enable", "true"} + }); + + tcToQueueMapTable.set("AZURE", + { + {"0", "0"}, + {"1", "1"} + }); + + dscpToTcMapTable.set("AZURE", + { + {"0", "0"}, + {"1", "1"} + }); + + tcToPgMapTable.set("AZURE", + { + {"0", "0"}, + {"1", "1"} + }); + + dot1pToTcMapTable.set("AZURE", + { + {"0", "0"}, + {"1", "1"} + }); + + pfcPriorityToPgMapTable.set("AZURE", + { + {"0", "0"}, + {"1", "1"} + }); + + pfcPriorityToQueueMapTable.set("AZURE", + { + {"0", "0"}, + {"1", "1"} + }); + + dot1pToTcMapTable.set("AZURE", + { + {"0", "0"}, + {"1", "1"} + }); + + gQosOrch->addExistingData(&tcToQueueMapTable); + gQosOrch->addExistingData(&dscpToTcMapTable); + gQosOrch->addExistingData(&tcToPgMapTable); + gQosOrch->addExistingData(&pfcPriorityToPgMapTable); + gQosOrch->addExistingData(&pfcPriorityToQueueMapTable); + gQosOrch->addExistingData(&scheduleTable); + gQosOrch->addExistingData(&wredProfileTable); + + static_cast(gQosOrch)->doTask(); + } + + void TearDown() override + { + auto qos_maps = QosOrch::getTypeMap(); + for (auto &i : qos_maps) + { + i.second->clear(); + } + + gDirectory.m_values.clear(); + + delete gCrmOrch; + gCrmOrch = nullptr; + + delete gSwitchOrch; + gSwitchOrch = nullptr; + + delete gVrfOrch; + gVrfOrch = nullptr; + + delete gIntfsOrch; + gIntfsOrch = nullptr; + + delete gNeighOrch; + gNeighOrch = nullptr; + + delete gFdbOrch; + gFdbOrch = nullptr; + + delete gPortsOrch; + gPortsOrch = nullptr; + + delete gQosOrch; + gQosOrch = nullptr; + + sai_qos_map_api = pold_sai_qos_map_api; + sai_scheduler_api = pold_sai_scheduler_api; + sai_wred_api = pold_sai_wred_api; + sai_switch_api = pold_sai_switch_api; + ut_helper::uninitSaiApi(); + } + }; + + TEST_F(QosOrchTest, QosOrchTestPortQosMapRemoveOneField) + { + Table portQosMapTable = Table(m_config_db.get(), CFG_PORT_QOS_MAP_TABLE_NAME); + + portQosMapTable.set("Ethernet0", + { + {"dscp_to_tc_map", "AZURE"}, + {"pfc_to_pg_map", "AZURE"}, + {"pfc_to_queue_map", "AZURE"}, + {"tc_to_pg_map", "AZURE"}, + {"tc_to_queue_map", "AZURE"}, + {"pfc_enable", "3,4"} + }); + gQosOrch->addExistingData(&portQosMapTable); + static_cast(gQosOrch)->doTask(); + + // Check whether the dependencies have been recorded + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "dscp_to_tc_map", CFG_DSCP_TO_TC_MAP_TABLE_NAME, "AZURE"); + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "pfc_to_pg_map", CFG_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_TABLE_NAME, "AZURE"); + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "pfc_to_queue_map", CFG_PFC_PRIORITY_TO_QUEUE_MAP_TABLE_NAME, "AZURE"); + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "tc_to_pg_map", CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, "AZURE"); + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "tc_to_queue_map", CFG_TC_TO_QUEUE_MAP_TABLE_NAME, "AZURE"); + + // Try removing AZURE from DSCP_TO_TC_MAP while it is still referenced + RemoveItem(CFG_DSCP_TO_TC_MAP_TABLE_NAME, "AZURE"); + auto current_sai_remove_qos_map_count = sai_remove_qos_map_count; + static_cast(gQosOrch)->doTask(); + ASSERT_EQ(current_sai_remove_qos_map_count, sai_remove_qos_map_count); + // Dependency is not cleared + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "dscp_to_tc_map", CFG_DSCP_TO_TC_MAP_TABLE_NAME, "AZURE"); + + // Remove dscp_to_tc_map from Ethernet0 via resetting the entry with field dscp_to_tc_map removed + std::deque entries; + entries.push_back({"Ethernet0", "SET", + { + {"pfc_to_pg_map", "AZURE"}, + {"pfc_to_queue_map", "AZURE"}, + {"tc_to_pg_map", "AZURE"}, + {"tc_to_queue_map", "AZURE"}, + {"pfc_enable", "3,4"} + }}); + auto consumer = dynamic_cast(gQosOrch->getExecutor(CFG_PORT_QOS_MAP_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + // Drain PORT_QOS_MAP table + static_cast(gQosOrch)->doTask(); + // Drain DSCP_TO_TC_MAP table + static_cast(gQosOrch)->doTask(); + ASSERT_EQ(current_sai_remove_qos_map_count + 1, sai_remove_qos_map_count); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_DSCP_TO_TC_MAP_TABLE_NAME]).count("AZURE"), 0); + // Dependency of dscp_to_tc_map should be cleared + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "dscp_to_tc_map", CFG_DSCP_TO_TC_MAP_TABLE_NAME); + // Dependencies of other items are not touched + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "pfc_to_pg_map", CFG_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_TABLE_NAME, "AZURE"); + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "pfc_to_queue_map", CFG_PFC_PRIORITY_TO_QUEUE_MAP_TABLE_NAME, "AZURE"); + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "tc_to_pg_map", CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, "AZURE"); + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "tc_to_queue_map", CFG_TC_TO_QUEUE_MAP_TABLE_NAME, "AZURE"); + } + + TEST_F(QosOrchTest, QosOrchTestQueueRemoveWredProfile) + { + std::deque entries; + Table queueTable = Table(m_config_db.get(), CFG_QUEUE_TABLE_NAME); + + queueTable.set("Ethernet0|3", + { + {"scheduler", "scheduler.1"}, + {"wred_profile", "AZURE_LOSSLESS"} + }); + gQosOrch->addExistingData(&queueTable); + static_cast(gQosOrch)->doTask(); + + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.1"); + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "wred_profile", CFG_WRED_PROFILE_TABLE_NAME, "AZURE_LOSSLESS"); + + // Try removing scheduler from WRED_PROFILE table while it is still referenced + RemoveItem(CFG_WRED_PROFILE_TABLE_NAME, "AZURE_LOSSLESS"); + auto current_sai_remove_wred_profile_count = sai_remove_wred_profile_count; + static_cast(gQosOrch)->doTask(); + ASSERT_EQ(current_sai_remove_wred_profile_count, sai_remove_wred_profile_count); + // Make sure the dependency is untouched + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "wred_profile", CFG_WRED_PROFILE_TABLE_NAME, "AZURE_LOSSLESS"); + + // Remove wred_profile from Ethernet0 queue 3 + entries.push_back({"Ethernet0|3", "SET", + { + {"scheduler", "scheduler.1"} + }}); + auto consumer = dynamic_cast(gQosOrch->getExecutor(CFG_QUEUE_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + // Drain QUEUE table + static_cast(gQosOrch)->doTask(); + // Drain WRED_PROFILE table + static_cast(gQosOrch)->doTask(); + // Make sure the dependency is cleared + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "wred_profile", CFG_WRED_PROFILE_TABLE_NAME); + // And the sai remove API has been called + ASSERT_EQ(current_sai_remove_wred_profile_count + 1, sai_remove_wred_profile_count); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_WRED_PROFILE_TABLE_NAME]).count("AZURE_LOSSLESS"), 0); + // Other field should be untouched + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.1"); + } + + TEST_F(QosOrchTest, QosOrchTestQueueRemoveScheduler) + { + std::deque entries; + Table queueTable = Table(m_config_db.get(), CFG_QUEUE_TABLE_NAME); + + queueTable.set("Ethernet0|3", + { + {"scheduler", "scheduler.1"}, + {"wred_profile", "AZURE_LOSSLESS"} + }); + gQosOrch->addExistingData(&queueTable); + static_cast(gQosOrch)->doTask(); + + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.1"); + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "wred_profile", CFG_WRED_PROFILE_TABLE_NAME, "AZURE_LOSSLESS"); + + // Try removing scheduler from QUEUE table while it is still referenced + RemoveItem(CFG_SCHEDULER_TABLE_NAME, "scheduler.1"); + auto current_sai_remove_scheduler_count = sai_remove_scheduler_count; + static_cast(gQosOrch)->doTask(); + ASSERT_EQ(current_sai_remove_scheduler_count, sai_remove_scheduler_count); + // Make sure the dependency is untouched + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.1"); + + // Remove scheduler from Ethernet0 queue 3 + entries.push_back({"Ethernet0|3", "SET", + { + {"wred_profile", "AZURE_LOSSLESS"} + }}); + auto consumer = dynamic_cast(gQosOrch->getExecutor(CFG_QUEUE_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + // Drain QUEUE table + static_cast(gQosOrch)->doTask(); + // Drain SCHEDULER table + static_cast(gQosOrch)->doTask(); + // Make sure the dependency is cleared + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "scheduler", CFG_SCHEDULER_TABLE_NAME); + // And the sai remove API has been called + ASSERT_EQ(current_sai_remove_scheduler_count + 1, sai_remove_scheduler_count); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_SCHEDULER_TABLE_NAME]).count("scheduler.1"), 0); + // Other field should be untouched + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "wred_profile", CFG_WRED_PROFILE_TABLE_NAME, "AZURE_LOSSLESS"); + } + + TEST_F(QosOrchTest, QosOrchTestQueueReplaceFieldAndRemoveObject) + { + std::deque entries; + Table queueTable = Table(m_config_db.get(), CFG_QUEUE_TABLE_NAME); + auto queueConsumer = dynamic_cast(gQosOrch->getExecutor(CFG_QUEUE_TABLE_NAME)); + auto wredProfileConsumer = dynamic_cast(gQosOrch->getExecutor(CFG_WRED_PROFILE_TABLE_NAME)); + auto schedulerConsumer = dynamic_cast(gQosOrch->getExecutor(CFG_SCHEDULER_TABLE_NAME)); + + queueTable.set("Ethernet0|3", + { + {"scheduler", "scheduler.1"}, + {"wred_profile", "AZURE_LOSSLESS"} + }); + gQosOrch->addExistingData(&queueTable); + static_cast(gQosOrch)->doTask(); + + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.1"); + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "wred_profile", CFG_WRED_PROFILE_TABLE_NAME, "AZURE_LOSSLESS"); + + // Try replacing scheduler in QUEUE table: scheduler.1 => scheduler.0 + entries.push_back({"Ethernet0|3", "SET", + { + {"scheduler", "scheduler.0"}, + {"wred_profile", "AZURE_LOSSLESS"} + }}); + queueConsumer->addToSync(entries); + entries.clear(); + // Drain QUEUE table + static_cast(gQosOrch)->doTask(); + // Make sure the dependency is updated + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.0"); + // And the other field is not touched + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "wred_profile", CFG_WRED_PROFILE_TABLE_NAME, "AZURE_LOSSLESS"); + + RemoveItem(CFG_SCHEDULER_TABLE_NAME, "scheduler.1"); + auto current_sai_remove_scheduler_count = sai_remove_scheduler_count; + static_cast(gQosOrch)->doTask(); + ASSERT_EQ(++current_sai_remove_scheduler_count, sai_remove_scheduler_count); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_SCHEDULER_TABLE_NAME]).count("scheduler.1"), 0); + + entries.push_back({"AZURE_LOSSLESS_1", "SET", + { + {"ecn", "ecn_all"}, + {"green_drop_probability", "5"}, + {"green_max_threshold", "2097152"}, + {"green_min_threshold", "1048576"}, + {"wred_green_enable", "true"}, + {"yellow_drop_probability", "5"}, + {"yellow_max_threshold", "2097152"}, + {"yellow_min_threshold", "1048576"}, + {"wred_yellow_enable", "true"}, + {"red_drop_probability", "5"}, + {"red_max_threshold", "2097152"}, + {"red_min_threshold", "1048576"}, + {"wred_red_enable", "true"} + }}); + wredProfileConsumer->addToSync(entries); + entries.clear(); + // Drain WRED_PROFILE table + static_cast(gQosOrch)->doTask(); + + // Replace wred_profile from Ethernet0 queue 3 + entries.push_back({"Ethernet0|3", "SET", + { + {"scheduler", "scheduler.0"}, + {"wred_profile", "AZURE_LOSSLESS_1"} + }}); + queueConsumer->addToSync(entries); + entries.clear(); + // Drain QUEUE table + static_cast(gQosOrch)->doTask(); + // Make sure the dependency is updated + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "wred_profile", CFG_WRED_PROFILE_TABLE_NAME, "AZURE_LOSSLESS_1"); + // And the other field is not touched + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.0"); + + RemoveItem(CFG_WRED_PROFILE_TABLE_NAME, "AZURE_LOSSLESS"); + // Drain WRED_PROFILE table + auto current_sai_remove_wred_profile_count = sai_remove_wred_profile_count; + static_cast(gQosOrch)->doTask(); + ASSERT_EQ(++current_sai_remove_wred_profile_count, sai_remove_wred_profile_count); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_WRED_PROFILE_TABLE_NAME]).count("AZURE_LOSSLESS"), 0); + + // Remove object + entries.push_back({"Ethernet0|3", "DEL", {}}); + queueConsumer->addToSync(entries); + entries.clear(); + static_cast(gQosOrch)->doTask(); + + // Make sure the dependency is updated + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "wred_profile", CFG_WRED_PROFILE_TABLE_NAME); + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|3", "scheduler", CFG_SCHEDULER_TABLE_NAME); + + // Remove scheduler object + entries.push_back({"scheduler.0", "DEL", {}}); + schedulerConsumer->addToSync(entries); + entries.clear(); + static_cast(gQosOrch)->doTask(); + ASSERT_EQ(++current_sai_remove_scheduler_count, sai_remove_scheduler_count); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_SCHEDULER_TABLE_NAME]).count("scheduler.0"), 0); + + // Remove wred profile object + entries.push_back({"AZURE_LOSSLESS_1", "DEL", {}}); + wredProfileConsumer->addToSync(entries); + entries.clear(); + static_cast(gQosOrch)->doTask(); + ASSERT_EQ(++current_sai_remove_wred_profile_count, sai_remove_wred_profile_count); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_WRED_PROFILE_TABLE_NAME]).count("AZURE_LOSSLESS_1"), 0); + } + + TEST_F(QosOrchTest, QosOrchTestPortQosMapReplaceOneFieldAndRemoveObject) + { + std::deque entries; + Table portQosMapTable = Table(m_config_db.get(), CFG_PORT_QOS_MAP_TABLE_NAME); + + portQosMapTable.set("Ethernet0", + { + {"dscp_to_tc_map", "AZURE"}, + {"pfc_to_pg_map", "AZURE"}, + {"pfc_to_queue_map", "AZURE"}, + {"tc_to_pg_map", "AZURE"}, + {"tc_to_queue_map", "AZURE"}, + {"pfc_enable", "3,4"} + }); + + static_cast(gQosOrch)->doTask(); + + entries.push_back({"AZURE_1", "SET", + { + {"1", "0"}, + {"0", "1"} + }}); + + auto consumer = dynamic_cast(gQosOrch->getExecutor(CFG_DSCP_TO_TC_MAP_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + // Drain DSCP_TO_TC_MAP table + static_cast(gQosOrch)->doTask(); + + entries.push_back({"Ethernet0", "SET", + { + {"dscp_to_tc_map", "AZURE_1"}, + {"pfc_to_pg_map", "AZURE"}, + {"pfc_to_queue_map", "AZURE"}, + {"tc_to_pg_map", "AZURE"}, + {"tc_to_queue_map", "AZURE"}, + {"pfc_enable", "3,4"} + }}); + consumer = dynamic_cast(gQosOrch->getExecutor(CFG_PORT_QOS_MAP_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + // Drain PORT_QOS_MAP table + static_cast(gQosOrch)->doTask(); + // Dependency is updated + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "dscp_to_tc_map", CFG_DSCP_TO_TC_MAP_TABLE_NAME, "AZURE_1"); + + // Try removing AZURE from DSCP_TO_TC_MAP + RemoveItem(CFG_DSCP_TO_TC_MAP_TABLE_NAME, "AZURE"); + auto current_sai_remove_qos_map_count = sai_remove_qos_map_count; + static_cast(gQosOrch)->doTask(); + ASSERT_EQ(++current_sai_remove_qos_map_count, sai_remove_qos_map_count); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_DSCP_TO_TC_MAP_TABLE_NAME]).count("AZURE"), 0); + // Global dscp to tc map should not be cleared + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_DSCP_TO_TC_MAP_TABLE_NAME])["AZURE_1"].m_saiObjectId, switch_dscp_to_tc_map_id); + + // Make sure other dependencies are not touched + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "pfc_to_pg_map", CFG_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_TABLE_NAME, "AZURE"); + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "pfc_to_queue_map", CFG_PFC_PRIORITY_TO_QUEUE_MAP_TABLE_NAME, "AZURE"); + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "tc_to_pg_map", CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, "AZURE"); + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "tc_to_queue_map", CFG_TC_TO_QUEUE_MAP_TABLE_NAME, "AZURE"); + + // Remove port from PORT_QOS_MAP table + entries.push_back({"Ethernet0", "DEL", {}}); + consumer->addToSync(entries); + entries.clear(); + static_cast(gQosOrch)->doTask(); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_PORT_QOS_MAP_TABLE_NAME]).count("Ethernet0"), 0); + + // Make sure the maps can be removed now. Checking anyone should suffice since all the maps are handled in the same way. + entries.push_back({"AZURE", "DEL", {}}); + consumer = dynamic_cast(gQosOrch->getExecutor(CFG_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + static_cast(gQosOrch)->doTask(); + ASSERT_EQ(++current_sai_remove_qos_map_count, sai_remove_qos_map_count); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_PFC_PRIORITY_TO_PRIORITY_GROUP_MAP_TABLE_NAME]).count("AZURE"), 0); + + entries.push_back({"AZURE_1", "DEL", {}}); + consumer = dynamic_cast(gQosOrch->getExecutor(CFG_DSCP_TO_TC_MAP_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + static_cast(gQosOrch)->doTask(); + ASSERT_EQ(++current_sai_remove_qos_map_count, sai_remove_qos_map_count); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_DSCP_TO_TC_MAP_TABLE_NAME]).count("AZURE_1"), 0); + // Global dscp to tc map should be cleared + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_DSCP_TO_TC_MAP_TABLE_NAME])["AZURE_1"].m_saiObjectId, SAI_NULL_OBJECT_ID); + } + + TEST_F(QosOrchTest, QosOrchTestPortQosMapReferencingObjRemoveThenAdd) + { + vector ts; + std::deque entries; + Table portQosMapTable = Table(m_config_db.get(), CFG_PORT_QOS_MAP_TABLE_NAME); + + portQosMapTable.set("Ethernet0", + { + {"dscp_to_tc_map", "AZURE"} + }); + gQosOrch->addExistingData(&portQosMapTable); + static_cast(gQosOrch)->doTask(); + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "dscp_to_tc_map", CFG_DSCP_TO_TC_MAP_TABLE_NAME, "AZURE"); + + // Remove referenced obj + entries.push_back({"AZURE", "DEL", {}}); + auto dscpToTcMapConsumer = dynamic_cast(gQosOrch->getExecutor(CFG_DSCP_TO_TC_MAP_TABLE_NAME)); + dscpToTcMapConsumer->addToSync(entries); + entries.clear(); + // Drain DSCP_TO_TC_MAP table + static_cast(gQosOrch)->doTask(); + // Make sure the dependency remains + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "dscp_to_tc_map", CFG_DSCP_TO_TC_MAP_TABLE_NAME, "AZURE"); + // Make sure the notification isn't drained + static_cast(gQosOrch)->dumpPendingTasks(ts); + ASSERT_EQ(ts.size(), 1); + ASSERT_EQ(ts[0], "DSCP_TO_TC_MAP|AZURE|DEL"); + ts.clear(); + + // Remove and readd referencing obj + entries.push_back({"Ethernet0", "DEL", {}}); + entries.push_back({"Ethernet0", "SET", + { + {"dscp_to_tc_map", "AZURE"} + }}); + auto portQosMapConsumer = dynamic_cast(gQosOrch->getExecutor(CFG_PORT_QOS_MAP_TABLE_NAME)); + portQosMapConsumer->addToSync(entries); + entries.clear(); + // Drain the PORT_QOS_MAP table + static_cast(gQosOrch)->doTask(); + // Drain the DSCP_TO_TC_MAP table which contains items need to retry + static_cast(gQosOrch)->doTask(); + // The dependency should be removed + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "dscp_to_tc_map", CFG_DSCP_TO_TC_MAP_TABLE_NAME); + static_cast(gQosOrch)->dumpPendingTasks(ts); + ASSERT_EQ(ts.size(), 1); + ASSERT_EQ(ts[0], "PORT_QOS_MAP|Ethernet0|SET|dscp_to_tc_map:AZURE"); + ts.clear(); + + // Re-create referenced obj + entries.push_back({"AZURE", "SET", + { + {"1", "0"} + }}); + dscpToTcMapConsumer->addToSync(entries); + entries.clear(); + // Drain DSCP_TO_TC_MAP table + static_cast(gQosOrch)->doTask(); + // Make sure the dependency recovers + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "dscp_to_tc_map", CFG_DSCP_TO_TC_MAP_TABLE_NAME, "AZURE"); + + // All items have been drained + static_cast(gQosOrch)->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + + // Remove and recreate the referenced obj + entries.push_back({"AZURE", "DEL", {}}); + entries.push_back({"AZURE", "SET", + { + {"1", "0"} + }}); + dscpToTcMapConsumer->addToSync(entries); + entries.clear(); + // Drain DSCP_TO_TC_MAP table + static_cast(gQosOrch)->doTask(); + // Make sure the dependency remains + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "dscp_to_tc_map", CFG_DSCP_TO_TC_MAP_TABLE_NAME, "AZURE"); + // Make sure the notification isn't drained + static_cast(gQosOrch)->dumpPendingTasks(ts); + ASSERT_EQ(ts.size(), 2); + ASSERT_EQ(ts[0], "DSCP_TO_TC_MAP|AZURE|DEL"); + ASSERT_EQ(ts[1], "DSCP_TO_TC_MAP|AZURE|SET|1:0"); + ts.clear(); + } + + TEST_F(QosOrchTest, QosOrchTestQueueReferencingObjRemoveThenAdd) + { + vector ts; + std::deque entries; + Table queueTable = Table(m_config_db.get(), CFG_QUEUE_TABLE_NAME); + + queueTable.set("Ethernet0|0", + { + {"scheduler", "scheduler.0"} + }); + gQosOrch->addExistingData(&queueTable); + static_cast(gQosOrch)->doTask(); + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|0", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.0"); + + // Remove referenced obj + entries.push_back({"scheduler.0", "DEL", {}}); + auto schedulerConsumer = dynamic_cast(gQosOrch->getExecutor(CFG_SCHEDULER_TABLE_NAME)); + schedulerConsumer->addToSync(entries); + entries.clear(); + // Drain SCHEDULER table + static_cast(gQosOrch)->doTask(); + // Make sure the dependency remains + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|0", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.0"); + static_cast(gQosOrch)->dumpPendingTasks(ts); + ASSERT_EQ(ts.size(), 1); + ASSERT_EQ(ts[0], "SCHEDULER|scheduler.0|DEL"); + ts.clear(); + + // Remove and readd referencing obj + entries.push_back({"Ethernet0|0", "DEL", {}}); + entries.push_back({"Ethernet0|0", "SET", + { + {"scheduler", "scheduler.0"} + }}); + auto queueConsumer = dynamic_cast(gQosOrch->getExecutor(CFG_QUEUE_TABLE_NAME)); + queueConsumer->addToSync(entries); + entries.clear(); + // Drain QUEUE table + static_cast(gQosOrch)->doTask(); + // The dependency should be removed + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|0", "scheduler", CFG_SCHEDULER_TABLE_NAME); + static_cast(gQosOrch)->dumpPendingTasks(ts); + ASSERT_EQ(ts.size(), 1); + ASSERT_EQ(ts[0], "QUEUE|Ethernet0|0|SET|scheduler:scheduler.0"); + ts.clear(); + + // Re-create referenced obj + entries.push_back({"scheduler.0", "SET", + { + {"type", "DWRR"}, + {"weight", "14"} + }}); + schedulerConsumer->addToSync(entries); + entries.clear(); + // Drain SCHEDULER table + static_cast(gQosOrch)->doTask(); + // Drain QUEUE table + static_cast(gQosOrch)->doTask(); + // Make sure the dependency recovers + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|0", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.0"); + + // All items have been drained + static_cast(gQosOrch)->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + + // Remove and then re-add the referenced obj + entries.push_back({"scheduler.0", "DEL", {}}); + entries.push_back({"scheduler.0", "SET", + { + {"type", "DWRR"}, + {"weight", "14"} + }}); + schedulerConsumer->addToSync(entries); + entries.clear(); + // Drain SCHEDULER table + static_cast(gQosOrch)->doTask(); + // Make sure the dependency remains + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|0", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.0"); + static_cast(gQosOrch)->dumpPendingTasks(ts); + ASSERT_EQ(ts.size(), 2); + ASSERT_EQ(ts[0], "SCHEDULER|scheduler.0|DEL"); + ASSERT_EQ(ts[1], "SCHEDULER|scheduler.0|SET|type:DWRR|weight:14"); + ts.clear(); + } + + TEST_F(QosOrchTest, QosOrchTestGlobalDscpToTcMap) + { + // Make sure dscp to tc map is correct + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_DSCP_TO_TC_MAP_TABLE_NAME])["AZURE"].m_saiObjectId, switch_dscp_to_tc_map_id); + + // Create a new dscp to tc map + std::deque entries; + entries.push_back({"AZURE_1", "SET", + { + {"1", "0"}, + {"0", "1"} + }}); + + auto consumer = dynamic_cast(gQosOrch->getExecutor(CFG_DSCP_TO_TC_MAP_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + // Drain DSCP_TO_TC_MAP table + static_cast(gQosOrch)->doTask(); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_DSCP_TO_TC_MAP_TABLE_NAME])["AZURE_1"].m_saiObjectId, switch_dscp_to_tc_map_id); + + entries.push_back({"AZURE_1", "DEL", {}}); + consumer->addToSync(entries); + entries.clear(); + // Drain DSCP_TO_TC_MAP table + static_cast(gQosOrch)->doTask(); + ASSERT_EQ((*QosOrch::getTypeMap()[CFG_DSCP_TO_TC_MAP_TABLE_NAME])["AZURE"].m_saiObjectId, switch_dscp_to_tc_map_id); + } + + TEST_F(QosOrchTest, QosOrchTestRetryFirstItem) + { + // There was a bug in QosOrch that the 2nd notifications and after can not be handled, eg the 1st one needs to be retried + // This is to verify the bug has been fixed + vector ts; + std::deque entries; + + // Try adding dscp_to_tc_map AZURE.1 and AZURE to PORT_QOS_MAP table + // The object AZURE.1 does not exist so the first item can not be handled and remain in m_toSync. + entries.push_back({"Ethernet0", "SET", + { + {"dscp_to_tc_map", "AZURE.1"} + }}); + entries.push_back({"Ethernet4", "SET", + { + {"dscp_to_tc_map", "AZURE"} + }}); + auto portQosMapConsumer = dynamic_cast(gQosOrch->getExecutor(CFG_PORT_QOS_MAP_TABLE_NAME)); + portQosMapConsumer->addToSync(entries); + entries.clear(); + static_cast(gQosOrch)->doTask(); + // The 2nd notification should be handled. Make sure by checking reference + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet4", "dscp_to_tc_map", CFG_DSCP_TO_TC_MAP_TABLE_NAME, "AZURE"); + // Make sure there is one item left + portQosMapConsumer->dumpPendingTasks(ts); + ASSERT_EQ(ts[0], "PORT_QOS_MAP|Ethernet0|SET|dscp_to_tc_map:AZURE.1"); + ASSERT_EQ(ts.size(), 1); + ts.clear(); + + // Try adding scheduler.0 and scheduler.2 to QUEUE table + entries.push_back({"Ethernet0|0", "SET", + { + {"scheduler", "scheduler.2"} + }}); + entries.push_back({"Ethernet0|1", "SET", + { + {"scheduler", "scheduler.0"} + }}); + auto queueConsumer = dynamic_cast(gQosOrch->getExecutor(CFG_QUEUE_TABLE_NAME)); + queueConsumer->addToSync(entries); + entries.clear(); + static_cast(gQosOrch)->doTask(); + // The 2nd notification should be handled. Make sure by checking reference + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|1", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.0"); + // Make sure there is one item left + queueConsumer->dumpPendingTasks(ts); + ASSERT_EQ(ts[0], "QUEUE|Ethernet0|0|SET|scheduler:scheduler.2"); + ASSERT_EQ(ts.size(), 1); + ts.clear(); + + // Try removing AZURE and adding AZURE.1 to DSCP_TO_TC_MAP table + entries.push_back({"AZURE", "DEL", {{}}}); + entries.push_back({"AZURE.1", "SET", + { + {"1", "1"} + }}); + auto consumer = dynamic_cast(gQosOrch->getExecutor(CFG_DSCP_TO_TC_MAP_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + static_cast(gQosOrch)->doTask(); + // The 2nd notification should be handled. Make sure by checking reference + CheckDependency(CFG_PORT_QOS_MAP_TABLE_NAME, "Ethernet0", "dscp_to_tc_map", CFG_DSCP_TO_TC_MAP_TABLE_NAME, "AZURE.1"); + // The pending item in PORT_QOS_MAP table should also be handled since the dependency is met + portQosMapConsumer->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + consumer->dumpPendingTasks(ts); + ASSERT_EQ(ts[0], "DSCP_TO_TC_MAP|AZURE|DEL|:"); + ASSERT_EQ(ts.size(), 1); + ts.clear(); + + entries.push_back({"scheduler.0", "DEL", {{}}}); + entries.push_back({"scheduler.2", "SET", + { + {"type", "DWRR"}, + {"weight", "15"} + }}); + consumer = dynamic_cast(gQosOrch->getExecutor(CFG_SCHEDULER_TABLE_NAME)); + consumer->addToSync(entries); + entries.clear(); + static_cast(gQosOrch)->doTask(); + // We need a second call to "doTask" because scheduler table is handled after queue table + static_cast(gQosOrch)->doTask(); + // The 2nd notification should be handled. Make sure by checking reference + CheckDependency(CFG_QUEUE_TABLE_NAME, "Ethernet0|0", "scheduler", CFG_SCHEDULER_TABLE_NAME, "scheduler.2"); + // The pending item in QUEUE table should also be handled since the dependency is met + queueConsumer->dumpPendingTasks(ts); + ASSERT_TRUE(ts.empty()); + consumer->dumpPendingTasks(ts); + ASSERT_EQ(ts[0], "SCHEDULER|scheduler.0|DEL|:"); + ASSERT_EQ(ts.size(), 1); + ts.clear(); + } +} diff --git a/tests/mock_tests/routeorch_ut.cpp b/tests/mock_tests/routeorch_ut.cpp index 84f92a088c..66df4bfbcc 100644 --- a/tests/mock_tests/routeorch_ut.cpp +++ b/tests/mock_tests/routeorch_ut.cpp @@ -185,6 +185,12 @@ namespace routeorch_test ASSERT_EQ(gPortsOrch, nullptr); gPortsOrch = new PortsOrch(m_app_db.get(), m_state_db.get(), ports_tables, m_chassis_app_db.get()); + static const vector route_pattern_tables = { + CFG_FLOW_COUNTER_ROUTE_PATTERN_TABLE_NAME, + }; + gFlowCounterRouteOrch = new FlowCounterRouteOrch(m_config_db.get(), route_pattern_tables); + gDirectory.set(gFlowCounterRouteOrch); + ASSERT_EQ(gVrfOrch, nullptr); gVrfOrch = new VRFOrch(m_app_db.get(), APP_VRF_TABLE_NAME, m_state_db.get(), STATE_VRF_OBJECT_TABLE_NAME); diff --git a/tests/mock_tests/swssnet_ut.cpp b/tests/mock_tests/swssnet_ut.cpp new file mode 100644 index 0000000000..6ec64e0202 --- /dev/null +++ b/tests/mock_tests/swssnet_ut.cpp @@ -0,0 +1,39 @@ +#include "ut_helper.h" +#include "swssnet.h" + +namespace swssnet_test +{ + struct SwssNetTest : public ::testing::Test + { + SwssNetTest() {} + }; + + TEST_F(SwssNetTest, CovertSAIPrefixToSONiCPrefix) + { + IpPrefix ip_prefix("1.2.3.4/24"); + sai_ip_prefix_t sai_prefix; + swss::copy(sai_prefix, ip_prefix); + IpPrefix ip_prefix_copied = swss::getIpPrefixFromSaiPrefix(sai_prefix); + ASSERT_EQ("1.2.3.4/24", ip_prefix_copied.to_string()); + + IpPrefix ip_prefix1("1.2.3.4/32"); + swss::copy(sai_prefix, ip_prefix1); + ip_prefix_copied = swss::getIpPrefixFromSaiPrefix(sai_prefix); + ASSERT_EQ("1.2.3.4/32", ip_prefix_copied.to_string()); + + IpPrefix ip_prefix2("0.0.0.0/0"); + swss::copy(sai_prefix, ip_prefix2); + ip_prefix_copied = swss::getIpPrefixFromSaiPrefix(sai_prefix); + ASSERT_EQ("0.0.0.0/0", ip_prefix_copied.to_string()); + + IpPrefix ip_prefix3("2000::1/128"); + swss::copy(sai_prefix, ip_prefix3); + ip_prefix_copied = swss::getIpPrefixFromSaiPrefix(sai_prefix); + ASSERT_EQ("2000::1/128", ip_prefix_copied.to_string()); + + IpPrefix ip_prefix4("::/0"); + swss::copy(sai_prefix, ip_prefix4); + ip_prefix_copied = swss::getIpPrefixFromSaiPrefix(sai_prefix); + ASSERT_EQ("::/0", ip_prefix_copied.to_string()); + } +} diff --git a/tests/mock_tests/ut_helper.h b/tests/mock_tests/ut_helper.h index baaf8184d8..f8f411d025 100644 --- a/tests/mock_tests/ut_helper.h +++ b/tests/mock_tests/ut_helper.h @@ -13,7 +13,7 @@ namespace ut_helper { sai_status_t initSaiApi(const std::map &profile); - void uninitSaiApi(); + sai_status_t uninitSaiApi(); map> getInitialSaiPorts(); } diff --git a/tests/mock_tests/ut_saihelper.cpp b/tests/mock_tests/ut_saihelper.cpp index e16a217e71..80a2d6ee38 100644 --- a/tests/mock_tests/ut_saihelper.cpp +++ b/tests/mock_tests/ut_saihelper.cpp @@ -76,16 +76,26 @@ namespace ut_helper sai_api_query(SAI_API_NEXT_HOP, (void **)&sai_next_hop_api); sai_api_query(SAI_API_ACL, (void **)&sai_acl_api); sai_api_query(SAI_API_HOSTIF, (void **)&sai_hostif_api); + sai_api_query(SAI_API_POLICER, (void **)&sai_policer_api); sai_api_query(SAI_API_BUFFER, (void **)&sai_buffer_api); + sai_api_query(SAI_API_QOS_MAP, (void **)&sai_qos_map_api); + sai_api_query(SAI_API_SCHEDULER_GROUP, (void **)&sai_scheduler_group_api); + sai_api_query(SAI_API_SCHEDULER, (void **)&sai_scheduler_api); + sai_api_query(SAI_API_WRED, (void **)&sai_wred_api); sai_api_query(SAI_API_QUEUE, (void **)&sai_queue_api); sai_api_query(SAI_API_MPLS, (void**)&sai_mpls_api); + sai_api_query(SAI_API_COUNTER, (void**)&sai_counter_api); return SAI_STATUS_SUCCESS; } - void uninitSaiApi() + sai_status_t uninitSaiApi() { - sai_api_uninitialize(); + auto status = sai_api_uninitialize(); + if (status != SAI_STATUS_SUCCESS) + { + return status; + } sai_switch_api = nullptr; sai_bridge_api = nullptr; @@ -100,8 +110,12 @@ namespace ut_helper sai_next_hop_api = nullptr; sai_acl_api = nullptr; sai_hostif_api = nullptr; + sai_policer_api = nullptr; sai_buffer_api = nullptr; sai_queue_api = nullptr; + sai_counter_api = nullptr; + + return SAI_STATUS_SUCCESS; } map> getInitialSaiPorts() diff --git a/tests/p4rt/test_l3.py b/tests/p4rt/test_l3.py index 42f32facbd..4156576bc2 100644 --- a/tests/p4rt/test_l3.py +++ b/tests/p4rt/test_l3.py @@ -1328,7 +1328,7 @@ def test_PruneNextHopOnWarmBoot(self, dvs, testlog): util.set_interface_status(dvs, if_name) # Execute the warm reboot. - dvs.runcmd("config warm_restart enable swss") + dvs.warm_restart_swss("true") dvs.stop_swss() dvs.start_swss() diff --git a/tests/p4rt/util.py b/tests/p4rt/util.py index 831c7a5cbe..778a54960d 100644 --- a/tests/p4rt/util.py +++ b/tests/p4rt/util.py @@ -84,8 +84,8 @@ def get_port_oid_by_name(dvs, port_name): return port_oid def initialize_interface(dvs, port_name, ip): - dvs.runcmd("config interface startup {}".format(port_name)) - dvs.runcmd("config interface ip add {} {}".format(port_name, ip)) + dvs.port_admin_set(port_name, "up") + dvs.interface_ip_add(port_name, ip) def set_interface_status(dvs, if_name, status = "down", server = 0): dvs.servers[0].runcmd("ip link set {} dev {}".format(status, if_name)) == 0 diff --git a/tests/test_acl.py b/tests/test_acl.py index cd40d84e86..f47cfd5c78 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -553,11 +553,11 @@ def test_AclRuleRedirect(self, dvs, dvs_acl, l3_acl_table, setup_teardown_neighb class TestAclCrmUtilization: @pytest.fixture(scope="class", autouse=True) def configure_crm_polling_interval_for_test(self, dvs): - dvs.runcmd("crm config polling interval 1") + dvs.crm_poll_set("1") yield - dvs.runcmd("crm config polling interval 300") + dvs.crm_poll_set("300") def test_ValidateAclTableBindingCrmUtilization(self, dvs, dvs_acl): counter_db = dvs.get_counters_db() diff --git a/tests/test_acl_cli.py b/tests/test_acl_cli.py deleted file mode 100644 index 02785314d2..0000000000 --- a/tests/test_acl_cli.py +++ /dev/null @@ -1,33 +0,0 @@ -class TestAclCli: - def test_AddTableMultipleTimes(self, dvs, dvs_acl): - dvs.runcmd("config acl add table TEST L3 -p Ethernet0") - - cdb = dvs.get_config_db() - cdb.wait_for_field_match( - "ACL_TABLE", - "TEST", - {"ports": "Ethernet0"} - ) - - # Verify that subsequent updates don't delete "ports" from config DB - dvs.runcmd("config acl add table TEST L3 -p Ethernet4") - cdb.wait_for_field_match( - "ACL_TABLE", - "TEST", - {"ports": "Ethernet4"} - ) - - # Verify that subsequent updates propagate to ASIC DB - L3_BIND_PORTS = ["Ethernet0", "Ethernet4", "Ethernet8", "Ethernet12"] - dvs.runcmd(f"config acl add table TEST L3 -p {','.join(L3_BIND_PORTS)}") - acl_table_id = dvs_acl.get_acl_table_ids(1)[0] - acl_table_group_ids = dvs_acl.get_acl_table_group_ids(len(L3_BIND_PORTS)) - - dvs_acl.verify_acl_table_group_members(acl_table_id, acl_table_group_ids, 1) - dvs_acl.verify_acl_table_port_binding(acl_table_id, L3_BIND_PORTS, 1) - - -# Add Dummy always-pass test at end as workaroud -# for issue when Flaky fail on final test it invokes module tear-down before retrying -def test_nonflaky_dummy(): - pass diff --git a/tests/test_acl_portchannel.py b/tests/test_acl_portchannel.py index 759850d1be..210c4f18d8 100644 --- a/tests/test_acl_portchannel.py +++ b/tests/test_acl_portchannel.py @@ -1,9 +1,87 @@ import time import pytest +import logging from swsscommon import swsscommon +logging.basicConfig(level=logging.INFO) +acllogger = logging.getLogger(__name__) + + +@pytest.fixture(autouse=True, scope="class") +def dvs_api(request, dvs_acl): + # Fixtures are created when first requested by a test, and are destroyed based on their scope + if request.cls is None: + yield + return + acllogger.info("Initialize DVS API: ACL") + request.cls.dvs_acl = dvs_acl + yield + acllogger.info("Deinitialize DVS API: ACL") + del request.cls.dvs_acl + + +@pytest.mark.usefixtures("dvs_lag_manager") +class TestAclInterfaceBinding: + @pytest.mark.parametrize("stage", ["ingress", "egress"]) + def test_AclTablePortChannelMemberBinding(self, testlog, stage): + """Verify that LAG member creation is prohibited when ACL binding is configured + + The test flow: + 1. Create ACL table and bind Ethernet124 + 2. Verify ACL table has been successfully added + 3. Create LAG + 4. Verify LAG has been successfully added + 5. Create LAG member Ethernet120 + 6. Verify LAG member has been successfully added + 7. Create LAG member Ethernet124 + 8. Verify LAG member hasn't been added because of active ACL binding + + Args: + testlog: test start/end log record injector + stage: ACL table stage (e.g., ingress/egress) + """ + try: + acllogger.info("Create ACL table: acl_table") + self.dvs_acl.create_acl_table( + table_name="acl_table", + table_type="L3", + ports=["Ethernet124"], + stage=stage + ) + self.dvs_acl.verify_acl_table_count(1) + + acllogger.info("Create LAG: PortChannel0001") + self.dvs_lag.create_port_channel("0001") + self.dvs_lag.get_and_verify_port_channel(1) + + acllogger.info("Create LAG member: Ethernet120") + self.dvs_lag.create_port_channel_member("0001", "Ethernet120") + self.dvs_lag.get_and_verify_port_channel_members(1) + + acllogger.info("Create LAG member: Ethernet124") + self.dvs_lag.create_port_channel_member("0001", "Ethernet124") + acllogger.info("Verify LAG member hasn't been created: Ethernet124") + self.dvs_lag.get_and_verify_port_channel_members(1) + finally: + acllogger.info("Remove LAG member: Ethernet124") + self.dvs_lag.remove_port_channel_member("0001", "Ethernet124") + self.dvs_lag.get_and_verify_port_channel_members(1) + + acllogger.info("Remove LAG member: Ethernet120") + self.dvs_lag.remove_port_channel_member("0001", "Ethernet120") + self.dvs_lag.get_and_verify_port_channel_members(0) + + acllogger.info("Remove LAG: PortChannel0001") + self.dvs_lag.remove_port_channel("0001") + self.dvs_lag.get_and_verify_port_channel(0) + + acllogger.info("Remove ACL table: acl_table") + self.dvs_acl.remove_acl_table("acl_table") + self.dvs_acl.verify_acl_table_count(0) + + class TestPortChannelAcl(object): def setup_db(self, dvs): self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) @@ -129,7 +207,7 @@ def check_asic_table_absent(self, dvs): # Second create ACL table def test_PortChannelAfterAcl(self, dvs): self.setup_db(dvs) - dvs.runcmd("crm config polling interval 1") + dvs.crm_poll_set("1") time.sleep(2) used_counter = dvs.getCrmCounterValue('ACL_STATS:INGRESS:LAG', 'crm_stats_acl_group_used') @@ -162,7 +240,7 @@ def test_PortChannelAfterAcl(self, dvs): new_new_used_counter = 0 assert new_used_counter - new_new_used_counter == 1 # slow down crm polling - dvs.runcmd("crm config polling interval 10000") + dvs.crm_poll_set("10000") # Frist create ACL table # Second create port channel diff --git a/tests/test_buffer_dynamic.py b/tests/test_buffer_dynamic.py index 4f97752355..a1ef4cf574 100644 --- a/tests/test_buffer_dynamic.py +++ b/tests/test_buffer_dynamic.py @@ -164,14 +164,14 @@ def test_changeSpeed(self, dvs, testlog): self.setup_db(dvs) # Startup interface - dvs.runcmd('config interface startup Ethernet0') + dvs.port_admin_set('Ethernet0', 'up') self.check_queues_after_port_startup(dvs) # Configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) # Change speed to speed1 and verify whether the profile has been updated - dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest1) + dvs.port_field_set("Ethernet0", "speed", self.speedToTest1) expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.originalCableLen) self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) @@ -185,7 +185,7 @@ def test_changeSpeed(self, dvs, testlog): self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4") # Change speed to speed2 and verify - dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest2) + dvs.port_field_set("Ethernet0", "speed", self.speedToTest2) expectedProfile = self.make_lossless_profile_name(self.speedToTest2, self.originalCableLen) # Re-add another lossless PG @@ -197,7 +197,7 @@ def test_changeSpeed(self, dvs, testlog): self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6") # Remove the lossless PG 3-4 and revert speed - dvs.runcmd("config interface speed Ethernet0 " + self.originalSpeed) + dvs.port_field_set("Ethernet0", "speed", self.originalSpeed) self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) @@ -210,7 +210,7 @@ def test_changeSpeed(self, dvs, testlog): self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:3-4") # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') + dvs.port_admin_set('Ethernet0', 'down') self.cleanup_db(dvs) @@ -219,7 +219,7 @@ def test_changeCableLen(self, dvs, testlog): self.setup_db(dvs) # Startup interface - dvs.runcmd('config interface startup Ethernet0') + dvs.port_admin_set('Ethernet0', 'up') # Configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) @@ -263,7 +263,7 @@ def test_changeCableLen(self, dvs, testlog): self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') + dvs.port_admin_set('Ethernet0', 'down') self.cleanup_db(dvs) @@ -271,7 +271,7 @@ def test_MultipleLosslessPg(self, dvs, testlog): self.setup_db(dvs) # Startup interface - dvs.runcmd('config interface startup Ethernet0') + dvs.port_admin_set('Ethernet0', 'up') # Configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) @@ -282,7 +282,7 @@ def test_MultipleLosslessPg(self, dvs, testlog): self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": expectedProfile}) # Change speed and check - dvs.runcmd("config interface speed Ethernet0 " + self.speedToTest1) + dvs.port_field_set("Ethernet0", "speed", self.speedToTest1) expectedProfile = self.make_lossless_profile_name(self.speedToTest1, self.originalCableLen) self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfile) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile}) @@ -299,7 +299,7 @@ def test_MultipleLosslessPg(self, dvs, testlog): # Revert the speed and cable length and check self.change_cable_length(self.originalCableLen) - dvs.runcmd("config interface speed Ethernet0 " + self.originalSpeed) + dvs.port_field_set("Ethernet0", "speed", self.originalSpeed) self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfile) self.asic_db.wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE", self.newProfileInAsicDb) expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) @@ -312,7 +312,7 @@ def test_MultipleLosslessPg(self, dvs, testlog): self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6') # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') + dvs.port_admin_set('Ethernet0', 'down') self.cleanup_db(dvs) @@ -320,7 +320,7 @@ def test_headroomOverride(self, dvs, testlog): self.setup_db(dvs) # Startup interface - dvs.runcmd('config interface startup Ethernet0') + dvs.port_admin_set('Ethernet0', 'up') # Configure static profile self.config_db.update_entry('BUFFER_PROFILE', 'test', @@ -397,7 +397,7 @@ def test_headroomOverride(self, dvs, testlog): self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') + dvs.port_admin_set('Ethernet0', 'down') self.cleanup_db(dvs) @@ -405,7 +405,7 @@ def test_mtuUpdate(self, dvs, testlog): self.setup_db(dvs) # Startup interface - dvs.runcmd('config interface startup Ethernet0') + dvs.port_admin_set('Ethernet0', 'up') test_mtu = '1500' default_mtu = '9100' @@ -413,7 +413,7 @@ def test_mtuUpdate(self, dvs, testlog): expectedProfileNormal = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) # update the mtu on the interface - dvs.runcmd("config interface mtu Ethernet0 {}".format(test_mtu)) + dvs.port_field_set("Ethernet0", "mtu", test_mtu) # configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) @@ -423,7 +423,7 @@ def test_mtuUpdate(self, dvs, testlog): self.check_new_profile_in_asic_db(dvs, expectedProfileMtu) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfileMtu}) - dvs.runcmd("config interface mtu Ethernet0 {}".format(default_mtu)) + dvs.port_field_set("Ethernet0", "mtu", default_mtu) self.app_db.wait_for_deleted_entry("BUFFER_PROFILE_TABLE", expectedProfileMtu) self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", expectedProfileNormal) @@ -433,7 +433,7 @@ def test_mtuUpdate(self, dvs, testlog): self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') + dvs.port_admin_set('Ethernet0', 'down') self.cleanup_db(dvs) @@ -441,7 +441,7 @@ def test_nonDefaultAlpha(self, dvs, testlog): self.setup_db(dvs) # Startup interface - dvs.runcmd('config interface startup Ethernet0') + dvs.port_admin_set('Ethernet0', 'up') test_dynamic_th_1 = '1' expectedProfile_th1 = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen, dynamic_th = test_dynamic_th_1) @@ -477,7 +477,7 @@ def test_nonDefaultAlpha(self, dvs, testlog): self.config_db.delete_entry('BUFFER_PROFILE', 'non-default-dynamic') # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') + dvs.port_admin_set('Ethernet0', 'down') self.cleanup_db(dvs) @@ -485,7 +485,7 @@ def test_sharedHeadroomPool(self, dvs, testlog): self.setup_db(dvs) # Startup interface - dvs.runcmd('config interface startup Ethernet0') + dvs.port_admin_set('Ethernet0', 'up') # configure lossless PG 3-4 on interface and start up the interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) @@ -574,10 +574,10 @@ def test_sharedHeadroomPool(self, dvs, testlog): # remove lossless PG 3-4 on interface self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') - dvs.runcmd('config interface shutdown Ethernet0') + dvs.port_admin_set('Ethernet0', 'down') # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') + dvs.port_admin_set('Ethernet0', 'down') self.cleanup_db(dvs) @@ -595,7 +595,7 @@ def test_shutdownPort(self, dvs, testlog): lossless_queue_zero_reference = 'egress_lossless_zero_profile' # Startup interface - dvs.runcmd('config interface startup Ethernet0') + dvs.port_admin_set('Ethernet0', 'up') # Configure lossless PG 3-4 on interface self.config_db.update_entry('BUFFER_PG', 'Ethernet0|3-4', {'profile': 'NULL'}) @@ -604,7 +604,7 @@ def test_shutdownPort(self, dvs, testlog): # Shutdown port and check whether zero profiles have been applied on queues and the PG 0 maximumQueues = int(self.bufferMaxParameter['max_queues']) - 1 - dvs.runcmd("config interface shutdown Ethernet0") + dvs.port_admin_set('Ethernet0', 'down') self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:0", {"profile": lossy_pg_zero_reference}) self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:0-2", {"profile": lossy_queue_zero_reference}) self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:3-4", {"profile": lossless_queue_zero_reference}) @@ -632,7 +632,7 @@ def test_shutdownPort(self, dvs, testlog): self.app_db.wait_for_deleted_entry("BUFFER_PG_TABLE", "Ethernet0:6") # Startup port and check whether all the PGs have been added - dvs.runcmd("config interface startup Ethernet0") + dvs.port_admin_set('Ethernet0', 'up') self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:0", {"profile": lossy_pg_reference_appl_db}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:1", {"profile": lossy_pg_reference_appl_db}) self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:3-4", {"profile": expectedProfile}) @@ -645,7 +645,7 @@ def test_shutdownPort(self, dvs, testlog): self.app_db.wait_for_deleted_entry("BUFFER_QUEUE_TABLE", "Ethernet0:9-{}".format(maximumQueues)) # Shutdown the port again to verify flow to remove buffer objects from an admin down port - dvs.runcmd("config interface shutdown Ethernet0") + dvs.port_admin_set('Ethernet0', 'down') # First, check whether the objects have been correctly handled self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:0", {"profile": lossy_pg_zero_reference}) self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:0-2", {"profile": lossy_queue_zero_reference}) @@ -671,7 +671,7 @@ def test_shutdownPort(self, dvs, testlog): self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:7-{}".format(maximumQueues), {"profile": lossy_queue_zero_reference}) # Startup again - dvs.runcmd("config interface startup Ethernet0") + dvs.port_admin_set('Ethernet0', 'up') self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:0-2", {"profile": lossy_queue_reference_appl_db}) self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:3-4", {"profile": lossless_queue_reference_appl_db}) self.app_db.wait_for_field_match("BUFFER_QUEUE_TABLE", "Ethernet0:5-6", {"profile": lossy_queue_reference_appl_db}) @@ -683,7 +683,7 @@ def test_shutdownPort(self, dvs, testlog): self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|3-4') # Shutdown interface - dvs.runcmd("config interface shutdown Ethernet0") + dvs.port_admin_set('Ethernet0', 'down') self.cleanup_db(dvs) @@ -698,14 +698,14 @@ def test_autoNegPort(self, dvs, testlog): maximum_advertised_speed = '25000' # Startup interfaces - dvs.runcmd('config interface startup Ethernet0') + dvs.port_admin_set('Ethernet0', 'up') # 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)) + dvs.port_field_set('Ethernet0','autoneg', 'on') + dvs.port_field_set('Ethernet0','adv_speeds', advertised_speeds) # Check the buffer profile. The maximum_advertised_speed should be used expectedProfile = self.make_lossless_profile_name(maximum_advertised_speed, self.originalCableLen) @@ -719,7 +719,7 @@ def test_autoNegPort(self, dvs, testlog): self.app_db.wait_for_field_match("BUFFER_PG_TABLE", "Ethernet0:6", {"profile": expectedProfile}) # Disable port auto negotiation - dvs.runcmd('config interface autoneg Ethernet0 disabled') + dvs.port_field_set('Ethernet0','autoneg', 'off') # Check the buffer profile. The configured speed should be used expectedProfile = self.make_lossless_profile_name(self.originalSpeed, self.originalCableLen) @@ -733,10 +733,11 @@ def test_autoNegPort(self, dvs, testlog): self.config_db.delete_entry('BUFFER_PG', 'Ethernet0|6') # Shutdown interface - dvs.runcmd('config interface shutdown Ethernet0') + dvs.port_admin_set('Ethernet0', 'down') self.cleanup_db(dvs) + @pytest.mark.skip(reason="Failing. Under investigation") def test_removeBufferPool(self, dvs, testlog): self.setup_db(dvs) # Initialize additional databases that are used by this test only diff --git a/tests/test_buffer_traditional.py b/tests/test_buffer_traditional.py index 3defae0c80..3d2285fd7b 100644 --- a/tests/test_buffer_traditional.py +++ b/tests/test_buffer_traditional.py @@ -3,7 +3,7 @@ class TestBuffer(object): - LOSSLESS_PGS = [3, 4] + lossless_pgs = [] INTF = "Ethernet0" def setup_db(self, dvs): @@ -15,6 +15,10 @@ def setup_db(self, dvs): # enable PG watermark self.set_pg_wm_status('enable') + def get_pfc_enable_queues(self): + qos_map = self.config_db.get_entry("PORT_QOS_MAP", self.INTF) + return qos_map['pfc_enable'].split(',') + def get_pg_oid(self, pg): fvs = dict() fvs = self.counter_db.get_entry("COUNTERS_PG_NAME_MAP", "") @@ -51,19 +55,32 @@ def get_asic_buf_pg_profiles(self): 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): + def change_cable_len(self, cable_len, extra_port=None): fvs = dict() fvs[self.INTF] = cable_len + if extra_port: + fvs[extra_port] = cable_len self.config_db.update_entry("CABLE_LENGTH", "AZURE", fvs) + def set_port_qos_table(self, port, pfc_enable_flag): + fvs=dict() + fvs['pfc_enable'] = pfc_enable_flag + self.config_db.update_entry("PORT_QOS_MAP", port, fvs) + self.lossless_pgs = pfc_enable_flag.split(',') + + def get_pg_name_map(self): + 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) + return pg_name_map + @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) + self.set_port_qos_table(self.INTF, '2,3,4,6') + pg_name_map = self.get_pg_name_map() yield pg_name_map finally: self.teardown() @@ -91,7 +108,7 @@ def test_zero_cable_len_profile_update(self, dvs, setup_teardown_test): test_speed = "100000" test_cable_len = "0m" - dvs.runcmd("config interface startup {}".format(self.INTF)) + dvs.port_admin_set(self.INTF, "up") # Make sure the buffer PG has been created orig_lossless_profile = "pg_lossless_{}_{}_profile".format(orig_speed, cable_len_before_test) self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", orig_lossless_profile) @@ -113,13 +130,14 @@ def test_zero_cable_len_profile_update(self, dvs, setup_teardown_test): self.change_cable_len(test_cable_len) # change intf speed to 'test_speed' - dvs.runcmd("config interface speed {} {}".format(self.INTF, test_speed)) + dvs.port_field_set(self.INTF, "speed", 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 - self.app_db.wait_for_field_match("BUFFER_PG_TABLE", self.INTF + ":3-4", {"profile": orig_lossless_profile}) + for pg in self.lossless_pgs: + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", self.INTF + ":" + pg, {"profile": 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] @@ -129,7 +147,7 @@ def test_zero_cable_len_profile_update(self, dvs, setup_teardown_test): self.change_cable_len(cable_len_before_test) # change intf speed to 'test_speed' - dvs.runcmd("config interface speed {} {}".format(self.INTF, test_speed)) + dvs.port_field_set(self.INTF, "speed", 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) @@ -150,5 +168,79 @@ def test_zero_cable_len_profile_update(self, dvs, setup_teardown_test): 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)) + dvs.port_field_set(self.INTF, "speed", orig_speed) + dvs.port_admin_set(self.INTF, "down") + + # To verify the BUFFER_PG is not hardcoded to 3,4 + # buffermgrd will read 'pfc_enable' entry and apply lossless profile to that queue + def test_buffer_pg_update(self, dvs, setup_teardown_test): + self.pg_name_map = setup_teardown_test + orig_cable_len = None + orig_speed = None + test_speed = None + extra_port = "Ethernet4" + try: + # Retrieve cable len + fvs_cable_len = self.config_db.get_entry("CABLE_LENGTH", "AZURE") + orig_cable_len = fvs_cable_len[self.INTF] + if orig_cable_len == "0m": + cable_len_for_test = "300m" + fvs_cable_len[self.INTF] = cable_len_for_test + fvs_cable_len[extra_port] = cable_len_for_test + + self.config_db.update_entry("CABLE_LENGTH", "AZURE", fvs_cable_len) + else: + cable_len_for_test = orig_cable_len + # Ethernet4 is set to up, while no 'pfc_enable' available. `Ethernet0` is not supposed to be impacted + dvs.port_admin_set(extra_port, "up") + + dvs.port_admin_set(self.INTF, "up") + + # Retrieve port speed + fvs_port = self.config_db.get_entry("PORT", self.INTF) + orig_speed = fvs_port["speed"] + + # Make sure the buffer PG has been created + orig_lossless_profile = "pg_lossless_{}_{}_profile".format(orig_speed, cable_len_for_test) + self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", orig_lossless_profile) + self.orig_profiles = self.get_asic_buf_profile() + + # get the orig buf profiles attached to the pgs + self.get_asic_buf_pg_profiles() + + # Update port speed + if orig_speed == "100000": + test_speed = "40000" + elif orig_speed == "40000": + test_speed = "100000" + # change intf speed to 'test_speed' + dvs.port_field_set(self.INTF, "speed", test_speed) + dvs.port_field_set(extra_port, "speed", test_speed) + # Verify new profile is generated + new_lossless_profile = "pg_lossless_{}_{}_profile".format(test_speed, cable_len_for_test) + self.app_db.wait_for_entry("BUFFER_PROFILE_TABLE", new_lossless_profile) + + # Verify BUFFER_PG is updated + for pg in self.lossless_pgs: + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", self.INTF + ":" + pg, {"profile": new_lossless_profile}) + + fvs_negative = {} + for pg in self.pg_name_map: + # verify that buffer pgs do not point to the old profile since we cannot deduce the new profile oid + fvs_negative["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_negative) + + # Add pfc_enable field for extra port + self.set_port_qos_table(extra_port, '2,3,4,6') + time.sleep(1) + # Verify BUFFER_PG is updated when pfc_enable is available + for pg in self.lossless_pgs: + self.app_db.wait_for_field_match("BUFFER_PG_TABLE", extra_port + ":" + pg, {"profile": new_lossless_profile}) + finally: + if orig_cable_len: + self.change_cable_len(orig_cable_len, extra_port) + if orig_speed: + dvs.port_field_set(self.INTF, "speed", orig_speed) + dvs.port_field_set(extra_port, "speed", orig_speed) + dvs.port_admin_set(self.INTF, "down") + dvs.port_admin_set(extra_port, "down") diff --git a/tests/test_crm.py b/tests/test_crm.py index 200b15cf79..31d0b57ae5 100644 --- a/tests/test_crm.py +++ b/tests/test_crm.py @@ -17,29 +17,16 @@ def getCrmCounterValue(dvs, key, counter): return 0 -def getCrmConfigValue(dvs, key, counter): - - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - crm_stats_table = swsscommon.Table(config_db, 'CRM') - - for k in crm_stats_table.get(key)[1]: - if k[0] == counter: - return int(k[1]) - -def getCrmConfigStr(dvs, key, counter): - - config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) - crm_stats_table = swsscommon.Table(config_db, 'CRM') - - for k in crm_stats_table.get(key)[1]: - if k[0] == counter: - return k[1] - return "" - def check_syslog(dvs, marker, err_log, expected_cnt): (exitcode, num) = dvs.runcmd(['sh', '-c', "awk \'/%s/,ENDFILE {print;}\' /var/log/syslog | grep \"%s\" | wc -l" % (marker, err_log)]) assert num.strip() >= str(expected_cnt) +def crm_update(dvs, field, value): + cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(cfg_db, "CRM") + fvs = swsscommon.FieldValuePairs([(field, value)]) + tbl.set("Config", fvs) + time.sleep(1) class TestCrm(object): def test_CrmFdbEntry(self, dvs, testlog): @@ -48,7 +35,7 @@ def test_CrmFdbEntry(self, dvs, testlog): # configured, server 2 will send packet which can switch to learn another # mac and fail the test. dvs.servers[2].runcmd("sysctl -w net.ipv6.conf.eth0.disable_ipv6=1") - dvs.runcmd("crm config polling interval 1") + crm_update(dvs, "polling_interval", "1") dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') @@ -99,9 +86,9 @@ def test_CrmFdbEntry(self, dvs, testlog): assert new_avail_counter == avail_counter marker = dvs.add_log_marker() - dvs.runcmd("crm config polling interval 2") - dvs.runcmd("crm config thresholds fdb high 90") - dvs.runcmd("crm config thresholds fdb type free") + crm_update(dvs, "polling_interval", "2") + crm_update(dvs, "fdb_entry_high_threshold", "90") + crm_update(dvs, "fdb_entry_threshold_type", "free") time.sleep(2) check_syslog(dvs, marker, "FDB_ENTRY THRESHOLD_EXCEEDED for TH_FREE", 1) @@ -115,9 +102,9 @@ def test_CrmIpv4Route(self, dvs, testlog): fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) intf_tbl.set("Ethernet0", fvs) intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - dvs.runcmd("config interface startup Ethernet0") + dvs.port_admin_set("Ethernet0", "up") - dvs.runcmd("crm config polling interval 1") + crm_update(dvs, "polling_interval", "1") dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '1000') @@ -162,9 +149,9 @@ def test_CrmIpv4Route(self, dvs, testlog): assert new_avail_counter == avail_counter marker = dvs.add_log_marker() - dvs.runcmd("crm config polling interval 2") - dvs.runcmd("crm config thresholds ipv4 route high 90") - dvs.runcmd("crm config thresholds ipv4 route type free") + crm_update(dvs, "polling_interval", "2") + crm_update(dvs, "ipv4_route_high_threshold", "90") + crm_update(dvs, "ipv4_route_threshold_type", "free") time.sleep(2) check_syslog(dvs, marker, "IPV4_ROUTE THRESHOLD_EXCEEDED for TH_FREE",1) @@ -182,12 +169,12 @@ def test_CrmIpv6Route(self, dvs, testlog): fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) intf_tbl.set("Ethernet0", fvs) intf_tbl.set("Ethernet0|fc00::1/126", fvs) - dvs.runcmd("config interface startup Ethernet0") + dvs.port_admin_set("Ethernet0", "up") dvs.servers[0].runcmd("ifconfig eth0 inet6 add fc00::2/126") dvs.servers[0].runcmd("ip -6 route add default via fc00::1") - dvs.runcmd("crm config polling interval 1") + crm_update(dvs, "polling_interval", "1") dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '1000') @@ -232,9 +219,9 @@ def test_CrmIpv6Route(self, dvs, testlog): assert new_avail_counter == avail_counter marker = dvs.add_log_marker() - dvs.runcmd("crm config polling interval 2") - dvs.runcmd("crm config thresholds ipv6 route high 90") - dvs.runcmd("crm config thresholds ipv6 route type free") + crm_update(dvs, "polling_interval", "2") + crm_update(dvs, "ipv6_route_high_threshold", "90") + crm_update(dvs, "ipv6_route_threshold_type", "free") time.sleep(2) check_syslog(dvs, marker, "IPV6_ROUTE THRESHOLD_EXCEEDED for TH_FREE",1) @@ -248,9 +235,8 @@ def test_CrmIpv4Nexthop(self, dvs, testlog): fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) intf_tbl.set("Ethernet0", fvs) - dvs.runcmd("config interface startup Ethernet0") - - dvs.runcmd("crm config polling interval 1") + dvs.port_admin_set("Ethernet0", "up") + crm_update(dvs, "polling_interval", "1") dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '1000') @@ -287,9 +273,9 @@ def test_CrmIpv4Nexthop(self, dvs, testlog): assert new_avail_counter == avail_counter marker = dvs.add_log_marker() - dvs.runcmd("crm config polling interval 2") - dvs.runcmd("crm config thresholds ipv4 nexthop high 90") - dvs.runcmd("crm config thresholds ipv4 nexthop type free") + crm_update(dvs, "polling_interval", "2") + crm_update(dvs, "ipv4_nexthop_high_threshold", "90") + crm_update(dvs, "ipv4_nexthop_threshold_type", "free") time.sleep(2) check_syslog(dvs, marker, "IPV4_NEXTHOP THRESHOLD_EXCEEDED for TH_FREE",1) @@ -307,9 +293,9 @@ def test_CrmIpv6Nexthop(self, dvs, testlog): fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) intf_tbl.set("Ethernet0", fvs) intf_tbl.set("Ethernet0|fc00::1/126", fvs) - dvs.runcmd("config interface startup Ethernet0") + dvs.port_admin_set("Ethernet0", "up") - dvs.runcmd("crm config polling interval 1") + crm_update(dvs, "polling_interval", "1") dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '1000') @@ -346,9 +332,9 @@ def test_CrmIpv6Nexthop(self, dvs, testlog): assert new_avail_counter == avail_counter marker = dvs.add_log_marker() - dvs.runcmd("crm config polling interval 2") - dvs.runcmd("crm config thresholds ipv6 nexthop high 90") - dvs.runcmd("crm config thresholds ipv6 nexthop type free") + crm_update(dvs, "polling_interval", "2") + crm_update(dvs, "ipv6_nexthop_high_threshold", "90") + crm_update(dvs, "ipv6_nexthop_threshold_type", "free") time.sleep(2) check_syslog(dvs, marker, "IPV6_NEXTHOP THRESHOLD_EXCEEDED for TH_FREE",1) @@ -362,9 +348,9 @@ def test_CrmIpv4Neighbor(self, dvs, testlog): fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) intf_tbl.set("Ethernet0", fvs) intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) - dvs.runcmd("config interface startup Ethernet0") + dvs.port_admin_set("Ethernet0", "up") - dvs.runcmd("crm config polling interval 1") + crm_update(dvs, "polling_interval", "1") dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '1000') @@ -401,9 +387,9 @@ def test_CrmIpv4Neighbor(self, dvs, testlog): assert new_avail_counter == avail_counter marker = dvs.add_log_marker() - dvs.runcmd("crm config polling interval 2") - dvs.runcmd("crm config thresholds ipv4 neighbor high 90") - dvs.runcmd("crm config thresholds ipv4 neighbor type free") + crm_update(dvs, "polling_interval", "2") + crm_update(dvs, "ipv4_neighbor_high_threshold", "90") + crm_update(dvs, "ipv4_neighbor_threshold_type", "free") time.sleep(2) check_syslog(dvs, marker, "IPV4_NEIGHBOR THRESHOLD_EXCEEDED for TH_FREE",1) @@ -421,9 +407,9 @@ def test_CrmIpv6Neighbor(self, dvs, testlog): fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) intf_tbl.set("Ethernet0", fvs) intf_tbl.set("Ethernet0|fc00::1/126", fvs) - dvs.runcmd("config interface startup Ethernet0") + dvs.port_admin_set("Ethernet0", "up") - dvs.runcmd("crm config polling interval 1") + crm_update(dvs, "polling_interval", "1") dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '1000') @@ -460,9 +446,9 @@ def test_CrmIpv6Neighbor(self, dvs, testlog): assert new_avail_counter == avail_counter marker = dvs.add_log_marker() - dvs.runcmd("crm config polling interval 2") - dvs.runcmd("crm config thresholds ipv6 neighbor high 90") - dvs.runcmd("crm config thresholds ipv6 neighbor type free") + crm_update(dvs, "polling_interval", "2") + crm_update(dvs, "ipv6_neighbor_high_threshold", "90") + crm_update(dvs, "ipv6_neighbor_threshold_type", "free") time.sleep(2) check_syslog(dvs, marker, "IPV6_NEIGHBOR THRESHOLD_EXCEEDED for TH_FREE",1) @@ -478,10 +464,10 @@ def test_CrmNexthopGroup(self, dvs, testlog): intf_tbl.set("Ethernet4", fvs) intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) - dvs.runcmd("config interface startup Ethernet0") - dvs.runcmd("config interface startup Ethernet4") + dvs.port_admin_set("Ethernet0", "up") + dvs.port_admin_set("Ethernet4", "up") - dvs.runcmd("crm config polling interval 1") + crm_update(dvs, "polling_interval", "1") dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '1000') @@ -528,9 +514,9 @@ def test_CrmNexthopGroup(self, dvs, testlog): assert new_avail_counter == avail_counter marker = dvs.add_log_marker() - dvs.runcmd("crm config polling interval 2") - dvs.runcmd("crm config thresholds nexthop group member high 90") - dvs.runcmd("crm config thresholds nexthop group object type free") + crm_update(dvs, "polling_interval", "2") + crm_update(dvs, "nexthop_group_high_threshold", "90") + crm_update(dvs, "nexthop_group_threshold_type", "free") time.sleep(2) check_syslog(dvs, marker, "NEXTHOP_GROUP THRESHOLD_EXCEEDED for TH_FREE",1) @@ -553,10 +539,10 @@ def test_CrmNexthopGroupMember(self, dvs, testlog): intf_tbl.set("Ethernet4", fvs) intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) - dvs.runcmd("config interface startup Ethernet0") - dvs.runcmd("config interface startup Ethernet4") + dvs.port_admin_set("Ethernet0", "up") + dvs.port_admin_set("Ethernet4", "up") - dvs.runcmd("crm config polling interval 1") + crm_update(dvs, "polling_interval", "1") dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '1000') @@ -603,9 +589,9 @@ def test_CrmNexthopGroupMember(self, dvs, testlog): assert new_avail_counter == avail_counter marker = dvs.add_log_marker() - dvs.runcmd("crm config polling interval 2") - dvs.runcmd("crm config thresholds nexthop group member high 90") - dvs.runcmd("crm config thresholds nexthop group member type free") + crm_update(dvs, "polling_interval", "2") + crm_update(dvs, "nexthop_group_member_high_threshold", "90") + crm_update(dvs, "nexthop_group_member_threshold_type", "free") time.sleep(2) check_syslog(dvs, marker, "NEXTHOP_GROUP_MEMBER THRESHOLD_EXCEEDED for TH_FREE",1) @@ -618,7 +604,7 @@ def test_CrmAcl(self, dvs, testlog): db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - dvs.runcmd("crm config polling interval 1") + crm_update(dvs, "polling_interval", "1") time.sleep(1) bind_ports = ["Ethernet0", "Ethernet4"] @@ -698,7 +684,7 @@ def test_CrmAclGroup(self, dvs, testlog): db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - dvs.runcmd("crm config polling interval 1") + crm_update(dvs, "polling_interval", "1") bind_ports = ["Ethernet0", "Ethernet4", "Ethernet8"] # create ACL table @@ -734,262 +720,59 @@ def test_CrmDnatEntry(self, dvs, testlog): assert used_counter == 0 assert avail_counter != 0 -# commented ipmc test case till vslib is updated -# def test_CrmIpmcEntry(self, dvs, testlog): -# -# # get counters -# used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipmc_entry_used') -# avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipmc_entry_available') -# assert used_counter == 0 -# assert avail_counter != 0 - - def test_Configure(self, dvs, testlog): - - #polling interval - dvs.runcmd("crm config polling interval 10") - time.sleep(2) - polling_interval = getCrmConfigValue(dvs, 'Config', 'polling_interval') - assert polling_interval == 10 - - def test_Configure_ipv4_route(self, dvs, testlog): - - #ipv4 route low/high threshold/type - dvs.runcmd("crm config thresholds ipv4 route low 50") - dvs.runcmd("crm config thresholds ipv4 route high 90") - dvs.runcmd("crm config thresholds ipv4 route type percentage") - - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv4_route_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv4_route_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv4_route_threshold_type') - assert threshold_type == 'percentage' - - def test_Configure_ipv6_route(self, dvs, testlog): - - #ipv6 route low/high threshold/type - dvs.runcmd("crm config thresholds ipv6 route low 50") - dvs.runcmd("crm config thresholds ipv6 route high 90") - dvs.runcmd("crm config thresholds ipv6 route type used") + def test_CrmResetThresholdExceedCount(self, dvs, testlog): - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv6_route_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv6_route_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv6_route_threshold_type') - assert threshold_type == 'used' - - def test_Configure_ipv4_nexthop(self, dvs, testlog): - - #ipv4 nexthop low/high threshold/type - dvs.runcmd("crm config thresholds ipv4 nexthop low 50") - dvs.runcmd("crm config thresholds ipv4 nexthop high 90") - dvs.runcmd("crm config thresholds ipv4 nexthop type 'percentage'") - - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv4_nexthop_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv4_nexthop_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv4_nexthop_threshold_type') - assert threshold_type == 'percentage' - - def test_Configure_ipv6_nexthop(self, dvs, testlog): - - #ipv6 nexthop low/high threshold/type - dvs.runcmd("crm config thresholds ipv6 nexthop low 50") - dvs.runcmd("crm config thresholds ipv6 nexthop high 90") - dvs.runcmd("crm config thresholds ipv6 nexthop type free") - - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv6_nexthop_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv6_nexthop_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv6_nexthop_threshold_type') - assert threshold_type == 'free' - - def test_Configure_ipv4_neighbor(self, dvs, testlog): - - #ipv4 neighbor low/high threshold/type - dvs.runcmd("crm config thresholds ipv4 neighbor low 50") - dvs.runcmd("crm config thresholds ipv4 neighbor high 90") - dvs.runcmd("crm config thresholds ipv4 neighbor type percentage") - - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv4_neighbor_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv4_neighbor_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv4_neighbor_threshold_type') - assert threshold_type == 'percentage' - - def test_Configure_ipv6_neighbor(self, dvs, testlog): - - #ipv6 neighbor low/high threshold/type - dvs.runcmd("crm config thresholds ipv6 neighbor low 50") - dvs.runcmd("crm config thresholds ipv6 neighbor high 90") - dvs.runcmd("crm config thresholds ipv6 neighbor type used") - - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'ipv6_neighbor_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'ipv6_neighbor_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'ipv6_neighbor_threshold_type') - assert threshold_type == 'used' - - def test_Configure_group_member(self, dvs, testlog): - - #nexthop group member low/high threshold/type - dvs.runcmd("crm config thresholds nexthop group member low 50") - dvs.runcmd("crm config thresholds nexthop group member high 90") - dvs.runcmd("crm config thresholds nexthop group member type percentage") - - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'nexthop_group_member_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'nexthop_group_member_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'nexthop_group_member_threshold_type') - assert threshold_type == 'percentage' - - def test_Configure_group_object(self, dvs, testlog): - - #nexthop group object low/high threshold/type - dvs.runcmd("crm config thresholds nexthop group object low 50") - dvs.runcmd("crm config thresholds nexthop group object high 90") - dvs.runcmd("crm config thresholds nexthop group object type free") - - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'nexthop_group_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'nexthop_group_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'nexthop_group_threshold_type') - assert threshold_type == 'free' - - def test_Configure_acl_table(self, dvs, testlog): - - #thresholds acl table low/high threshold/type - dvs.runcmd("crm config thresholds acl table low 50") - dvs.runcmd("crm config thresholds acl table high 90") - dvs.runcmd("crm config thresholds acl table type percentage") - - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'acl_table_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'acl_table_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'acl_table_threshold_type') - assert threshold_type == 'percentage' - - def test_Configure_acl_group(self, dvs, testlog): - - #thresholds acl group low/high threshold/type - dvs.runcmd("crm config thresholds acl group low 50") - dvs.runcmd("crm config thresholds acl group high 90") - dvs.runcmd("crm config thresholds acl group type used") - - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'acl_group_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'acl_group_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'acl_group_threshold_type') - assert threshold_type == 'used' - - def test_Configure_acl_group_entry(self, dvs, testlog): - - #thresholds acl group entry low/high threshold/type - dvs.runcmd("crm config thresholds acl group entry low 50") - dvs.runcmd("crm config thresholds acl group entry high 90") - dvs.runcmd("crm config thresholds acl group entry type percentage") - - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'acl_entry_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'acl_entry_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'acl_entry_threshold_type') - assert threshold_type == 'percentage' - - def test_Configure_acl_group_counter(self, dvs, testlog): + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + intf_tbl = swsscommon.Table(config_db, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL","NULL")]) + intf_tbl.set("Ethernet0", fvs) + intf_tbl.set("Ethernet0|10.0.0.0/31", fvs) + dvs.port_admin_set("Ethernet0", "up") - #thresholds acl group counter low/high threshold/type - dvs.runcmd("crm config thresholds acl group counter low 50") - dvs.runcmd("crm config thresholds acl group counter high 90") - dvs.runcmd("crm config thresholds acl group counter type free") + crm_update(dvs, "polling_interval", "1") - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'acl_counter_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'acl_counter_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'acl_counter_threshold_type') - assert threshold_type == 'free' + # add static neighbor + dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") - def test_Configure_fdb(self, dvs, testlog): + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1"), ("ifname", "Ethernet0")]) - #thresholds fdb low/high threshold/type - dvs.runcmd("crm config thresholds fdb low 50") - dvs.runcmd("crm config thresholds fdb high 90") - dvs.runcmd("crm config thresholds fdb type percentage") + # add route to make sure used count at least 1 and update available counter + ps.set("2.2.2.0/24", fvs) + dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '1000') time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'fdb_entry_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'fdb_entry_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'fdb_entry_threshold_type') - assert threshold_type == 'percentage' - - def test_Configure_snat(self, dvs, testlog): - #thresholds snat low/high threshold/type - dvs.runcmd("crm config thresholds snat low 50") - dvs.runcmd("crm config thresholds snat high 90") - dvs.runcmd("crm config thresholds snat type percentage") + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') - time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'snat_entry_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'snat_entry_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'snat_entry_threshold_type') - assert threshold_type == 'percentage' + crm_update(dvs, "ipv4_route_low_threshold", "0") - def test_Configure_dnat(self, dvs, testlog): + # for changing to free type will rest crm threshold exceed count, previous type can not be same free type + crm_update(dvs, "ipv4_route_threshold_type", "percentage") - #thresholds dnat low/high threshold/type - dvs.runcmd("crm config thresholds dnat low 50") - dvs.runcmd("crm config thresholds dnat high 90") - dvs.runcmd("crm config thresholds dnat type percentage") + # trigger exceed high threshold of free type + marker = dvs.add_log_marker() + crm_update(dvs, "ipv4_route_high_threshold", str(avail_counter)) + crm_update(dvs, "ipv4_route_threshold_type", "free") + time.sleep(20) + check_syslog(dvs, marker, "IPV4_ROUTE THRESHOLD_EXCEEDED for TH_FREE", 1) + # crm threshold exceed count will be reset when threshold type changed + marker = dvs.add_log_marker() + crm_update(dvs, "ipv4_route_high_threshold", str(used_counter)) + crm_update(dvs, "ipv4_route_threshold_type", "used") time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'dnat_entry_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'dnat_entry_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'dnat_entry_threshold_type') - assert threshold_type == 'percentage' - - def test_Configure_ipmc(self, dvs, testlog): + check_syslog(dvs, marker, "IPV4_ROUTE THRESHOLD_EXCEEDED for TH_USED", 1) - #thresholds ipmc low/high threshold/type - dvs.runcmd("crm config thresholds ipmc low 50") - dvs.runcmd("crm config thresholds ipmc high 90") - dvs.runcmd("crm config thresholds ipmc type percentage") + # remove route + ps._del("2.2.2.0/24") + dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + intf_tbl._del("Ethernet0|10.0.0.0/31") time.sleep(2) - threshold_low = getCrmConfigValue(dvs, 'Config', 'ipmc_entry_low_threshold') - assert threshold_low == 50 - threshold_high = getCrmConfigValue(dvs, 'Config', 'ipmc_entry_high_threshold') - assert threshold_high == 90 - threshold_type = getCrmConfigStr(dvs, 'Config', 'ipmc_entry_threshold_type') - assert threshold_type == 'percentage' # 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_drop_counters.py b/tests/test_drop_counters.py index b003876f1a..cd089be917 100644 --- a/tests/test_drop_counters.py +++ b/tests/test_drop_counters.py @@ -11,7 +11,7 @@ # Debug Counter Table DEBUG_COUNTER_TABLE = 'DEBUG_COUNTER' -DROP_REASON_TABLE = 'DEBUG_COUNTER_DROP_REASON' +DROP_REASON_TABLE = 'DEBUG_COUNTER_DROP_REASON' # Debug Counter Capability Table CAPABILITIES_TABLE = 'DEBUG_COUNTER_CAPABILITIES' @@ -54,7 +54,11 @@ EXPECTED_ASIC_FIELDS = [ASIC_COUNTER_TYPE_FIELD, ASIC_COUNTER_INGRESS_REASON_LIST_FIELD, ASIC_COUNTER_EGRESS_REASON_LIST_FIELD] EXPECTED_NUM_ASIC_FIELDS = 2 +# port to be add and removed +PORT = "Ethernet0" +PORT_TABLE_NAME = "PORT" +@pytest.mark.usefixtures('dvs_port_manager') # FIXME: It is really annoying to have to re-run tests due to inconsistent timing, should # implement some sort of polling interface for checking ASIC/flex counter tables after # applying changes to config DB @@ -64,6 +68,7 @@ def setup_db(self, dvs): self.config_db = swsscommon.DBConnector(4, dvs.redis_sock, 0) self.flex_db = swsscommon.DBConnector(5, dvs.redis_sock, 0) self.state_db = swsscommon.DBConnector(6, dvs.redis_sock, 0) + self.counters_db = swsscommon.DBConnector(2, dvs.redis_sock, 0) def genericGetAndAssert(self, table, key): status, fields = table.get(key) @@ -654,6 +659,79 @@ def test_removeInvalidDropReason(self, dvs, testlog): # Cleanup for the next test. self.delete_drop_counter(name) self.remove_drop_reason(name, reason1) + + def getPortOid(self, dvs, port_name): + port_name_map = swsscommon.Table(self.counters_db, "COUNTERS_PORT_NAME_MAP") + status, returned_value = port_name_map.hget("", port_name); + assert status == True + return returned_value + + def test_add_remove_port(self, dvs, testlog): + """ + This test verifies that debug counters are removed when we remove a port + and debug counters are added each time we add ports (if debug counter is enabled) + """ + self.setup_db(dvs) + + # save port info + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + tbl = swsscommon.Table(cdb, PORT_TABLE_NAME) + (status, fvs) = tbl.get(PORT) + assert status == True + + # get counter oid + oid = self.getPortOid(dvs, PORT) + + # verifies debug coutner dont exist for port + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + status, fields = flex_counter_table.get(oid) + assert len(fields) == 0 + + # add debug counters + name1 = 'DEBUG_0' + reason1 = 'L3_ANY' + name2 = 'DEBUG_1' + reason2 = 'L2_ANY' + + self.create_drop_counter(name1, PORT_INGRESS_DROPS) + self.add_drop_reason(name1, reason1) + + self.create_drop_counter(name2, PORT_EGRESS_DROPS) + self.add_drop_reason(name2, reason2) + time.sleep(3) + + # verifies debug counter exist for port + flex_counter_table = swsscommon.Table(self.flex_db, FLEX_COUNTER_TABLE) + status, fields = flex_counter_table.get(oid) + assert status == True + assert len(fields) == 1 + + # remove port and wait until it was removed from ASIC DB + self.dvs_port.remove_port(PORT) + dvs.get_asic_db().wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_PORT", oid) + + # verify that debug counter were removed + status, fields = flex_counter_table.get(oid) + assert len(fields) == 0 + + # add port and wait until the port is added on asic db + num_of_keys_without_port = len(dvs.get_asic_db().get_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT")) + tbl.set(PORT, fvs) + dvs.get_asic_db().wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT", num_of_keys_without_port + 1) + dvs.get_counters_db().wait_for_fields("COUNTERS_PORT_NAME_MAP", "", [PORT]) + + # verifies that debug counters were added for port + oid = self.getPortOid(dvs, PORT) + status, fields = flex_counter_table.get(oid) + assert status == True + assert len(fields) == 1 + + # Cleanup for the next test. + self.delete_drop_counter(name1) + self.remove_drop_reason(name1, reason1) + + self.delete_drop_counter(name2) + self.remove_drop_reason(name2, reason2) def test_createAndDeleteMultipleCounters(self, dvs, testlog): """ diff --git a/tests/test_evpn_fdb.py b/tests/test_evpn_fdb.py index 31d75535c7..3c9a217747 100644 --- a/tests/test_evpn_fdb.py +++ b/tests/test_evpn_fdb.py @@ -51,7 +51,7 @@ def test_evpnFdb(dvs, testlog): helper = VxlanEvpnHelper() dvs.setup_db() - dvs.runcmd("sonic-clear fdb all") + dvs.clear_fdb() time.sleep(2) #Find switch_id @@ -62,7 +62,6 @@ def test_evpnFdb(dvs, testlog): # create vlan print("Creating Vlan3") - #dvs.runcmd("config vlan add 3") dvs.create_vlan("3") time.sleep(2) @@ -79,7 +78,6 @@ def test_evpnFdb(dvs, testlog): vm_before = helper.how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") print("Making Ethernet0 as a member of Vlan3") - #dvs.runcmd("config vlan member add 3 Ethernet0") dvs.create_vlan_member("3", "Ethernet0") time.sleep(2) diff --git a/tests/test_evpn_fdb_p2mp.py b/tests/test_evpn_fdb_p2mp.py index 7929bc862f..8c1cfbf1d6 100644 --- a/tests/test_evpn_fdb_p2mp.py +++ b/tests/test_evpn_fdb_p2mp.py @@ -54,7 +54,7 @@ def test_evpnFdbP2MP(dvs, testlog): helper = VxlanEvpnHelper() dvs.setup_db() - dvs.runcmd("sonic-clear fdb all") + dvs.clear_fdb() time.sleep(2) #Find switch_id diff --git a/tests/test_fdb.py b/tests/test_fdb.py index 9893a4e3b0..2f9067a599 100644 --- a/tests/test_fdb.py +++ b/tests/test_fdb.py @@ -31,9 +31,10 @@ class TestFdb(object): def test_FdbWarmRestartNotifications(self, dvs, testlog): dvs.setup_db() - dvs.runcmd("sonic-clear fdb all") + dvs.clear_fdb() - dvs.runcmd("crm config polling interval 1") + dvs.crm_poll_set("1") + dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') time.sleep(2) @@ -225,8 +226,7 @@ def test_FdbWarmRestartNotifications(self, dvs, testlog): assert ok, str(extra) # enable warm restart - (exitcode, result) = dvs.runcmd("config warm_restart enable swss") - assert exitcode == 0 + dvs.warm_restart_swss("true") # freeze orchagent for warm restart (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") @@ -317,14 +317,14 @@ def test_FdbWarmRestartNotifications(self, dvs, testlog): finally: # disable warm restart - dvs.runcmd("config warm_restart disable swss") + dvs.warm_restart_swss("false") # slow down crm polling - dvs.runcmd("crm config polling interval 10000") + dvs.crm_poll_set("10000") def test_FdbAddedAfterMemberCreated(self, dvs, testlog): dvs.setup_db() - dvs.runcmd("sonic-clear fdb all") + dvs.clear_fdb() time.sleep(2) # create a FDB entry in Application DB @@ -377,7 +377,7 @@ def test_FdbAddedAfterMemberCreated(self, dvs, testlog): ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"])]) assert ok, str(extra) - dvs.runcmd("sonic-clear fdb all") + dvs.clear_fdb() dvs.remove_vlan_member("2", "Ethernet0") dvs.remove_vlan("2") diff --git a/tests/test_fdb_update.py b/tests/test_fdb_update.py index 5daf27804e..128dc3773b 100644 --- a/tests/test_fdb_update.py +++ b/tests/test_fdb_update.py @@ -56,8 +56,7 @@ def get_mac_by_bridge_id(self, dvs, bridge_id): def test_FDBAddedAndUpdated(self, dvs, testlog): dvs.setup_db() - - dvs.runcmd("sonic-clear fdb all") + dvs.clear_fdb() time.sleep(2) # create a FDB entry in Application DB @@ -173,7 +172,7 @@ def test_FDBAddedAndUpdated(self, dvs, testlog): def test_FDBLearnedAndUpdated(self, dvs, testlog): dvs.setup_db() - dvs.runcmd("sonic-clear fdb all") + dvs.clear_fdb() # create vlan; create vlan member dvs.create_vlan("6") @@ -261,12 +260,12 @@ def test_FDBLearnedAndUpdated(self, dvs, testlog): dvs.remove_vlan("6") # clear fdb - dvs.runcmd("sonic-clear fdb all") + dvs.clear_fdb() def test_FDBLearnedAndFlushed(self, dvs, testlog): dvs.setup_db() - dvs.runcmd("sonic-clear fdb all") + dvs.clear_fdb() VLAN = "9" VLAN_NAME = "Vlan9" diff --git a/tests/test_fgnhg.py b/tests/test_fgnhg.py index 2fa8a9d890..645853e24c 100644 --- a/tests/test_fgnhg.py +++ b/tests/test_fgnhg.py @@ -216,7 +216,7 @@ def startup_link(dvs, db, port): db.wait_for_field_match("PORT_TABLE", "Ethernet%d" % (port * 4), {"oper_status": "up"}) def run_warm_reboot(dvs): - dvs.runcmd("config warm_restart enable swss") + dvs.warm_restart_swss("true") # Stop swss before modifing the configDB dvs.stop_swss() @@ -280,7 +280,7 @@ def create_interface_n_fg_ecmp_config(dvs, nh_range_start, nh_range_end, fg_nhg_ ip_pref_key = "Ethernet" + str(i*4) + "|10.0.0." + str(i*2) + "/31" create_entry(config_db, IF_TB, if_name_key, fvs_nul) create_entry(config_db, IF_TB, ip_pref_key, fvs_nul) - dvs.runcmd("config interface startup " + if_name_key) + dvs.port_admin_set(if_name_key, "up") shutdown_link(dvs, app_db, i) startup_link(dvs, app_db, i) bank = 1 @@ -300,7 +300,7 @@ def remove_interface_n_fg_ecmp_config(dvs, nh_range_start, nh_range_end, fg_nhg_ ip_pref_key = "Ethernet" + str(i*4) + "|10.0.0." + str(i*2) + "/31" remove_entry(config_db, IF_TB, if_name_key) remove_entry(config_db, IF_TB, ip_pref_key) - dvs.runcmd("config interface shutdown " + if_name_key) + dvs.port_admin_set(if_name_key, "down") shutdown_link(dvs, app_db, i) remove_entry(config_db, FG_NHG_MEMBER, "10.0.0." + str(1 + i*2)) remove_entry(config_db, FG_NHG, fg_nhg_name) @@ -334,7 +334,7 @@ def fine_grained_ecmp_base_test(dvs, match_mode): create_entry(config_db, VLAN_MEMB_TB, vlan_name_key + "|" + if_name_key, fvs) create_entry(config_db, VLAN_IF_TB, vlan_name_key, fvs_nul) create_entry(config_db, VLAN_IF_TB, ip_pref_key, fvs_nul) - dvs.runcmd("config interface startup " + if_name_key) + dvs.port_admin_set(if_name_key, "up") dvs.servers[i].runcmd("ip link set down dev eth0") == 0 dvs.servers[i].runcmd("ip link set up dev eth0") == 0 bank = 0 @@ -619,7 +619,7 @@ def fine_grained_ecmp_base_test(dvs, match_mode): remove_entry(config_db, VLAN_IF_TB, vlan_name_key) remove_entry(config_db, VLAN_MEMB_TB, vlan_name_key + "|" + if_name_key) remove_entry(config_db, VLAN_TB, vlan_name_key) - dvs.runcmd("config interface shutdown " + if_name_key) + dvs.port_admin_set(if_name_key, "down") dvs.servers[i].runcmd("ip link set down dev eth0") == 0 remove_entry(config_db, "FG_NHG_MEMBER", "10.0.0." + str(1 + i*2)) @@ -770,7 +770,7 @@ def test_fgnhg_matchmode_nexthop_multi_route(self, dvs, testlog): ip_pref_key = "Ethernet" + str(i*4) + "|10.0.0." + str(i*2) + "/31" create_entry(config_db, IF_TB, if_name_key, fvs_nul) create_entry(config_db, IF_TB, ip_pref_key, fvs_nul) - dvs.runcmd("config interface startup " + if_name_key) + dvs.port_admin_set(if_name_key, "up") shutdown_link(dvs, app_db, i) startup_link(dvs, app_db, i) dvs.runcmd("arp -s 10.0.0." + str(1 + i*2) + " 00:00:00:00:00:" + str(1 + i*2)) diff --git a/tests/test_flex_counters.py b/tests/test_flex_counters.py index ea950af7c1..76a1a535f9 100644 --- a/tests/test_flex_counters.py +++ b/tests/test_flex_counters.py @@ -3,9 +3,12 @@ from swsscommon import swsscommon -TUNNEL_TYPE_MAP = "COUNTERS_TUNNEL_TYPE_MAP" -NUMBER_OF_RETRIES = 10 +TUNNEL_TYPE_MAP = "COUNTERS_TUNNEL_TYPE_MAP" +ROUTE_TO_PATTERN_MAP = "COUNTERS_ROUTE_TO_PATTERN_MAP" +NUMBER_OF_RETRIES = 10 CPU_PORT_OID = "0x0" +PORT = "Ethernet0" +PORT_MAP = "COUNTERS_PORT_NAME_MAP" counter_group_meta = { 'port_counter': { @@ -60,10 +63,17 @@ 'name_map': 'ACL_COUNTER_RULE_MAP', 'pre_test': 'pre_acl_tunnel_counter_test', 'post_test': 'post_acl_tunnel_counter_test', + }, + 'route_flow_counter': { + 'key': 'FLOW_CNT_ROUTE', + 'group_name': 'ROUTE_FLOW_COUNTER', + 'name_map': 'COUNTERS_ROUTE_NAME_MAP', + 'pre_test': 'pre_route_flow_counter_test', + 'post_test': 'post_route_flow_counter_test', } } - +@pytest.mark.usefixtures('dvs_port_manager') class TestFlexCounters(object): def setup_dbs(self, dvs): @@ -157,13 +167,14 @@ def verify_only_phy_ports_created(self, meta_data): 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 set_flex_counter_group_status(self, group, map, status='enable'): + def set_flex_counter_group_status(self, group, map, status='enable', check_name_map=True): group_stats_entry = {"FLEX_COUNTER_STATUS": status} self.config_db.create_entry("FLEX_COUNTER_TABLE", group, group_stats_entry) - if status == 'enable': - self.wait_for_table(map) - else: - self.wait_for_table_empty(map) + if check_name_map: + if status == 'enable': + self.wait_for_table(map) + else: + self.wait_for_table_empty(map) def set_flex_counter_group_interval(self, key, group, interval): group_stats_entry = {"POLL_INTERVAL": interval} @@ -184,6 +195,7 @@ def test_flex_counters(self, dvs, counter_type): counter_map = meta_data['name_map'] pre_test = meta_data.get('pre_test') post_test = meta_data.get('post_test') + meta_data['dvs'] = dvs self.verify_no_flex_counters_tables(counter_stat) @@ -225,6 +237,37 @@ def pre_acl_tunnel_counter_test(self, meta_data): } ) + def pre_route_flow_counter_test(self, meta_data): + dvs = meta_data['dvs'] + self.config_db.create_entry('FLOW_COUNTER_ROUTE_PATTERN', '1.1.0.0/16', + { + 'max_match_count': '30' + } + ) + self.config_db.create_entry('FLOW_COUNTER_ROUTE_PATTERN', '2000::/64', + { + 'max_match_count': '30' + } + ) + + self.create_l3_intf("Ethernet0", "") + self.add_ip_address("Ethernet0", "10.0.0.0/31") + self.set_admin_status("Ethernet0", "up") + dvs.servers[0].runcmd("ip address add 10.0.0.1/31 dev eth0") + dvs.servers[0].runcmd("ip route add default via 10.0.0.0") + dvs.servers[0].runcmd("ping -c 1 10.0.0.1") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 1.1.1.0/24 10.0.0.1\"") + + self.create_l3_intf("Ethernet4", "") + self.set_admin_status("Ethernet4", "up") + self.add_ip_address("Ethernet4", "2001::1/64") + dvs.runcmd("sysctl -w net.ipv6.conf.all.forwarding=1") + dvs.servers[1].runcmd("ip -6 address add 2001::2/64 dev eth0") + dvs.servers[1].runcmd("ip -6 route add default via 2001::1") + time.sleep(2) + dvs.servers[1].runcmd("ping -6 -c 1 2001::1") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ipv6 route 2000::/64 2001::2\"") + def post_rif_counter_test(self, meta_data): self.config_db.db_connection.hdel('INTERFACE|Ethernet0|192.168.0.1/24', "NULL") @@ -241,7 +284,7 @@ def post_trap_flow_counter_test(self, meta_data): meta_data (object): flex counter meta data """ counters_keys = self.counters_db.db_connection.hgetall(meta_data['name_map']) - self.set_flex_counter_group_status(meta_data['key'], meta_data['group_name'], 'disable') + self.set_flex_counter_group_status(meta_data['key'], meta_data['name_map'], 'disable') for counter_entry in counters_keys.items(): self.wait_for_id_list_remove(meta_data['group_name'], counter_entry[0], counter_entry[1]) @@ -258,6 +301,46 @@ def post_acl_tunnel_counter_test(self, meta_data): self.config_db.delete_entry('ACL_RULE', 'DATAACL|RULE0') self.config_db.delete_entry('ACL_TABLE', 'DATAACL') + def post_route_flow_counter_test(self, meta_data): + dvs = meta_data['dvs'] + # Verify prefix to route pattern name map + self.wait_for_table(ROUTE_TO_PATTERN_MAP) + + # Remove route pattern and verify related couters are removed + v4_name_map_key = '1.1.1.0/24' + counter_oid = self.counters_db.db_connection.hget(meta_data['name_map'], v4_name_map_key) + assert counter_oid + self.config_db.delete_entry('FLOW_COUNTER_ROUTE_PATTERN', '1.1.0.0/16') + self.wait_for_id_list_remove(meta_data['group_name'], v4_name_map_key, counter_oid) + counter_oid = self.counters_db.db_connection.hget(meta_data['name_map'], v4_name_map_key) + assert not counter_oid + route_pattern = self.counters_db.db_connection.hget(ROUTE_TO_PATTERN_MAP, v4_name_map_key) + assert not route_pattern + + # Disable route flow counter and verify all counters are removed + v6_name_map_key = '2000::/64' + counter_oid = self.counters_db.db_connection.hget(meta_data['name_map'], v6_name_map_key) + assert counter_oid + self.set_flex_counter_group_status(meta_data['key'], meta_data['name_map'], 'disable') + self.wait_for_id_list_remove(meta_data['group_name'], v6_name_map_key, counter_oid) + self.wait_for_table_empty(meta_data['name_map']) + self.wait_for_table_empty(ROUTE_TO_PATTERN_MAP) + + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route {} 10.0.0.1\"".format(v4_name_map_key)) + self.remove_ip_address("Ethernet0", "10.0.0.0/31") + self.remove_l3_intf("Ethernet0") + self.set_admin_status("Ethernet0", "down") + dvs.servers[0].runcmd("ip route del default dev eth0") + dvs.servers[0].runcmd("ip address del 10.0.0.1/31 dev eth0") + + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ipv6 route 2000::/64 2001::2\"") + self.remove_ip_address("Ethernet4", "2001::1/64") + self.remove_l3_intf("Ethernet4") + self.set_admin_status("Ethernet4", "down") + dvs.servers[1].runcmd("ip -6 route del default dev eth0") + dvs.servers[1].runcmd("ip -6 address del 2001::2/64 dev eth0") + self.config_db.delete_entry('FLOW_COUNTER_ROUTE_PATTERN', '2000::/64') + def test_add_remove_trap(self, dvs): """Test steps: 1. Enable trap_flow_counter @@ -320,7 +403,7 @@ def test_add_remove_trap(self, dvs): assert oid, 'Add trap {}, but trap counter is not created'.format(removed_trap) self.wait_for_id_list(meta_data['group_name'], removed_trap, oid) - self.set_flex_counter_group_status(meta_data['key'], meta_data['group_name'], 'disable') + self.set_flex_counter_group_status(meta_data['key'], meta_data['name_map'], 'disable') def test_remove_trap_group(self, dvs): """Remove trap group and verify that all related trap counters are removed @@ -371,4 +454,302 @@ def test_remove_trap_group(self, dvs): for trap_id in trap_ids: assert trap_id not in counters_keys + self.set_flex_counter_group_status(meta_data['key'], meta_data['name_map'], 'disable') + + def test_update_route_pattern(self, dvs): + self.setup_dbs(dvs) + self.config_db.create_entry('FLOW_COUNTER_ROUTE_PATTERN', '1.1.0.0/16', + { + 'max_match_count': '30' + } + ) + self.create_l3_intf("Ethernet0", "") + self.create_l3_intf("Ethernet4", "") + self.add_ip_address("Ethernet0", "10.0.0.0/31") + self.add_ip_address("Ethernet4", "10.0.0.2/31") + self.set_admin_status("Ethernet0", "up") + self.set_admin_status("Ethernet4", "up") + # set ip address and default route + dvs.servers[0].runcmd("ip address add 10.0.0.1/31 dev eth0") + dvs.servers[0].runcmd("ip route add default via 10.0.0.0") + dvs.servers[1].runcmd("ip address add 10.0.0.3/31 dev eth0") + dvs.servers[1].runcmd("ip route add default via 10.0.0.2") + dvs.servers[0].runcmd("ping -c 1 10.0.0.3") + + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 1.1.1.0/24 10.0.0.1\"") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 2.2.2.0/24 10.0.0.3\"") + + meta_data = counter_group_meta['route_flow_counter'] + self.set_flex_counter_group_status(meta_data['key'], meta_data['name_map']) + self.wait_for_table(meta_data['name_map']) + self.wait_for_table(ROUTE_TO_PATTERN_MAP) + counter_oid = self.counters_db.db_connection.hget(meta_data['name_map'], '1.1.1.0/24') + self.wait_for_id_list(meta_data['group_name'], '1.1.1.0/24', counter_oid) + assert not self.counters_db.db_connection.hget(meta_data['name_map'], '2.2.2.0/24') + assert not self.counters_db.db_connection.hget(ROUTE_TO_PATTERN_MAP, '2.2.2.0/24') + + self.config_db.delete_entry('FLOW_COUNTER_ROUTE_PATTERN', '1.1.0.0/16') + self.wait_for_id_list_remove(meta_data['group_name'], '1.1.1.0/24', counter_oid) + self.wait_for_table_empty(meta_data['name_map']) + self.wait_for_table_empty(ROUTE_TO_PATTERN_MAP) + assert not self.counters_db.db_connection.hget(meta_data['name_map'], '1.1.1.0/24') + assert not self.counters_db.db_connection.hget(ROUTE_TO_PATTERN_MAP, '1.1.1.0/24') + + self.config_db.create_entry('FLOW_COUNTER_ROUTE_PATTERN', '2.2.0.0/16', + { + 'max_match_count': '30' + } + ) + self.wait_for_table(meta_data['name_map']) + self.wait_for_table(ROUTE_TO_PATTERN_MAP) + counter_oid = self.counters_db.db_connection.hget(meta_data['name_map'], '2.2.2.0/24') + self.wait_for_id_list(meta_data['group_name'], '2.2.2.0/24', counter_oid) + + self.set_flex_counter_group_status(meta_data['key'], meta_data['name_map'], 'disable') + self.wait_for_id_list_remove(meta_data['group_name'], '2.2.2.0/24', counter_oid) + self.wait_for_table_empty(meta_data['name_map']) + self.wait_for_table_empty(ROUTE_TO_PATTERN_MAP) + + self.config_db.delete_entry('FLOW_COUNTER_ROUTE_PATTERN', '2.2.0.0/16') + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route {} 10.0.0.1\"".format('1.1.1.0/24')) + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route {} 10.0.0.3\"".format('2.2.2.0/24')) + + # remove ip address + self.remove_ip_address("Ethernet0", "10.0.0.0/31") + self.remove_ip_address("Ethernet4", "10.0.0.2/31") + + # remove l3 interface + self.remove_l3_intf("Ethernet0") + self.remove_l3_intf("Ethernet4") + + self.set_admin_status("Ethernet0", "down") + self.set_admin_status("Ethernet4", "down") + + # remove ip address and default route + dvs.servers[0].runcmd("ip route del default dev eth0") + dvs.servers[0].runcmd("ip address del 10.0.0.1/31 dev eth0") + + dvs.servers[1].runcmd("ip route del default dev eth0") + dvs.servers[1].runcmd("ip address del 10.0.0.3/31 dev eth0") + + def test_add_remove_route_flow_counter(self, dvs): + self.setup_dbs(dvs) + self.config_db.create_entry('FLOW_COUNTER_ROUTE_PATTERN', '1.1.0.0/16', + { + 'max_match_count': '30' + } + ) + meta_data = counter_group_meta['route_flow_counter'] + self.set_flex_counter_group_status(meta_data['key'], meta_data['name_map'], check_name_map=False) + + self.create_l3_intf("Ethernet0", "") + self.add_ip_address("Ethernet0", "10.0.0.0/31") + self.set_admin_status("Ethernet0", "up") + dvs.servers[0].runcmd("ip address add 10.0.0.1/31 dev eth0") + dvs.servers[0].runcmd("ip route add default via 10.0.0.0") + dvs.servers[0].runcmd("ping -c 1 10.0.0.1") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 1.1.1.0/24 10.0.0.1\"") + + self.wait_for_table(meta_data['name_map']) + self.wait_for_table(ROUTE_TO_PATTERN_MAP) + counter_oid = self.counters_db.db_connection.hget(meta_data['name_map'], '1.1.1.0/24') + self.wait_for_id_list(meta_data['group_name'], '1.1.1.0/24', counter_oid) + + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route {} 10.0.0.1\"".format('1.1.1.0/24')) + self.wait_for_id_list_remove(meta_data['group_name'], '1.1.1.0/24', counter_oid) + self.wait_for_table_empty(meta_data['name_map']) + self.wait_for_table_empty(ROUTE_TO_PATTERN_MAP) + + self.config_db.delete_entry('FLOW_COUNTER_ROUTE_PATTERN', '1.1.0.0/16') self.set_flex_counter_group_status(meta_data['key'], meta_data['group_name'], 'disable') + + # remove ip address + self.remove_ip_address("Ethernet0", "10.0.0.0/31") + + # remove l3 interface + self.remove_l3_intf("Ethernet0") + + self.set_admin_status("Ethernet0", "down") + + # remove ip address and default route + dvs.servers[0].runcmd("ip route del default dev eth0") + dvs.servers[0].runcmd("ip address del 10.0.0.1/31 dev eth0") + + def test_router_flow_counter_max_match_count(self, dvs): + self.setup_dbs(dvs) + self.config_db.create_entry('FLOW_COUNTER_ROUTE_PATTERN', '1.1.0.0/16', + { + 'max_match_count': '1' + } + ) + meta_data = counter_group_meta['route_flow_counter'] + self.set_flex_counter_group_status(meta_data['key'], meta_data['name_map'], check_name_map=False) + self.create_l3_intf("Ethernet0", "") + self.create_l3_intf("Ethernet4", "") + self.add_ip_address("Ethernet0", "10.0.0.0/31") + self.add_ip_address("Ethernet4", "10.0.0.2/31") + self.set_admin_status("Ethernet0", "up") + self.set_admin_status("Ethernet4", "up") + # set ip address and default route + dvs.servers[0].runcmd("ip address add 10.0.0.1/31 dev eth0") + dvs.servers[0].runcmd("ip route add default via 10.0.0.0") + dvs.servers[1].runcmd("ip address add 10.0.0.3/31 dev eth0") + dvs.servers[1].runcmd("ip route add default via 10.0.0.2") + dvs.servers[0].runcmd("ping -c 1 10.0.0.3") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 1.1.1.0/24 10.0.0.1\"") + dvs.runcmd("vtysh -c \"configure terminal\" -c \"ip route 1.1.2.0/24 10.0.0.3\"") + + self.wait_for_table(meta_data['name_map']) + self.wait_for_table(ROUTE_TO_PATTERN_MAP) + counter_oid = self.counters_db.db_connection.hget(meta_data['name_map'], '1.1.1.0/24') + self.wait_for_id_list(meta_data['group_name'], '1.1.1.0/24', counter_oid) + assert not self.counters_db.db_connection.hget(meta_data['name_map'], '1.1.2.0/24') + self.config_db.update_entry('FLOW_COUNTER_ROUTE_PATTERN', '1.1.0.0/16', + { + 'max_match_count': '2' + } + ) + for _ in range(NUMBER_OF_RETRIES): + counter_oid = self.counters_db.db_connection.hget(meta_data['name_map'], '1.1.2.0/24') + if not counter_oid: + time.sleep(1) + else: + break + assert counter_oid + self.wait_for_id_list(meta_data['group_name'], '1.1.2.0/24', counter_oid) + + self.config_db.update_entry('FLOW_COUNTER_ROUTE_PATTERN', '1.1.0.0/16', + { + 'max_match_count': '1' + } + ) + + for _ in range(NUMBER_OF_RETRIES): + counters_keys = self.counters_db.db_connection.hgetall(meta_data['name_map']) + if len(counters_keys) == 1: + break + else: + time.sleep(1) + + assert len(counters_keys) == 1 + + to_remove = '1.1.2.0/24' if '1.1.2.0/24' in counters_keys else '1.1.1.0/24' + to_remove_nexthop = '10.0.0.3' if '1.1.2.0/24' in counters_keys else '10.0.0.1' + to_bound = '1.1.2.0/24' if '1.1.1.0/24' == to_remove else '1.1.1.0/24' + to_bound_nexthop = '10.0.0.1' if '1.1.2.0/24' in counters_keys else '10.0.0.3' + + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route {} {}\"".format(to_remove, to_remove_nexthop)) + for _ in range(NUMBER_OF_RETRIES): + counter_oid = self.counters_db.db_connection.hget(meta_data['name_map'], to_bound) + if not counter_oid: + time.sleep(1) + else: + break + assert counter_oid + self.wait_for_id_list(meta_data['group_name'], to_bound, counter_oid) + counters_keys = self.counters_db.db_connection.hgetall(meta_data['name_map']) + assert to_remove not in counters_keys + assert to_bound in counters_keys + counters_keys = self.counters_db.db_connection.hgetall(ROUTE_TO_PATTERN_MAP) + assert to_remove not in counters_keys + assert to_bound in counters_keys + + dvs.runcmd("vtysh -c \"configure terminal\" -c \"no ip route {} {}\"".format(to_bound, to_bound_nexthop)) + + # remove ip address + self.remove_ip_address("Ethernet0", "10.0.0.0/31") + self.remove_ip_address("Ethernet4", "10.0.0.2/31") + + # remove l3 interface + self.remove_l3_intf("Ethernet0") + self.remove_l3_intf("Ethernet4") + + self.set_admin_status("Ethernet0", "down") + self.set_admin_status("Ethernet4", "down") + + # remove ip address and default route + dvs.servers[0].runcmd("ip route del default dev eth0") + dvs.servers[0].runcmd("ip address del 10.0.0.1/31 dev eth0") + + dvs.servers[1].runcmd("ip route del default dev eth0") + dvs.servers[1].runcmd("ip address del 10.0.0.3/31 dev eth0") + self.config_db.delete_entry('FLOW_COUNTER_ROUTE_PATTERN', '1.1.0.0/16') + + def create_l3_intf(self, interface, vrf_name): + if len(vrf_name) == 0: + self.config_db.create_entry("INTERFACE", interface, {"NULL": "NULL"}) + else: + self.config_db.create_entry("INTERFACE", interface, {"vrf_name": vrf_name}) + + def remove_l3_intf(self, interface): + self.config_db.delete_entry("INTERFACE", interface) + + def add_ip_address(self, interface, ip): + self.config_db.create_entry("INTERFACE", interface + "|" + ip, {"NULL": "NULL"}) + + def remove_ip_address(self, interface, ip): + self.config_db.delete_entry("INTERFACE", interface + "|" + ip) + + def set_admin_status(self, interface, status): + self.config_db.update_entry("PORT", interface, {"admin_status": status}) + + def test_add_remove_ports(self, dvs): + self.setup_dbs(dvs) + + # set flex counter + counter_key = counter_group_meta['queue_counter']['key'] + counter_stat = counter_group_meta['queue_counter']['group_name'] + counter_map = counter_group_meta['queue_counter']['name_map'] + self.set_flex_counter_group_status(counter_key, counter_map) + + # receive port info + fvs = self.config_db.get_entry("PORT", PORT) + assert len(fvs) > 0 + + # save all the oids of the pg drop counters + oid_list = [] + counters_queue_map = self.counters_db.get_entry("COUNTERS_QUEUE_NAME_MAP", "") + for key, oid in counters_queue_map.items(): + if PORT in key: + oid_list.append(oid) + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", counter_stat + ":%s" % oid) + assert len(fields) == 1 + oid_list_len = len(oid_list) + + # get port oid + port_oid = self.counters_db.get_entry(PORT_MAP, "")[PORT] + + # remove port and verify that it was removed properly + self.dvs_port.remove_port(PORT) + dvs.get_asic_db().wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid) + + # verify counters were removed from flex counter table + for oid in oid_list: + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", counter_stat + ":%s" % oid) + assert len(fields) == 0 + + # verify that port counter maps were removed from counters db + counters_queue_map = self.counters_db.get_entry("COUNTERS_QUEUE_NAME_MAP", "") + for key in counters_queue_map.keys(): + if PORT in key: + assert False + + # add port and wait until the port is added on asic db + num_of_keys_without_port = len(dvs.get_asic_db().get_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT")) + + self.config_db.create_entry("PORT", PORT, fvs) + + dvs.get_asic_db().wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT", num_of_keys_without_port + 1) + dvs.get_counters_db().wait_for_fields("COUNTERS_QUEUE_NAME_MAP", "", ["%s:0"%(PORT)]) + + # verify queue counters were added + oid_list = [] + counters_queue_map = self.counters_db.get_entry("COUNTERS_QUEUE_NAME_MAP", "") + + for key, oid in counters_queue_map.items(): + if PORT in key: + oid_list.append(oid) + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", counter_stat + ":%s" % oid) + assert len(fields) == 1 + # the number of the oids needs to be the same as the original number of oids (before removing a port and adding) + assert oid_list_len == len(oid_list) diff --git a/tests/test_inband_intf_mgmt_vrf.py b/tests/test_inband_intf_mgmt_vrf.py index 05aa1f7389..4b1b8c86ed 100644 --- a/tests/test_inband_intf_mgmt_vrf.py +++ b/tests/test_inband_intf_mgmt_vrf.py @@ -14,7 +14,6 @@ def setup_db(self, dvs): def add_mgmt_vrf(self, dvs): initial_entries = set(self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER")) - #dvs.runcmd("config vrf add mgmt") dvs.runcmd("ip link add mgmt type vrf table 5000") dvs.runcmd("ifconfig mgmt up") time.sleep(2) diff --git a/tests/test_macsec.py b/tests/test_macsec.py index f74f31c008..61c90d84a8 100644 --- a/tests/test_macsec.py +++ b/tests/test_macsec.py @@ -1,11 +1,9 @@ from swsscommon import swsscommon import conftest -import sys import functools import typing import re -import time def to_string(value): @@ -94,11 +92,8 @@ def gen_sci(macsec_system_identifier: str, macsec_port_identifier: int) -> str: str.maketrans("", "", ":.-")) sci = "{}{}".format( macsec_system_identifier, - str(macsec_port_identifier).zfill(4)) - sci = int(sci, 16) - if sys.byteorder == "little": - sci = int.from_bytes(sci.to_bytes(8, 'big'), 'little', signed=False) - return str(sci) + str(macsec_port_identifier).zfill(4)).lower() + return sci def gen_sc_key( @@ -321,6 +316,13 @@ def delete_transmit_sa(self, sai: str): del self.app_transmit_sa_table[sai] self.state_transmit_sa_table.wait_delete(sai) + @macsec_sa() + def set_macsec_pn( + self, + sai: str, + pn: int): + self.app_transmit_sa_table[sai] = {"next_pn": pn} + @macsec_sc() def set_enable_transmit_sa(self, sci: str, an: int, enable: bool): if enable: @@ -475,6 +477,12 @@ def rekey_macsec( auth_key: str, ssci: int, salt: str): + wpa.set_macsec_pn( + port_name, + local_mac_address, + macsec_port_identifier, + an, + 0x00000000C0000000) wpa.create_receive_sa( port_name, peer_mac_address, diff --git a/tests/test_mclag_fdb.py b/tests/test_mclag_fdb.py index 5049859437..a4e5ff0f9d 100644 --- a/tests/test_mclag_fdb.py +++ b/tests/test_mclag_fdb.py @@ -76,7 +76,7 @@ def how_many_entries_exist(db, table): @pytest.mark.dev_sanity def test_mclagFdb_basic_config_add(dvs, testlog): dvs.setup_db() - dvs.runcmd("sonic-clear fdb all") + dvs.clear_fdb() time.sleep(2) vlan_before = how_many_entries_exist(dvs.adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") diff --git a/tests/test_mux.py b/tests/test_mux.py index eb31d2258e..f90c98eead 100644 --- a/tests/test_mux.py +++ b/tests/test_mux.py @@ -30,6 +30,9 @@ class TestMuxTunnelBase(): STATE_FDB_TABLE = "FDB_TABLE" MUX_TUNNEL_0 = "MuxTunnel0" PEER_SWITCH_HOST = "peer_switch_hostname" + CONFIG_TUNNEL_TABLE_NAME = "TUNNEL" + ASIC_QOS_MAP_TABLE_KEY = "ASIC_STATE:SAI_OBJECT_TYPE_QOS_MAP" + TUNNEL_QOS_MAP_NAME = "AZURE_TUNNEL" SELF_IPV4 = "10.1.0.32" PEER_IPV4 = "10.1.0.33" @@ -61,7 +64,11 @@ class TestMuxTunnelBase(): "dst_ip": SELF_IPV4, "dscp_mode": "uniform", "ecn_mode": "standard", - "ttl_mode": "pipe" + "ttl_mode": "pipe", + "encap_tc_to_queue_map": self.TUNNEL_QOS_MAP_NAME, + "encap_tc_to_dscp_map": self.TUNNEL_QOS_MAP_NAME, + "decap_dscp_to_tc_map": self.TUNNEL_QOS_MAP_NAME, + "decap_tc_to_pg_map": self.TUNNEL_QOS_MAP_NAME } DEFAULT_PEER_SWITCH_PARAMS = { @@ -82,10 +89,16 @@ class TestMuxTunnelBase(): "pipe" : "SAI_TUNNEL_TTL_MODE_PIPE_MODEL", "uniform" : "SAI_TUNNEL_TTL_MODE_UNIFORM_MODEL" } - + + TC_TO_DSCP_MAP = {str(i):str(i) for i in range(0, 8)} + TC_TO_QUEUE_MAP = {str(i):str(i) for i in range(0, 8)} + DSCP_TO_TC_MAP = {str(i):str(1) for i in range(0, 64)} + TC_TO_PRIORITY_GROUP_MAP = {str(i):str(i) for i in range(0, 8)} + def create_vlan_interface(self, dvs): confdb = dvs.get_config_db() + def create_vlan_interface(self, confdb, asicdb, dvs): fvs = {"vlanid": "1000"} confdb.create_entry("VLAN", self.VLAN_1000, fvs) @@ -99,9 +112,9 @@ def create_vlan_interface(self, dvs): confdb.create_entry("VLAN_INTERFACE", "Vlan1000|192.168.0.1/24", fvs) confdb.create_entry("VLAN_INTERFACE", "Vlan1000|fc02:1000::1/64", fvs) - dvs.runcmd("config interface startup Ethernet0") - dvs.runcmd("config interface startup Ethernet4") - dvs.runcmd("config interface startup Ethernet8") + dvs.port_admin_set("Ethernet0", "up") + dvs.port_admin_set("Ethernet4", "up") + dvs.port_admin_set("Ethernet8", "up") def create_mux_cable(self, confdb): fvs = {"server_ipv4": self.SERV1_IPV4+self.IPV4_MASK, @@ -594,7 +607,7 @@ def check_vr_exists_in_asicdb(self, asicdb, sai_oid): asicdb.wait_for_entry(self.ASIC_VRF_TABLE, sai_oid) return True - def create_and_test_peer(self, asicdb): + def create_and_test_peer(self, asicdb, tc_to_dscp_map_oid=None, tc_to_queue_map_oid=None): """ Create PEER entry verify all needed enties in ASIC DB exists """ # check asic db table @@ -617,6 +630,11 @@ def create_and_test_peer(self, asicdb): fvs = asicdb.wait_for_entry(self.ASIC_TUNNEL_TABLE, p2p_obj) + if tc_to_dscp_map_oid: + assert "SAI_TUNNEL_ATTR_ENCAP_QOS_TC_AND_COLOR_TO_DSCP_MAP" in fvs + if tc_to_queue_map_oid: + assert "SAI_TUNNEL_ATTR_ENCAP_QOS_TC_TO_QUEUE_MAP" in fvs + for field, value in fvs.items(): if field == "SAI_TUNNEL_ATTR_TYPE": assert value == "SAI_TUNNEL_TYPE_IPINIP" @@ -634,28 +652,37 @@ def create_and_test_peer(self, asicdb): assert value == "SAI_TUNNEL_TTL_MODE_PIPE_MODEL" elif field == "SAI_TUNNEL_ATTR_LOOPBACK_PACKET_ACTION": assert value == "SAI_PACKET_ACTION_DROP" + elif field == "SAI_TUNNEL_ATTR_ENCAP_QOS_TC_AND_COLOR_TO_DSCP_MAP": + assert value == tc_to_dscp_map_oid + elif field == "SAI_TUNNEL_ATTR_ENCAP_QOS_TC_TO_QUEUE_MAP": + assert value == tc_to_queue_map_oid + elif field == "SAI_TUNNEL_ATTR_ENCAP_DSCP_MODE": + assert value == "SAI_TUNNEL_DSCP_MODE_PIPE_MODEL" else: assert False, "Field %s is not tested" % field - def check_tunnel_termination_entry_exists_in_asicdb(self, asicdb, tunnel_sai_oid, dst_ips): + def check_tunnel_termination_entry_exists_in_asicdb(self, asicdb, tunnel_sai_oid, dst_ips, src_ip=None): tunnel_term_entries = asicdb.wait_for_n_keys(self.ASIC_TUNNEL_TERM_ENTRIES, len(dst_ips)) - + expected_term_type = "SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P" if src_ip else "SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP" + expected_len = 6 if src_ip else 5 for term_entry in tunnel_term_entries: fvs = asicdb.get_entry(self.ASIC_TUNNEL_TERM_ENTRIES, term_entry) - assert len(fvs) == 5 + assert len(fvs) == expected_len for field, value in fvs.items(): if field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_VR_ID": assert self.check_vr_exists_in_asicdb(asicdb, value) elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE": - assert value == "SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP" + assert value == expected_term_type elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE": assert value == "SAI_TUNNEL_TYPE_IPINIP" elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID": assert value == tunnel_sai_oid elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP": assert value in dst_ips + elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP" and src_ip: + assert value == src_ip else: assert False, "Field %s is not tested" % field @@ -664,6 +691,28 @@ def create_and_test_tunnel(self, db, asicdb, tunnel_name, tunnel_params): is_symmetric_tunnel = "src_ip" in tunnel_params + # 6 parameters to check in case of decap tunnel + # + 1 (SAI_TUNNEL_ATTR_ENCAP_SRC_IP) in case of symmetric tunnel + expected_len = 7 if is_symmetric_tunnel else 6 + + if 'decap_tc_to_pg_map_id' in tunnel_params: + expected_len += 1 + decap_tc_to_pg_map_id = tunnel_params.pop('decap_tc_to_pg_map_id') + + if 'decap_dscp_to_tc_map_id' in tunnel_params: + expected_len += 1 + decap_dscp_to_tc_map_id = tunnel_params.pop('decap_dscp_to_tc_map_id') + + # create tunnel entry in DB + ps = swsscommon.ProducerStateTable(db, self.APP_TUNNEL_DECAP_TABLE_NAME) + + fvs = create_fvs(**tunnel_params) + + ps.set(tunnel_name, fvs) + + # wait till config will be applied + time.sleep(1) + # check asic db table tunnels = asicdb.wait_for_n_keys(self.ASIC_TUNNEL_TABLE, 1) @@ -671,14 +720,13 @@ def create_and_test_tunnel(self, db, asicdb, tunnel_name, tunnel_params): fvs = asicdb.wait_for_entry(self.ASIC_TUNNEL_TABLE, tunnel_sai_obj) - # 6 parameters to check in case of decap tunnel - # + 1 (SAI_TUNNEL_ATTR_ENCAP_SRC_IP) in case of symmetric tunnel - assert len(fvs) == 7 if is_symmetric_tunnel else 6 + assert len(fvs) == expected_len expected_ecn_mode = self.ecn_modes_map[tunnel_params["ecn_mode"]] expected_dscp_mode = self.dscp_modes_map[tunnel_params["dscp_mode"]] expected_ttl_mode = self.ttl_modes_map[tunnel_params["ttl_mode"]] + for field, value in fvs.items(): if field == "SAI_TUNNEL_ATTR_TYPE": assert value == "SAI_TUNNEL_TYPE_IPINIP" @@ -694,12 +742,16 @@ def create_and_test_tunnel(self, db, asicdb, tunnel_name, tunnel_params): assert self.check_interface_exists_in_asicdb(asicdb, value) elif field == "SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE": assert self.check_interface_exists_in_asicdb(asicdb, value) + elif field == "SAI_TUNNEL_ATTR_DECAP_QOS_DSCP_TO_TC_MAP": + assert value == decap_dscp_to_tc_map_id + elif field == "SAI_TUNNEL_ATTR_DECAP_QOS_TC_TO_PRIORITY_GROUP_MAP": + assert value == decap_tc_to_pg_map_id else: assert False, "Field %s is not tested" % field - self.check_tunnel_termination_entry_exists_in_asicdb( - asicdb, tunnel_sai_obj, tunnel_params["dst_ip"].split(",") - ) + + src_ip = tunnel_params['src_ip'] if 'src_ip' in kwargs else None + self.check_tunnel_termination_entry_exists_in_asicdb(asicdb, tunnel_sai_obj, tunnel_params["dst_ip"].split(","), src_ip) def remove_and_test_tunnel(self, db, asicdb, tunnel_name): """ Removes tunnel and checks that ASIC db is clear""" @@ -743,6 +795,23 @@ def check_app_db_neigh_table( appdb.wait_for_field_match(self.APP_NEIGH_TABLE, key, {'neigh': mac}) else: appdb.wait_for_deleted_keys(self.APP_NEIGH_TABLE, key) + def add_qos_map(self, configdb, asicdb, qos_map_type_name, qos_map_name, qos_map): + current_oids = asicdb.get_keys(self.ASIC_QOS_MAP_TABLE_KEY) + # Apply QoS map to config db + table = swsscommon.Table(configdb.db_connection, qos_map_type_name) + fvs = swsscommon.FieldValuePairs(list(qos_map.items())) + table.set(qos_map_name, fvs) + time.sleep(1) + + diff = set(asicdb.get_keys(self.ASIC_QOS_MAP_TABLE_KEY)) - set(current_oids) + assert len(diff) == 1 + oid = diff.pop() + return oid + + def remove_qos_map(self, configdb, qos_map_type_name, qos_map_oid): + """ Remove the testing qos map""" + table = swsscommon.Table(configdb.db_connection, qos_map_type_name) + table._del(qos_map_oid) def cleanup_left_over(self, db, asicdb): """ Cleanup APP and ASIC tables """ @@ -918,24 +987,48 @@ def intf_fdb_map(self, dvs, setup_vlan): class TestMuxTunnel(TestMuxTunnelBase): """ Tests for Mux tunnel creation and removal """ + @pytest.fixture(scope='class') + def setup(self, dvs): + db = dvs.get_config_db() + asicdb = dvs.get_asic_db() - def test_Tunnel(self, dvs, setup_tunnel, testlog): - """ test IPv4 Mux tunnel creation """ + tc_to_dscp_map_oid = self.add_qos_map(db, asicdb, swsscommon.CFG_TC_TO_DSCP_MAP_TABLE_NAME, self.TUNNEL_QOS_MAP_NAME, self.TC_TO_DSCP_MAP) + tc_to_queue_map_oid = self.add_qos_map(db, asicdb, swsscommon.CFG_TC_TO_QUEUE_MAP_TABLE_NAME, self.TUNNEL_QOS_MAP_NAME, self.TC_TO_QUEUE_MAP) + + dscp_to_tc_map_oid = self.add_qos_map(db, asicdb, swsscommon.CFG_DSCP_TO_TC_MAP_TABLE_NAME, self.TUNNEL_QOS_MAP_NAME, self.DSCP_TO_TC_MAP) + tc_to_pg_map_oid = self.add_qos_map(db, asicdb, swsscommon.CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, self.TUNNEL_QOS_MAP_NAME, self.TC_TO_PRIORITY_GROUP_MAP) + + yield tc_to_dscp_map_oid, tc_to_queue_map_oid, dscp_to_tc_map_oid, tc_to_pg_map_oid + + self.remove_qos_map(db, swsscommon.CFG_TC_TO_DSCP_MAP_TABLE_NAME, tc_to_dscp_map_oid) + self.remove_qos_map(db, swsscommon.CFG_TC_TO_QUEUE_MAP_TABLE_NAME, tc_to_queue_map_oid) + self.remove_qos_map(db, swsscommon.CFG_DSCP_TO_TC_MAP_TABLE_NAME, dscp_to_tc_map_oid) + self.remove_qos_map(db, swsscommon.CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, tc_to_pg_map_oid) + + def test_Tunnel(self, dvs, setup_tunnel, testlog, setup): + """ test IPv4 Mux tunnel creation """ db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) asicdb = dvs.get_asic_db() #self.cleanup_left_over(db, asicdb) + _, _, dscp_to_tc_map_oid, tc_to_pg_map_oid = setup + tunnel_params = self.DEFAULT_TUNNEL_PARAMS + tunnel_params["decap_dscp_to_tc_map_id"] = dscp_to_tc_map_oid + tunnel_params["decap_tc_to_pg_map_id"] = tc_to_pg_map_oid # create tunnel IPv4 tunnel - self.create_and_test_tunnel(db, asicdb, self.MUX_TUNNEL_0, self.DEFAULT_TUNNEL_PARAMS) + self.create_and_test_tunnel(db, asicdb, self.MUX_TUNNEL_0, tunnel_params) + + def test_Peer(self, dvs, setup_peer_switch, setup, testlog): - def test_Peer(self, dvs, setup_peer_switch, testlog): """ test IPv4 Mux tunnel creation """ asicdb = dvs.get_asic_db() + + encap_tc_to_dscp_map_id, encap_tc_to_queue_map_id, _, _ = setup - self.create_and_test_peer(asicdb) + self.create_and_test_peer(asicdb, encap_tc_to_dscp_map_id, encap_tc_to_queue_map_id) def test_Neighbor(self, dvs, dvs_route, testlog): """ test Neighbor entries and mux state change """ diff --git a/tests/test_nat.py b/tests/test_nat.py index 9e87b5f54c..1c509e464f 100644 --- a/tests/test_nat.py +++ b/tests/test_nat.py @@ -15,13 +15,10 @@ def setup_db(self, dvs): self.config_db = dvs.get_config_db() def set_interfaces(self, dvs): - fvs = {"NULL": "NULL"} - self.config_db.create_entry("INTERFACE", "Ethernet0|67.66.65.1/24", fvs) - self.config_db.create_entry("INTERFACE", "Ethernet4|18.18.18.1/24", fvs) - self.config_db.create_entry("INTERFACE", "Ethernet0", fvs) - self.config_db.create_entry("INTERFACE", "Ethernet4", fvs) - dvs.runcmd("config interface startup Ethernet0") - dvs.runcmd("config interface startup Ethernet4") + dvs.interface_ip_add("Ethernet0", "67.66.65.1/24") + dvs.interface_ip_add("Ethernet4", "18.18.18.1/24") + dvs.port_admin_set("Ethernet0", "up") + dvs.port_admin_set("Etherent4", "up") dvs.servers[0].runcmd("ip link set down dev eth0") dvs.servers[0].runcmd("ip link set up dev eth0") @@ -33,7 +30,7 @@ def set_interfaces(self, dvs): dvs.servers[1].runcmd("ifconfig eth0 18.18.18.2/24") dvs.servers[1].runcmd("ip route add default via 18.18.18.1") - dvs.runcmd("config nat add interface Ethernet0 -nat_zone 1") + dvs.set_nat_zone("Ethernet0", "1") time.sleep(1) @@ -48,10 +45,10 @@ def test_NatGlobalTable(self, dvs, testlog): self.setup_db(dvs) # enable NAT feature - dvs.runcmd("config nat feature enable") - dvs.runcmd("config nat set timeout 450") - dvs.runcmd("config nat set udp-timeout 360") - dvs.runcmd("config nat set tcp-timeout 900") + dvs.nat_mode_set("enabled") + dvs.nat_timeout_set("450") + dvs.nat_udp_timeout_set("360") + dvs.nat_tcp_timeout_set("900") # check NAT global values in appdb self.app_db.wait_for_n_keys("NAT_GLOBAL_TABLE", 1) @@ -82,7 +79,7 @@ def test_AddNatStaticEntry(self, dvs, testlog): dvs.servers[0].runcmd("ping -c 1 18.18.18.2") # add a static nat entry - dvs.runcmd("config nat add static basic 67.66.65.1 18.18.18.2") + dvs.add_nat_basic_entry("67.66.65.1", "18.18.18.2") # check the entry in the config db self.config_db.wait_for_n_keys("STATIC_NAT", 1) @@ -115,7 +112,7 @@ def test_DelNatStaticEntry(self, dvs, testlog): self.setup_db(dvs) # delete a static nat entry - dvs.runcmd("config nat remove static basic 67.66.65.1 18.18.18.2") + dvs.del_nat_basic_entry("67.66.65.1") # check the entry is no there in the config db self.config_db.wait_for_n_keys("STATIC_NAT", 0) @@ -134,7 +131,7 @@ def test_AddNaPtStaticEntry(self, dvs, testlog): dvs.servers[0].runcmd("ping -c 1 18.18.18.2") # add a static nat entry - dvs.runcmd("config nat add static udp 67.66.65.1 670 18.18.18.2 180") + dvs.add_nat_udp_entry("67.66.65.1", "670", "18.18.18.2", "180") # check the entry in the config db self.config_db.wait_for_n_keys("STATIC_NAPT", 1) @@ -165,7 +162,7 @@ def test_DelNaPtStaticEntry(self, dvs, testlog): self.setup_db(dvs) # delete a static nat entry - dvs.runcmd("config nat remove static udp 67.66.65.1 670 18.18.18.2 180") + dvs.del_nat_udp_entry("67.66.65.1", "670") # check the entry is no there in the config db self.config_db.wait_for_n_keys("STATIC_NAPT", 0) @@ -186,8 +183,8 @@ def test_AddTwiceNatEntry(self, dvs, testlog): dvs.servers[1].runcmd("ping -c 1 67.66.65.2") # add a twice nat entry - dvs.runcmd("config nat add static basic 67.66.65.2 18.18.18.1 -nat_type snat -twice_nat_id 9") - dvs.runcmd("config nat add static basic 67.66.65.1 18.18.18.2 -nat_type dnat -twice_nat_id 9") + dvs.add_twice_nat_basic_entry("67.66.65.2", "18.18.18.1", "snat", "9") + dvs.add_twice_nat_basic_entry("67.66.65.1", "18.18.18.2", "dnat", "9") # check the entry in the config db self.config_db.wait_for_n_keys("STATIC_NAT", 2) @@ -220,8 +217,8 @@ def test_DelTwiceNatStaticEntry(self, dvs, testlog): self.setup_db(dvs) # delete a static nat entry - dvs.runcmd("config nat remove static basic 67.66.65.2 18.18.18.1") - dvs.runcmd("config nat remove static basic 67.66.65.1 18.18.18.2") + dvs.del_twice_nat_basic_entry("67.66.65.2") + dvs.del_twice_nat_basic_entry("67.66.65.1") # check the entry is no there in the config db self.config_db.wait_for_n_keys("STATIC_NAT", 0) @@ -241,8 +238,8 @@ def test_AddTwiceNaPtEntry(self, dvs, testlog): dvs.servers[1].runcmd("ping -c 1 67.66.65.2") # add a twice nat entry - dvs.runcmd("config nat add static udp 67.66.65.2 670 18.18.18.1 181 -nat_type snat -twice_nat_id 7") - dvs.runcmd("config nat add static udp 67.66.65.1 660 18.18.18.2 182 -nat_type dnat -twice_nat_id 7") + dvs.add_twice_nat_udp_entry("67.66.65.2", "670", "18.18.18.1", "181", "snat", "7") + dvs.add_twice_nat_udp_entry("67.66.65.1", "660", "18.18.18.2", "182", "dnat", "7") # check the entry in the config db self.config_db.wait_for_n_keys("STATIC_NAPT", 2) @@ -277,8 +274,8 @@ def test_DelTwiceNaPtStaticEntry(self, dvs, testlog): self.setup_db(dvs) # delete a static nat entry - dvs.runcmd("config nat remove static udp 67.66.65.2 670 18.18.18.1 181") - dvs.runcmd("config nat remove static udp 67.66.65.1 660 18.18.18.2 182") + dvs.del_twice_nat_udp_entry("67.66.65.2", "670") + dvs.del_twice_nat_udp_entry("67.66.65.1", "660") # check the entry is not there in the config db self.config_db.wait_for_n_keys("STATIC_NAPT", 0) @@ -294,7 +291,7 @@ def test_VerifyConntrackTimeoutForNatEntry(self, dvs, testlog): dvs.servers[0].runcmd("ping -c 1 18.18.18.2") # add a static nat entry - dvs.runcmd("config nat add static basic 67.66.65.1 18.18.18.2") + dvs.add_nat_basic_entry("67.66.65.1", "18.18.18.2") # check the conntrack timeout for static entry def _check_conntrack_for_static_entry(): @@ -321,7 +318,7 @@ def _check_conntrack_for_static_entry(): wait_for_result(_check_conntrack_for_static_entry) # delete a static nat entry - dvs.runcmd("config nat remove static basic 67.66.65.1 18.18.18.2") + dvs.del_nat_basic_entry("67.66.65.1") def test_DoNotNatAclAction(self, dvs_acl, testlog): @@ -360,7 +357,7 @@ def test_CrmSnatAndDnatEntryUsedCount(self, dvs, testlog): dvs.servers[0].runcmd("ping -c 1 18.18.18.2") # set pooling interval to 1 - dvs.runcmd("crm config polling interval 1") + dvs.crm_poll_set("1") dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_SNAT_ENTRY', '1000') dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_DNAT_ENTRY', '1000') @@ -376,7 +373,7 @@ def test_CrmSnatAndDnatEntryUsedCount(self, dvs, testlog): avail_dnat_counter = dvs.getCrmCounterValue('STATS', 'crm_stats_dnat_entry_available') # add a static nat entry - dvs.runcmd("config nat add static basic 67.66.65.1 18.18.18.2") + dvs.add_nat_basic_entry("67.66.65.1", "18.18.18.2") #check the entry in asic db, 3 keys = SNAT, DNAT and DNAT_Pool keys = self.asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_NAT_ENTRY", 3) @@ -405,7 +402,7 @@ def test_CrmSnatAndDnatEntryUsedCount(self, dvs, testlog): assert avail_dnat_counter - new_avail_dnat_counter == 1 # delete a static nat entry - dvs.runcmd("config nat remove static basic 67.66.65.1 18.18.18.2") + dvs.del_nat_basic_entry("67.66.65.1") dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_SNAT_ENTRY', '1000') dvs.setReadOnlyAttr('SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_DNAT_ENTRY', '1000') diff --git a/tests/test_neighbor.py b/tests/test_neighbor.py index 4893faeb21..741ce9a71a 100644 --- a/tests/test_neighbor.py +++ b/tests/test_neighbor.py @@ -413,6 +413,53 @@ def test_FlushResolveNeighborIpv4(self, dvs, testlog): (exitcode, output) = dvs.runcmd(['sh', '-c', "supervisorctl status nbrmgrd | awk '{print $2}'"]) assert output == "RUNNING\n" + def test_Ipv4LinkLocalNeighbor(self, dvs, testlog): + self.setup_db(dvs) + + # bring up interface + self.set_admin_status("Ethernet8", "up") + + # create interface + self.create_l3_intf("Ethernet8", "") + + # assign IP to interface + self.add_ip_address("Ethernet8", "10.0.0.1/24") + + # add neighbor + self.add_neighbor("Ethernet8", "169.254.0.0", "00:01:02:03:04:05") + + # check application database + tbl = swsscommon.Table(self.pdb, "NEIGH_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # check ASIC neighbor database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # remove neighbor + self.remove_neighbor("Ethernet8", "169.254.0.0") + + # remove IP from interface + self.remove_ip_address("Ethernet8", "10.0.0.1/24") + + # remove interface + self.remove_l3_intf("Ethernet8") + + # bring down interface + self.set_admin_status("Ethernet8", "down") + + # check application database + tbl = swsscommon.Table(self.pdb, "NEIGH_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # check ASIC neighbor database + tbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + # 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_nhg.py b/tests/test_nhg.py index 2d004f6c1b..94d581b47c 100644 --- a/tests/test_nhg.py +++ b/tests/test_nhg.py @@ -135,7 +135,7 @@ def config_intf(self, i): self.config_db.create_entry("INTERFACE", self.port_name(i), fvs) self.config_db.create_entry("INTERFACE", "{}|{}".format(self.port_name(i), self.port_ipprefix(i)), fvs) - self.dvs.runcmd("config interface startup " + self.port_name(i)) + self.dvs.port_admin_set(self.port_name(i), "up") self.dvs.runcmd("arp -s {} {}".format(self.peer_ip(i), self.port_mac(i))) assert self.dvs.servers[i].runcmd("ip link set down dev eth0") == 0 assert self.dvs.servers[i].runcmd("ip link set up dev eth0") == 0 diff --git a/tests/test_nvgre_tunnel.py b/tests/test_nvgre_tunnel.py new file mode 100644 index 0000000000..90fe560141 --- /dev/null +++ b/tests/test_nvgre_tunnel.py @@ -0,0 +1,381 @@ +import time +import json +import random +import time +import pytest + + +from swsscommon import swsscommon +from pprint import pprint + + +NVGRE_TUNNEL = 'NVGRE_TUNNEL' +NVGRE_TUNNEL_MAP = 'NVGRE_TUNNEL_MAP' + + +SAI_OBJECT_TYPE_TUNNEL = 'ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL' +SAI_OBJECT_TYPE_TUNNEL_MAP = 'ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP' +SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY = 'ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY' + + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + time.sleep(1) + + +def create_entry_tbl(db, table, separator, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + + +def delete_entry_tbl(db, table, key): + tbl = swsscommon.Table(db, table) + tbl._del(key) + time.sleep(1) + + +def get_all_created_entries(db, table, existed_entries): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) >= 0, "DB entries was't created" + new_entries.sort() + return new_entries + + +def get_created_entries(db, table, existed_entries, count): + new_entries = get_all_created_entries(db, table, existed_entries) + assert len(new_entries) == count, "Wrong number of created entries." + return new_entries + + +def get_exist_entries(dvs, table): + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(db, table) + return set(tbl.getKeys()) + + +def get_created_entry(db, table, existed_entries): + tbl = swsscommon.Table(db, table) + entries = set(tbl.getKeys()) + new_entries = list(entries - existed_entries) + assert len(new_entries) == 1, "Wrong number of created entries." + return new_entries[0] + + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + + +def get_lo(dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(asic_db, 'ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE') + + entries = tbl.getKeys() + lo_id = None + for entry in entries: + status, fvs = tbl.get(entry) + assert status, "Got an error when get a key" + for key, value in fvs: + if key == 'SAI_ROUTER_INTERFACE_ATTR_TYPE' and value == 'SAI_ROUTER_INTERFACE_TYPE_LOOPBACK': + lo_id = entry + break + else: + assert False, 'Don\'t found loopback id' + + return lo_id + + +def check_object(db, table, key, expected_attributes): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + assert key in keys, "The desired key is not presented" + + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + assert len(fvs) == len(expected_attributes), "Unexpected number of attributes" + + attr_keys = {entry[0] for entry in fvs} + + for name, value in fvs: + assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ + (value, name, expected_attributes[name]) + + +loopback_id = 0 + + +class NvgreTunnel(object): + tunnel_ids = set() + tunnel_map_ids = set() + tunnel_map_entry_ids = set() + tunnel_map_map = {} + tunnel = {} + + + def fetch_exist_entries(self, dvs): + self.tunnel_ids = get_exist_entries(dvs, SAI_OBJECT_TYPE_TUNNEL) + self.tunnel_map_ids = get_exist_entries(dvs, SAI_OBJECT_TYPE_TUNNEL_MAP) + self.tunnel_map_entry_ids = get_exist_entries(dvs, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY) + + global loopback_id + if not loopback_id: + loopback_id = get_lo(dvs) + + + def create_nvgre_tunnel(self, dvs, tunnel_name, src_ip): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + create_entry_tbl(conf_db, NVGRE_TUNNEL, '|', tunnel_name, [ ('src_ip', src_ip) ]) + time.sleep(1) + + + def check_nvgre_tunnel(self, dvs, tunnel_name, src_ip): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + global loopback_id + + tunnel_id = get_created_entry(asic_db, SAI_OBJECT_TYPE_TUNNEL, self.tunnel_ids) + tunnel_map_ids = get_created_entries(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, self.tunnel_map_ids, 4) + + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP) == (len(self.tunnel_map_ids) + 4), "The TUNNEL_MAP wasn't created" + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY) == len(self.tunnel_map_entry_ids), "The TUNNEL_MAP_ENTRY is created too early" + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL) == (len(self.tunnel_ids) + 1), "The TUNNEL wasn't created" + + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, tunnel_map_ids[0], { 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VLAN_ID_TO_VSID' }) + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, tunnel_map_ids[1], { 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_BRIDGE_IF_TO_VSID' }) + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, tunnel_map_ids[2], { 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VSID_TO_VLAN_ID' }) + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, tunnel_map_ids[3], { 'SAI_TUNNEL_MAP_ATTR_TYPE': 'SAI_TUNNEL_MAP_TYPE_VSID_TO_BRIDGE_IF' }) + + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL, tunnel_id, + { + 'SAI_TUNNEL_ATTR_TYPE': 'SAI_TUNNEL_TYPE_NVGRE', + 'SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE': loopback_id, + 'SAI_TUNNEL_ATTR_DECAP_MAPPERS': f'2:{tunnel_map_ids[2]},{tunnel_map_ids[3]}', + 'SAI_TUNNEL_ATTR_ENCAP_MAPPERS': f'2:{tunnel_map_ids[0]},{tunnel_map_ids[1]}', + 'SAI_TUNNEL_ATTR_ENCAP_SRC_IP': src_ip + } + ) + + self.tunnel_map_ids.update(tunnel_map_ids) + self.tunnel_ids.add(tunnel_id) + self.tunnel_map_map[tunnel_name] = tunnel_map_ids + self.tunnel[tunnel_name] = tunnel_id + + + def check_invalid_nvgre_tunnel(self, dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL) == len(self.tunnel_ids), "Invalid TUNNEL was created" + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP) == len(self.tunnel_map_ids), "Invalid TUNNEL_MAP was created" + + + def remove_nvgre_tunnel(self, dvs, tunnel_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + delete_entry_tbl(conf_db, NVGRE_TUNNEL, tunnel_name) + time.sleep(1) + + + def check_remove_nvgre_tunnel(self, dvs, tunnel_name): + self.fetch_exist_entries(dvs) + self.tunnel.pop(tunnel_name, None) + self.tunnel_map_map.pop(tunnel_name, None) + + + def create_nvgre_tunnel_map_entry(self, dvs, tunnel_name, tunnel_map_entry_name, vlan_id, vsid): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + create_entry_tbl( + conf_db, + NVGRE_TUNNEL_MAP, '|', f'{tunnel_name}|{tunnel_map_entry_name}', + [ + ('vsid', vsid), + ('vlan_id', f'Vlan{vlan_id}'), + ], + ) + time.sleep(1) + + + def check_nvgre_tunnel_map_entry(self, dvs, tunnel_name, vlan_id, vsid): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + if (self.tunnel_map_map.get(tunnel_name) is None): + tunnel_map_ids = get_created_entries(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP, self.tunnel_map_ids, 4) + else: + tunnel_map_ids = self.tunnel_map_map[tunnel_name] + + tunnel_map_entry_id = get_created_entries(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY, self.tunnel_map_entry_ids, 1) + + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY) == (len(self.tunnel_map_entry_ids) + 1), "The TUNNEL_MAP_ENTRY is created too early" + + check_object(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY, tunnel_map_entry_id[0], + { + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP_TYPE': 'SAI_TUNNEL_MAP_TYPE_VSID_TO_VLAN_ID', + 'SAI_TUNNEL_MAP_ENTRY_ATTR_TUNNEL_MAP': tunnel_map_ids[2], + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VSID_ID_KEY': vsid, + 'SAI_TUNNEL_MAP_ENTRY_ATTR_VLAN_ID_VALUE': vlan_id, + } + ) + + self.tunnel_map_entry_ids.update(tunnel_map_entry_id) + + + def check_invalid_nvgre_tunnel_map_entry(self, dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + assert how_many_entries_exist(asic_db, SAI_OBJECT_TYPE_TUNNEL_MAP_ENTRY) == len(self.tunnel_map_entry_ids), "Invalid TUNNEL_MAP_ENTRY was created" + + + def remove_nvgre_tunnel_map_entry(self, dvs, tunnel_name, tunnel_map_entry_name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + delete_entry_tbl(conf_db, NVGRE_TUNNEL_MAP, f'{tunnel_name}|{tunnel_map_entry_name}') + time.sleep(1) + + + def check_remove_nvgre_tunnel_map_entry(self, dvs): + self.fetch_exist_entries(dvs) + + +@pytest.mark.usefixtures('dvs_vlan_manager') +class TestNvgreTunnel(object): + + def get_nvgre_tunnel_obj(self): + return NvgreTunnel() + + + def test_nvgre_create_tunnel_map_entry(self, dvs, testlog): + try: + tunnel_name = 'tunnel_1' + tunnel_map_entry_name = 'entry_1' + src_ip = '10.0.0.1' + vlan_id = '500' + vsid = '850' + + nvgre_obj = self.get_nvgre_tunnel_obj() + nvgre_obj.fetch_exist_entries(dvs) + + self.dvs_vlan.create_vlan(vlan_id) + + nvgre_obj.create_nvgre_tunnel(dvs, tunnel_name, src_ip) + nvgre_obj.check_nvgre_tunnel(dvs, tunnel_name, src_ip) + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name, tunnel_map_entry_name, vlan_id, vsid) + nvgre_obj.check_nvgre_tunnel_map_entry(dvs, tunnel_name, vlan_id, vsid) + finally: + nvgre_obj.remove_nvgre_tunnel_map_entry(dvs, tunnel_name, tunnel_map_entry_name) + nvgre_obj.check_remove_nvgre_tunnel_map_entry(dvs) + + nvgre_obj.remove_nvgre_tunnel(dvs, tunnel_name) + nvgre_obj.check_remove_nvgre_tunnel(dvs, tunnel_name) + + self.dvs_vlan.remove_vlan(vlan_id) + + + def test_multiple_nvgre_tunnels_entries(self, dvs, testlog): + try: + tunnel_name_1 = 'tunnel_1' + tunnel_name_2 = 'tunnel_2' + tunnel_name_3 = 'tunnel_3' + entry_1 = 'entry_1' + entry_2 = 'entry_2' + entry_3 = 'entry_3' + entry_4 = 'entry_4' + + nvgre_obj = self.get_nvgre_tunnel_obj() + nvgre_obj.fetch_exist_entries(dvs) + + self.dvs_vlan.create_vlan('501') + self.dvs_vlan.create_vlan('502') + self.dvs_vlan.create_vlan('503') + self.dvs_vlan.create_vlan('504') + + nvgre_obj.create_nvgre_tunnel(dvs, tunnel_name_1, '10.0.0.1') + nvgre_obj.check_nvgre_tunnel(dvs, tunnel_name_1, '10.0.0.1') + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name_1, entry_1, '501', '801') + nvgre_obj.check_nvgre_tunnel_map_entry(dvs, tunnel_name_1, '501', '801') + + nvgre_obj.create_nvgre_tunnel(dvs, tunnel_name_2, '10.0.0.2') + nvgre_obj.check_nvgre_tunnel(dvs, tunnel_name_2, '10.0.0.2') + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name_2, entry_2, '502', '802') + nvgre_obj.check_nvgre_tunnel_map_entry(dvs, tunnel_name_2, '502', '802') + + nvgre_obj.create_nvgre_tunnel(dvs, tunnel_name_3, '10.0.0.3') + nvgre_obj.check_nvgre_tunnel(dvs, tunnel_name_3, '10.0.0.3') + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name_3, entry_3, '503', '803') + nvgre_obj.check_nvgre_tunnel_map_entry(dvs, tunnel_name_3, '503', '803') + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name_3, entry_4, '504', '804') + nvgre_obj.check_nvgre_tunnel_map_entry(dvs, tunnel_name_3, '504', '804') + finally: + nvgre_obj.remove_nvgre_tunnel_map_entry(dvs, tunnel_name_1, entry_1) + nvgre_obj.check_remove_nvgre_tunnel_map_entry(dvs) + + nvgre_obj.remove_nvgre_tunnel(dvs, tunnel_name_1) + nvgre_obj.check_remove_nvgre_tunnel(dvs, tunnel_name_1) + + nvgre_obj.remove_nvgre_tunnel_map_entry(dvs, tunnel_name_2, entry_2) + nvgre_obj.check_remove_nvgre_tunnel_map_entry(dvs) + + nvgre_obj.remove_nvgre_tunnel(dvs, tunnel_name_2) + nvgre_obj.check_remove_nvgre_tunnel(dvs, tunnel_name_2) + + nvgre_obj.remove_nvgre_tunnel_map_entry(dvs, tunnel_name_3, entry_3) + nvgre_obj.check_remove_nvgre_tunnel_map_entry(dvs) + + nvgre_obj.remove_nvgre_tunnel_map_entry(dvs, tunnel_name_3, entry_4) + nvgre_obj.check_remove_nvgre_tunnel_map_entry(dvs) + + nvgre_obj.remove_nvgre_tunnel(dvs, tunnel_name_3) + nvgre_obj.check_remove_nvgre_tunnel(dvs, tunnel_name_3) + + self.dvs_vlan.remove_vlan('501') + self.dvs_vlan.remove_vlan('502') + self.dvs_vlan.remove_vlan('503') + self.dvs_vlan.remove_vlan('504') + + + def test_invalid_nvgre_tunnel(self, dvs, testlog): + nvgre_obj = self.get_nvgre_tunnel_obj() + nvgre_obj.fetch_exist_entries(dvs) + + nvgre_obj.create_nvgre_tunnel(dvs, 'tunnel_1', '1111.1111.1111.1111') + nvgre_obj.check_invalid_nvgre_tunnel(dvs) + + + def test_invalid_nvgre_tunnel_map_entry(self, dvs, testlog): + try: + tunnel_name = 'tunnel_1' + tunnel_map_entry_name = 'entry_1' + src_ip = '10.0.0.1' + vlan_id = '500' + vsid = 'INVALID' + + nvgre_obj = self.get_nvgre_tunnel_obj() + nvgre_obj.fetch_exist_entries(dvs) + + self.dvs_vlan.create_vlan(vlan_id) + + nvgre_obj.create_nvgre_tunnel(dvs, tunnel_name, src_ip) + nvgre_obj.check_nvgre_tunnel(dvs, tunnel_name, src_ip) + + nvgre_obj.create_nvgre_tunnel_map_entry(dvs, tunnel_name, tunnel_map_entry_name, vlan_id, vsid) + nvgre_obj.check_invalid_nvgre_tunnel_map_entry(dvs) + finally: + nvgre_obj.remove_nvgre_tunnel(dvs, tunnel_name) + nvgre_obj.check_remove_nvgre_tunnel(dvs, tunnel_name) + + self.dvs_vlan.remove_vlan(vlan_id) + + +# Add Dummy always-pass test at end as workaroud +# for issue when Flaky fail on final test it invokes module tear-down before retrying +def test_nonflaky_dummy(): + pass diff --git a/tests/test_pbh.py b/tests/test_pbh.py index 328e8231bc..270e59d429 100644 --- a/tests/test_pbh.py +++ b/tests/test_pbh.py @@ -1,6 +1,8 @@ import pytest import logging +import test_flex_counters as flex_counter_module + PBH_HASH_FIELD_NAME = "inner_ip_proto" PBH_HASH_FIELD_HASH_FIELD = "INNER_IP_PROTOCOL" @@ -253,6 +255,226 @@ def test_PbhRuleCreationDeletion(self, testlog): self.dvs_pbh.verify_pbh_hash_field_count(0) +class TestPbhBasicEditFlows: + def test_PbhRuleUpdate(self, testlog): + try: + # PBH hash field + pbhlogger.info("Create PBH hash field: {}".format(PBH_HASH_FIELD_NAME)) + self.dvs_pbh.create_pbh_hash_field( + hash_field_name=PBH_HASH_FIELD_NAME, + hash_field=PBH_HASH_FIELD_HASH_FIELD, + sequence_id=PBH_HASH_FIELD_SEQUENCE_ID + ) + self.dvs_pbh.verify_pbh_hash_field_count(1) + + # PBH hash + pbhlogger.info("Create PBH hash: {}".format(PBH_HASH_NAME)) + self.dvs_pbh.create_pbh_hash( + hash_name=PBH_HASH_NAME, + hash_field_list=PBH_HASH_HASH_FIELD_LIST + ) + self.dvs_pbh.verify_pbh_hash_count(1) + + # PBH table + pbhlogger.info("Create PBH table: {}".format(PBH_TABLE_NAME)) + self.dvs_pbh.create_pbh_table( + table_name=PBH_TABLE_NAME, + interface_list=PBH_TABLE_INTERFACE_LIST, + description=PBH_TABLE_DESCRIPTION + ) + self.dvs_acl.verify_acl_table_count(1) + + # PBH rule + attr_dict = { + "ether_type": PBH_RULE_ETHER_TYPE, + "ip_protocol": PBH_RULE_IP_PROTOCOL, + "gre_key": PBH_RULE_GRE_KEY, + "inner_ether_type": PBH_RULE_INNER_ETHER_TYPE + } + + pbhlogger.info("Create PBH rule: {}".format(PBH_RULE_NAME)) + self.dvs_pbh.create_pbh_rule( + table_name=PBH_TABLE_NAME, + rule_name=PBH_RULE_NAME, + priority=PBH_RULE_PRIORITY, + qualifiers=attr_dict, + hash_name=PBH_RULE_HASH + ) + self.dvs_acl.verify_acl_rule_count(1) + + attr_dict = { + "ether_type": "0x86dd", + "ipv6_next_header": "0x2f", + "inner_ether_type": "0x0800" + } + + pbhlogger.info("Update PBH rule: {}".format(PBH_RULE_NAME)) + self.dvs_pbh.update_pbh_rule( + table_name=PBH_TABLE_NAME, + rule_name=PBH_RULE_NAME, + priority="100", + qualifiers=attr_dict, + hash_name=PBH_RULE_HASH, + packet_action="SET_LAG_HASH", + flow_counter="ENABLED" + ) + + hash_id = self.dvs_pbh.get_pbh_hash_ids(1)[0] + counter_id = self.dvs_acl.get_acl_counter_ids(1)[0] + + sai_attr_dict = { + "SAI_ACL_ENTRY_ATTR_PRIORITY": self.dvs_acl.get_simple_qualifier_comparator("100"), + "SAI_ACL_ENTRY_ATTR_FIELD_ETHER_TYPE": self.dvs_acl.get_simple_qualifier_comparator("34525&mask:0xffff"), + "SAI_ACL_ENTRY_ATTR_FIELD_IP_PROTOCOL": self.dvs_acl.get_simple_qualifier_comparator("disabled"), + "SAI_ACL_ENTRY_ATTR_FIELD_IPV6_NEXT_HEADER": self.dvs_acl.get_simple_qualifier_comparator("47&mask:0xff"), + "SAI_ACL_ENTRY_ATTR_FIELD_GRE_KEY": self.dvs_acl.get_simple_qualifier_comparator("disabled"), + "SAI_ACL_ENTRY_ATTR_FIELD_INNER_ETHER_TYPE": self.dvs_acl.get_simple_qualifier_comparator("2048&mask:0xffff"), + "SAI_ACL_ENTRY_ATTR_ACTION_SET_ECMP_HASH_ID": self.dvs_acl.get_simple_qualifier_comparator("disabled"), + "SAI_ACL_ENTRY_ATTR_ACTION_SET_LAG_HASH_ID": self.dvs_acl.get_simple_qualifier_comparator(hash_id), + "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": self.dvs_acl.get_simple_qualifier_comparator(counter_id) + } + + self.dvs_acl.verify_acl_rule_generic( + sai_qualifiers=sai_attr_dict + ) + + finally: + # PBH rule + pbhlogger.info("Remove PBH rule: {}".format(PBH_RULE_NAME)) + self.dvs_pbh.remove_pbh_rule(PBH_TABLE_NAME, PBH_RULE_NAME) + self.dvs_acl.verify_acl_rule_count(0) + + # PBH table + pbhlogger.info("Remove PBH table: {}".format(PBH_TABLE_NAME)) + self.dvs_pbh.remove_pbh_table(PBH_TABLE_NAME) + self.dvs_acl.verify_acl_table_count(0) + + # PBH hash + pbhlogger.info("Remove PBH hash: {}".format(PBH_HASH_NAME)) + self.dvs_pbh.remove_pbh_hash(PBH_HASH_NAME) + self.dvs_pbh.verify_pbh_hash_count(0) + + # PBH hash field + pbhlogger.info("Remove PBH hash field: {}".format(PBH_HASH_FIELD_NAME)) + self.dvs_pbh.remove_pbh_hash_field(PBH_HASH_FIELD_NAME) + self.dvs_pbh.verify_pbh_hash_field_count(0) + + + def test_PbhRuleUpdateFlowCounter(self, dvs, testlog): + try: + # PBH hash field + pbhlogger.info("Create PBH hash field: {}".format(PBH_HASH_FIELD_NAME)) + self.dvs_pbh.create_pbh_hash_field( + hash_field_name=PBH_HASH_FIELD_NAME, + hash_field=PBH_HASH_FIELD_HASH_FIELD, + sequence_id=PBH_HASH_FIELD_SEQUENCE_ID + ) + self.dvs_pbh.verify_pbh_hash_field_count(1) + + # PBH hash + pbhlogger.info("Create PBH hash: {}".format(PBH_HASH_NAME)) + self.dvs_pbh.create_pbh_hash( + hash_name=PBH_HASH_NAME, + hash_field_list=PBH_HASH_HASH_FIELD_LIST + ) + self.dvs_pbh.verify_pbh_hash_count(1) + + # PBH table + pbhlogger.info("Create PBH table: {}".format(PBH_TABLE_NAME)) + self.dvs_pbh.create_pbh_table( + table_name=PBH_TABLE_NAME, + interface_list=PBH_TABLE_INTERFACE_LIST, + description=PBH_TABLE_DESCRIPTION + ) + self.dvs_acl.verify_acl_table_count(1) + + # Prepare ACL FLEX Counter environment + meta_data = flex_counter_module.counter_group_meta['acl_counter'] + counter_key = meta_data['key'] + counter_stat = meta_data['group_name'] + counter_map = meta_data['name_map'] + + test_flex_counters = flex_counter_module.TestFlexCounters() + test_flex_counters.setup_dbs(dvs) + test_flex_counters.verify_no_flex_counters_tables(counter_stat) + + # PBH rule + pbhlogger.info("Create PBH rule: {}".format(PBH_RULE_NAME)) + + attr_dict = { + "ether_type": PBH_RULE_ETHER_TYPE, + "ip_protocol": PBH_RULE_IP_PROTOCOL, + "gre_key": PBH_RULE_GRE_KEY, + "inner_ether_type": PBH_RULE_INNER_ETHER_TYPE + } + + self.dvs_pbh.create_pbh_rule( + table_name=PBH_TABLE_NAME, + rule_name=PBH_RULE_NAME, + priority=PBH_RULE_PRIORITY, + qualifiers=attr_dict, + hash_name=PBH_RULE_HASH, + packet_action="SET_ECMP_HASH", + flow_counter="ENABLED" + ) + self.dvs_acl.verify_acl_rule_count(1) + self.dvs_acl.get_acl_counter_ids(1) + + pbhlogger.info("Enable a ACL FLEX counter") + test_flex_counters.set_flex_counter_group_status(counter_key, counter_map) + test_flex_counters.set_flex_counter_group_interval(counter_key, counter_stat, '1000') + test_flex_counters.verify_flex_counters_populated(counter_map, counter_stat) + + pbhlogger.info("Disable a flow counter for PBH rule: {}".format(PBH_RULE_NAME)) + self.dvs_pbh.update_pbh_rule( + table_name=PBH_TABLE_NAME, + rule_name=PBH_RULE_NAME, + priority=PBH_RULE_PRIORITY, + qualifiers=attr_dict, + hash_name=PBH_RULE_HASH, + packet_action="SET_ECMP_HASH", + flow_counter="DISABLED" + ) + self.dvs_acl.get_acl_counter_ids(0) + + pbhlogger.info("Enable a flow counter for PBH rule: {}".format(PBH_RULE_NAME)) + self.dvs_pbh.update_pbh_rule( + table_name=PBH_TABLE_NAME, + rule_name=PBH_RULE_NAME, + priority=PBH_RULE_PRIORITY, + qualifiers=attr_dict, + hash_name=PBH_RULE_HASH, + packet_action="SET_ECMP_HASH", + flow_counter="ENABLED" + ) + self.dvs_acl.get_acl_counter_ids(1) + + finally: + # PBH rule + pbhlogger.info("Remove PBH rule: {}".format(PBH_RULE_NAME)) + self.dvs_pbh.remove_pbh_rule(PBH_TABLE_NAME, PBH_RULE_NAME) + self.dvs_acl.verify_acl_rule_count(0) + + # PBH table + pbhlogger.info("Remove PBH table: {}".format(PBH_TABLE_NAME)) + self.dvs_pbh.remove_pbh_table(PBH_TABLE_NAME) + self.dvs_acl.verify_acl_table_count(0) + + # PBH hash + pbhlogger.info("Remove PBH hash: {}".format(PBH_HASH_NAME)) + self.dvs_pbh.remove_pbh_hash(PBH_HASH_NAME) + self.dvs_pbh.verify_pbh_hash_count(0) + + # PBH hash field + pbhlogger.info("Remove PBH hash field: {}".format(PBH_HASH_FIELD_NAME)) + self.dvs_pbh.remove_pbh_hash_field(PBH_HASH_FIELD_NAME) + self.dvs_pbh.verify_pbh_hash_field_count(0) + + # ACL FLEX counter + pbhlogger.info("Disable ACL FLEX counter") + test_flex_counters.post_trap_flow_counter_test(meta_data) + + @pytest.mark.usefixtures("dvs_lag_manager") class TestPbhExtendedFlows: class PbhRefCountHelper(object): diff --git a/tests/test_pfcwd.py b/tests/test_pfcwd.py index 78cd851574..c88b6f6e96 100644 --- a/tests/test_pfcwd.py +++ b/tests/test_pfcwd.py @@ -103,7 +103,7 @@ def setup_test(self, dvs): # set cable len to non zero value. if port is down, default cable len is 0 self.set_cable_len(port, "5m") # startup port - dvs.runcmd("config interface startup {}".format(port)) + dvs.port_admin_set(port, "up") # enable pfcwd self.set_flex_counter_status("PFCWD", "enable") @@ -120,7 +120,7 @@ def teardown_test(self, dvs): if self.orig_cable_len: self.set_cable_len(port, self.orig_cable_len[port]) # shutdown port - dvs.runcmd("config interface shutdown {}".format(port)) + dvs.port_admin_set(port, "down") def get_db_handle(self, dvs): self.app_db = dvs.get_app_db() @@ -148,9 +148,11 @@ def _get_bitmask(self, queues): return str(mask) def set_ports_pfc(self, status='enable', pfc_queues=[3,4]): + keyname = 'pfcwd_sw_enable' for port in self.test_ports: if 'enable' in status: - fvs = {'pfc_enable': ",".join([str(q) for q in pfc_queues])} + queues = ",".join([str(q) for q in pfc_queues]) + fvs = {keyname: queues, 'pfc_enable': queues} self.config_db.create_entry("PORT_QOS_MAP", port, fvs) else: self.config_db.delete_entry("PORT_QOS_MAP", port) @@ -212,7 +214,7 @@ def set_storm_state(self, queues, state="enabled"): queue_name = port + ":" + str(queue) self.counters_db.update_entry("COUNTERS", self.queue_oids[queue_name], fvs) - def test_pfcwd_single_queue(self, dvs, setup_teardown_test): + def test_pfcwd_software_single_queue(self, dvs, setup_teardown_test): try: # enable PFC on queues test_queues = [3, 4] @@ -253,7 +255,7 @@ def test_pfcwd_single_queue(self, dvs, setup_teardown_test): self.reset_pfcwd_counters(storm_queue) self.stop_pfcwd_on_ports() - def test_pfcwd_multi_queue(self, dvs, setup_teardown_test): + def test_pfcwd_software_multi_queue(self, dvs, setup_teardown_test): try: # enable PFC on queues test_queues = [3, 4] diff --git a/tests/test_pg_drop_counter.py b/tests/test_pg_drop_counter.py index 1cdd834747..b3682881de 100644 --- a/tests/test_pg_drop_counter.py +++ b/tests/test_pg_drop_counter.py @@ -9,6 +9,9 @@ pg_drop_attr = "SAI_INGRESS_PRIORITY_GROUP_STAT_DROPPED_PACKETS" +PORT = "Ethernet0" + +@pytest.mark.usefixtures('dvs_port_manager') class TestPGDropCounter(object): DEFAULT_POLL_INTERVAL = 10 pgs = {} @@ -73,8 +76,7 @@ def clear_flex_counter(self): 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): self.setup_dbs(dvs) self.pgs = self.asic_db.get_keys("ASIC_STATE:SAI_OBJECT_TYPE_INGRESS_PRIORITY_GROUP") @@ -94,3 +96,49 @@ def test_pg_drop_counters(self, dvs): self.verify_value(dvs, self.pgs, pg_drop_attr, "123") finally: self.clear_flex_counter() + + def test_pg_drop_counter_port_add_remove(self, dvs): + self.setup_dbs(dvs) + + try: + # configure pg drop flex counter + self.set_up_flex_counter() + + # receive port info + fvs = self.config_db.get_entry("PORT", PORT) + assert len(fvs) > 0 + + # save all the oids of the pg drop counters + oid_list = [] + for priority in range(0,7): + oid_list.append(dvs.get_counters_db().get_entry("COUNTERS_PG_NAME_MAP", "")["%s:%d"%(PORT, priority)]) + # verify that counters exists on flex counter + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", "PG_WATERMARK_STAT_COUNTER:%s"%oid_list[-1]) + assert len(fields) == 1 + + # remove port + port_oid = self.counters_db.get_entry("COUNTERS_PORT_NAME_MAP", "")[PORT] + self.dvs_port.remove_port(PORT) + dvs.get_asic_db().wait_for_deleted_entry("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid) + + # verify counters were removed from flex counter table + for oid in oid_list: + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", "PG_WATERMARK_STAT_COUNTER:%s"%oid) + assert len(fields) == 0 + + # add port and wait until the port is added on asic db + num_of_keys_without_port = len(dvs.get_asic_db().get_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT")) + self.config_db.create_entry("PORT", PORT, fvs) + dvs.get_asic_db().wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_PORT", num_of_keys_without_port + 1) + dvs.get_counters_db().wait_for_fields("COUNTERS_PG_NAME_MAP", "", ["%s:0"%(PORT)]) + + # verify counter was added + for priority in range(0,7): + oid = dvs.get_counters_db().get_entry("COUNTERS_PG_NAME_MAP", "")["%s:%d"%(PORT, priority)] + + # verify that counters exists on flex counter + fields = self.flex_db.get_entry("FLEX_COUNTER_TABLE", "PG_WATERMARK_STAT_COUNTER:%s"%oid) + assert len(fields) == 1 + + finally: + self.clear_flex_counter() diff --git a/tests/test_port.py b/tests/test_port.py index 4766c87deb..c63dae5c57 100644 --- a/tests/test_port.py +++ b/tests/test_port.py @@ -59,11 +59,11 @@ def test_PortMtu(self, dvs, testlog): assert fv[1] == "9100" def test_PortNotification(self, dvs, testlog): - dvs.runcmd("config interface startup Ethernet0") - dvs.runcmd("config interface ip add Ethernet0 10.0.0.0/31") + dvs.port_admin_set("Ethernet0", "up") + dvs.interface_ip_add("Ethernet0", "10.0.0.0/31") - dvs.runcmd("config interface startup Ethernet4") - dvs.runcmd("config interface ip add Ethernet4 10.0.0.2/31") + dvs.port_admin_set("Ethernet4", "up") + dvs.interface_ip_add("Ethernet4", "10.0.0.2/31") dvs.servers[0].runcmd("ip link set down dev eth0") == 0 @@ -126,11 +126,11 @@ def test_PortFecForce(self, dvs, testlog): adb.wait_for_field_match("ASIC_STATE:SAI_OBJECT_TYPE_PORT", port_oid, expected_fields) def test_PortFec(self, dvs, testlog): - dvs.runcmd("config interface startup Ethernet0") - dvs.runcmd("config interface ip add Ethernet0 10.0.0.0/31") + dvs.port_admin_set("Ethernet0", "up") + dvs.interface_ip_add("Ethernet0", "10.0.0.0/31") - dvs.runcmd("config interface startup Ethernet4") - dvs.runcmd("config interface ip add Ethernet4 10.0.0.2/31") + dvs.port_admin_set("Ethernet4", "up") + dvs.interface_ip_add("Ethernet4", "10.0.0.2/31") dvs.servers[0].runcmd("ip link set down dev eth0") == 0 diff --git a/tests/test_port_an.py b/tests/test_port_an.py index 93add09b9a..dc98f43d0e 100644 --- a/tests/test_port_an.py +++ b/tests/test_port_an.py @@ -254,9 +254,8 @@ def test_PortAutoNegWarm(self, dvs, testlog): cfvs = swsscommon.FieldValuePairs([("admin_status", "up")]) ctbl.set("Ethernet0", cfvs) - # enable warm restart - (exitcode, result) = dvs.runcmd("config warm_restart enable swss") - assert exitcode == 0 + + dvs.warm_restart_swss("true") # freeze orchagent for warm restart (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") @@ -290,9 +289,9 @@ def test_PortAutoNegWarm(self, dvs, testlog): finally: # disable warm restart - dvs.runcmd("config warm_restart disable swss") + dvs.warm_restart_swss("disable") # slow down crm polling - dvs.runcmd("crm config polling interval 10000") + dvs.crm_poll_set("10000") # Add Dummy always-pass test at end as workaroud diff --git a/tests/test_port_dpb_vlan.py b/tests/test_port_dpb_vlan.py index df03a5ecf9..e6f89beb1a 100644 --- a/tests/test_port_dpb_vlan.py +++ b/tests/test_port_dpb_vlan.py @@ -52,6 +52,7 @@ def test_dependency(self, dvs): self.dvs_vlan.remove_vlan(vlan) self.dvs_vlan.get_and_verify_vlan_ids(0) + @pytest.mark.skip(reason="Failing. Under investigation") def test_one_port_one_vlan(self, dvs): dpb = DPB() vlan = "100" @@ -117,6 +118,7 @@ def test_one_port_one_vlan(self, dvs): self.dvs_vlan.remove_vlan(vlan) self.dvs_vlan.get_and_verify_vlan_ids(0) + @pytest.mark.skip(reason="Failing. Under investigation") def test_one_port_multiple_vlan(self, dvs): dpb = DPB() @@ -182,6 +184,7 @@ def test_one_port_multiple_vlan(self, dvs): self.dvs_vlan.remove_vlan("102") self.dvs_vlan.get_and_verify_vlan_ids(0) + @pytest.mark.skip(reason="Failing. Under investigation") def test_all_port_10_vlans(self, dvs): num_vlans = 10 start_vlan = 100 diff --git a/tests/test_portchannel.py b/tests/test_portchannel.py index ee612ec46d..3e24b6a340 100644 --- a/tests/test_portchannel.py +++ b/tests/test_portchannel.py @@ -382,6 +382,63 @@ def test_Portchannel_tpid(self, dvs, testlog): tbl._del("PortChannel0002") time.sleep(1) + def test_portchannel_member_netdev_oper_status(self, dvs, testlog): + config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + # create port-channel + tbl = swsscommon.Table(config_db, "PORTCHANNEL") + fvs = swsscommon.FieldValuePairs([("admin_status", "up"),("mtu", "9100"),("oper_status", "up")]) + tbl.set("PortChannel111", fvs) + + # set port-channel oper status + tbl = swsscommon.ProducerStateTable(app_db, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin_status", "up"),("mtu", "9100"),("oper_status", "up")]) + tbl.set("PortChannel111", fvs) + + # add members to port-channel + tbl = swsscommon.Table(config_db, "PORTCHANNEL_MEMBER") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + tbl.set("PortChannel111|Ethernet0", fvs) + tbl.set("PortChannel111|Ethernet4", fvs) + + # wait for port-channel netdev creation + time.sleep(1) + + # set netdev oper status + (exitcode, _) = dvs.runcmd("ip link set up dev Ethernet0") + assert exitcode == 0, "ip link set failed" + + (exitcode, _) = dvs.runcmd("ip link set up dev Ethernet4") + assert exitcode == 0, "ip link set failed" + + (exitcode, _) = dvs.runcmd("ip link set dev PortChannel111 carrier on") + assert exitcode == 0, "ip link set failed" + + # verify port-channel members netdev oper status + tbl = swsscommon.Table(state_db, "PORT_TABLE") + status, fvs = tbl.get("Ethernet0") + assert status is True + fvs = dict(fvs) + assert fvs['netdev_oper_status'] == 'up' + + status, fvs = tbl.get("Ethernet4") + assert status is True + fvs = dict(fvs) + assert fvs['netdev_oper_status'] == 'up' + + # remove port-channel members + tbl = swsscommon.Table(config_db, "PORTCHANNEL_MEMBER") + tbl._del("PortChannel111|Ethernet0") + tbl._del("PortChannel111|Ethernet4") + + # remove port-channel + tbl = swsscommon.Table(config_db, "PORTCHANNEL") + tbl._del("PortChannel111") + + # wait for port-channel deletion + time.sleep(1) # Add Dummy always-pass test at end as workaroud # for issue when Flaky fail on final test it invokes module tear-down before retrying diff --git a/tests/test_route.py b/tests/test_route.py index 9c56ef52a8..ed96a34bde 100644 --- a/tests/test_route.py +++ b/tests/test_route.py @@ -604,6 +604,7 @@ def test_RouteAddRemoveIpv6RouteUnresolvedNeigh(self, dvs, testlog): dvs.servers[1].runcmd("ip -6 route del default dev eth0") dvs.servers[1].runcmd("ip -6 address del 2001::2/64 dev eth0") + @pytest.mark.skip(reason="Failing. Under investigation") def test_RouteAddRemoveIpv4RouteWithVrf(self, dvs, testlog): self.setup_db(dvs) diff --git a/tests/test_sflow.py b/tests/test_sflow.py index e3c95a6946..f6ab6a3c13 100644 --- a/tests/test_sflow.py +++ b/tests/test_sflow.py @@ -146,7 +146,6 @@ def test_SamplingRatePortCfgUpdate(self, dvs, testlog): ''' self.setup_sflow(dvs) appldb = dvs.get_app_db() - #dvs.runcmd("portconfig -p {} -s {}".format("Ethernet0", "25000")) self.cdb.update_entry("PORT", "Ethernet0", {'speed' : "25000"}) expected_fields = {"sample_rate": self.speed_rate_table["25000"]} appldb.wait_for_field_match("SFLOW_SESSION_TABLE", "Ethernet0", expected_fields) diff --git a/tests/test_storm_control.py b/tests/test_storm_control.py new file mode 100644 index 0000000000..76deef9268 --- /dev/null +++ b/tests/test_storm_control.py @@ -0,0 +1,316 @@ +from swsscommon import swsscommon +import os +import sys +import time +import json +from distutils.version import StrictVersion +import pytest + +class TestStormControl(object): + def setup_db(self,dvs): + self.pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + self.adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + self.cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + self.sdb = swsscommon.DBConnector(6, dvs.redis_sock, 0) + dvs.runcmd(['sh', '-c', "echo 0 > /var/log/syslog"]) + + def create_port_channel(self, dvs, lag_name): + dvs.runcmd("config portchannel add " + lag_name) + time.sleep(1) + + def delete_port_channel(self, dvs, lag_name): + dvs.runcmd("config portchannel del " + lag_name) + time.sleep(1) + + def add_port_channel_member(self, dvs, lag_name, member): + dvs.runcmd("config portchannel member add "+ lag_name + " "+ member) + time.sleep(1) + + def remove_port_channel_member(self, dvs, lag_name, member): + dvs.runcmd("config portchannel member del "+ lag_name + " "+ member) + time.sleep(1) + + def create_vlan(self, dvs, vlan): + dvs.runcmd("config vlan add " + vlan) + time.sleep(1) + + def delete_vlan(self, dvs, vlan): + dvs.runcmd("config vlan del " + vlan) + time.sleep(1) + + def add_vlan_member(self, dvs, vlan, interface): + dvs.runcmd("config vlan member add " + vlan + " " + interface) + time.sleep(1) + + def remove_vlan_member(self, dvs, vlan, interface): + dvs.runcmd("config vlan member del " + vlan + " " + interface) + time.sleep(1) + + def add_storm_session(self, if_name, storm_type, kbps_value): + tbl = swsscommon.Table(self.cdb, "PORT_STORM_CONTROL") + fvs = swsscommon.FieldValuePairs([("kbps", str(kbps_value))]) + key = if_name + "|" + storm_type + tbl.set(key,fvs) + time.sleep(1) + + def delete_storm_session(self, if_name, storm_type): + tbl = swsscommon.Table(self.cdb, "PORT_STORM_CONTROL") + key = if_name + "|" + storm_type + tbl._del(key) + time.sleep(1) + + def test_bcast_storm(self,dvs,testlog): + self.setup_db(dvs) + + if_name = "Ethernet0" + storm_type = "broadcast" + #User input is Kbps + #Orchagent converts the value to CIR as below and programs the ASIC DB + #kbps_value * 1000 / 8 + kbps_value = 1000000 + self.add_storm_control_on_interface(dvs,if_name,storm_type,kbps_value) + self.del_storm_control(dvs,if_name,storm_type) + + def del_storm_control(self, dvs, if_name, storm_type): + self.setup_db(dvs) + port_oid = dvs.asicdb.portnamemap[if_name] + atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + status, fvs = atbl.get(dvs.asicdb.portnamemap[if_name]) + assert status == True + + storm_type_port_attr = self.get_port_attr_for_storm_type(storm_type) + + policer_oid = 0 + for fv in fvs: + if fv[0] == storm_type_port_attr: + policer_oid = fv[1] + + self.delete_storm_session(if_name, storm_type) + tbl = swsscommon.Table(self.cdb, "PORT_STORM_CONTROL") + (status,fvs) = tbl.get(if_name+"|"+storm_type) + assert status == False + + atbl = swsscommon.Table(self.adb,"ASIC_STATE:SAI_OBJECT_TYPE_PORT") + status, fvs = atbl.get(dvs.asicdb.portnamemap[if_name]) + assert status == True + + for fv in fvs: + if fv[0] == storm_type_port_attr: + assert fv[1] == "oid:0x0" + + if policer_oid != 0: + atbl = swsscommon.Table(self.adb,"ASIC_STATE:SAI_OBJECT_TYPE_POLICER") + status, fvs = atbl.get(policer_oid) + assert status == False + + def test_uucast_storm(self,dvs,testlog): + self.setup_db(dvs) + + if_name = "Ethernet0" + storm_type = "unknown-unicast" + #User input is Kbps + #Orchagent converts the value to CIR as below and programs the ASIC DB + #kbps_value * 1000 / 8 + kbps_value = 1000000 + + self.add_storm_control_on_interface(dvs,if_name,storm_type,kbps_value) + self.del_storm_control(dvs,if_name,storm_type) + + def test_umcast_storm(self,dvs,testlog): + self.setup_db(dvs) + + if_name = "Ethernet0" + storm_type = "unknown-multicast" + #User input is Kbps + #Orchagent converts the value to CIR as below and programs the ASIC DB + #kbps_value * 1000 / 8 + kbps_value = 1000000 + + self.add_storm_control_on_interface(dvs,if_name,storm_type,kbps_value) + self.del_storm_control(dvs,if_name,storm_type) + + def get_port_attr_for_storm_type(self,storm_type): + port_attr = "" + if storm_type == "broadcast": + port_attr = "SAI_PORT_ATTR_BROADCAST_STORM_CONTROL_POLICER_ID" + elif storm_type == "unknown-unicast": + port_attr = "SAI_PORT_ATTR_FLOOD_STORM_CONTROL_POLICER_ID" + elif storm_type == "unknown-multicast": + port_attr = "SAI_PORT_ATTR_MULTICAST_STORM_CONTROL_POLICER_ID" + + return port_attr + + def check_storm_control_on_interface(self,dvs,if_name,storm_type,kbps_value): + print ("interface {} storm_type {} kbps {}".format(if_name,storm_type, kbps_value)) + tbl = swsscommon.Table(self.cdb,"PORT_STORM_CONTROL") + (status,fvs) = tbl.get(if_name+"|"+storm_type) + + assert status == True + assert len(fvs) > 0 + + port_oid = dvs.asicdb.portnamemap[if_name] + + atbl = swsscommon.Table(self.adb,"ASIC_STATE:SAI_OBJECT_TYPE_PORT") + status, fvs = atbl.get(dvs.asicdb.portnamemap[if_name]) + assert status == True + + policer_oid = 0 + + storm_type_port_attr = self.get_port_attr_for_storm_type(storm_type) + + for fv in fvs: + if fv[0] == storm_type_port_attr: + assert fv[1] != "oid:0x0" + policer_oid = fv[1] + + if policer_oid != 0: + atbl = swsscommon.Table(self.adb,"ASIC_STATE:SAI_OBJECT_TYPE_POLICER") + status, fvs = atbl.get(policer_oid) + assert status == True + + bps = 0 + + for fv in fvs: + if fv[0] == "SAI_POLICER_ATTR_CIR": + bps = fv[1] + + #Retrieved value of bps from ASIC_DB is converted back to user input kbps + kbps = int(int(bps) / int(1000) * 8) + print ("Kbps value {}".format(kbps)) + + assert str(kbps) == str(kbps_value) + + + def add_storm_control_on_interface(self,dvs,if_name,storm_type,kbps_value): + print ("interface {} storm_type {} kbps {}".format(if_name,storm_type,kbps_value)) + self.add_storm_session(if_name, storm_type, kbps_value) + self.check_storm_control_on_interface(dvs,if_name,storm_type,kbps_value) + + def test_add_storm_all_interfaces(self,dvs,testlog): + self.setup_db(dvs) + + tbl = swsscommon.Table(self.cdb,"PORT") + for key in tbl.getKeys(): + self.add_storm_control_on_interface(dvs,key,"broadcast",1000000) + self.add_storm_control_on_interface(dvs,key,"unknown-unicast",2000000) + self.add_storm_control_on_interface(dvs,key,"unknown-multicast",3000000) + self.del_storm_control(dvs,key,"broadcast") + self.del_storm_control(dvs,key,"unknown-unicast") + self.del_storm_control(dvs,key,"unknown-multicast") + + def test_warm_restart_all_interfaces(self,dvs,testlog): + self.setup_db(dvs) + + tbl = swsscommon.Table(self.cdb,"PORT") + for key in tbl.getKeys(): + self.add_storm_control_on_interface(dvs,key,"broadcast",1000000) + self.add_storm_control_on_interface(dvs,key,"unknown-unicast",2000000) + self.add_storm_control_on_interface(dvs,key,"unknown-multicast",3000000) + #dvs.runcmd("config save -y") + # enable warm restart + dvs.warm_restart_swss("true") + + # freeze orchagent for warm restart + (exitcode, result) = dvs.runcmd("/usr/bin/orchagent_restart_check") + assert result == "RESTARTCHECK succeeded\n" + time.sleep(2) + + dvs.stop_swss() + time.sleep(10) + dvs.start_swss() + time.sleep(10) + + for key in tbl.getKeys(): + self.check_storm_control_on_interface(dvs,key,"broadcast",1000000) + self.check_storm_control_on_interface(dvs,key,"unknown-unicast",2000000) + self.check_storm_control_on_interface(dvs,key,"unknown-multicast",3000000) + self.del_storm_control(dvs,key,"broadcast") + self.del_storm_control(dvs,key,"unknown-unicast") + self.del_storm_control(dvs,key,"unknown-multicast") + # disable warm restart + dvs.warm_restart_swss("false") + + def test_add_storm_lag_interface(self,dvs,testlog): + self.setup_db(dvs) + lag_name = "PortChannel10" + member_interface = "Ethernet0" + kbps_value = 1000000 + storm_list = ["broadcast","unknown-unicast","unknown-multicast"] + kbps_value_list = [1000000,2000000,3000000] + + #Create LAG interface and add member + self.create_port_channel(dvs,lag_name) + self.add_port_channel_member(dvs,lag_name,member_interface) + + #click CLI verification + #for storm_type in storm_list: + # dvs.runcmd("config interface storm-control add "+lag_name+" "+storm_type+" "+str(kbps_value)) + # tbl = swsscommon.Table(self.cdb,"PORT_STORM_CONTROL") + # (status,fvs) = tbl.get(lag_name+"|"+storm_type) + # assert status == False + # assert len(fvs) == 0 + + #Orchagent verification + storm_list_db = ["broadcast","unknown-unicast","unknown-multicast"] + for storm_type,kbps_value in zip(storm_list_db,kbps_value_list): + #Cleanup syslog + dvs.runcmd(['sh', '-c', "echo 0 > /var/log/syslog"]) + time.sleep(1) + print ("storm type: {} kbps value: {}".format(storm_type,kbps_value)) + #Add storm entry to config DB directly + self.add_storm_session(lag_name,storm_type,kbps_value) + tbl = swsscommon.Table(self.cdb,"PORT_STORM_CONTROL") + (status,fvs) = tbl.get(lag_name+"|"+storm_type) + assert status == True + assert len(fvs) > 0 + time.sleep(1) + #grep for error message in syslog + (exitcode,num) = dvs.runcmd(['sh', '-c', 'cat /var/log/syslog | grep -i "handlePortStormControlTable: {}: Unsupported / Invalid interface PortChannel10"'.format(storm_type)]) + time.sleep(1) + assert exitcode == 0 + self.delete_storm_session(lag_name, storm_type) + self.remove_port_channel_member(dvs,lag_name,member_interface) + self.delete_port_channel(dvs,lag_name) + + def test_add_storm_vlan_interface(self,dvs,testlog): + self.setup_db(dvs) + vlan_id = 99 + member_interface = "Ethernet4" + kbps_value = 1000000 + storm_list = ["broadcast","unknown-unicast","unknown-multicast"] + kbps_value_list = [1000000,2000000,3000000] + vlan_name = "Vlan"+str(vlan_id) + + #Create VLAN interface and add member + self.create_vlan(dvs,str(vlan_id)) + self.add_vlan_member(dvs,str(vlan_id),member_interface) + + #click CLI verification + #for storm_type in storm_list: + # dvs.runcmd("config interface storm-control add Vlan"+str(vlan_id)+" "+storm_type+" "+str(kbps_value)) + # tbl = swsscommon.Table(self.cdb,"PORT_STORM_CONTROL") + # (status,fvs) = tbl.get("Vlan"+str(vlan_id)+"|"+storm_type) + # assert status == False + # assert len(fvs) == 0 + + #Orchagent verification + storm_list_db = ["broadcast","unknown-unicast","unknown-multicast"] + for storm_type,kbps_value in zip(storm_list_db,kbps_value_list): + #Cleanup syslog + dvs.runcmd(['sh', '-c', "echo 0 > /var/log/syslog"]) + time.sleep(1) + print ("storm type: {} kbps value: {}".format(storm_type,kbps_value)) + #Add storm entry to config DB directly + self.add_storm_session(vlan_name,storm_type,kbps_value) + tbl = swsscommon.Table(self.cdb,"PORT_STORM_CONTROL") + (status,fvs) = tbl.get(vlan_name+"|"+storm_type) + assert status == True + assert len(fvs) > 0 + time.sleep(1) + #grep for error message in syslog + (exitcode,num) = dvs.runcmd(['sh', '-c', 'cat /var/log/syslog | grep -i "handlePortStormControlTable: {}: Unsupported / Invalid interface {}"'.format(storm_type,vlan_name)]) + time.sleep(1) + assert exitcode == 0 + self.delete_storm_session(vlan_name, storm_type) + self.remove_vlan_member(dvs,str(vlan_id),member_interface) + self.delete_vlan(dvs,str(vlan_id)) diff --git a/tests/test_tunnel.py b/tests/test_tunnel.py index b69e6b6b73..4b96eb5060 100644 --- a/tests/test_tunnel.py +++ b/tests/test_tunnel.py @@ -7,13 +7,15 @@ def create_fvs(**kwargs): return swsscommon.FieldValuePairs(list(kwargs.items())) - class TestTunnelBase(object): APP_TUNNEL_DECAP_TABLE_NAME = "TUNNEL_DECAP_TABLE" ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" ASIC_TUNNEL_TERM_ENTRIES = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" ASIC_RIF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE" ASIC_VRF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER" + ASIC_QOS_MAP_TABLE_KEY = "ASIC_STATE:SAI_OBJECT_TYPE_QOS_MAP" + TUNNEL_QOS_MAP_NAME = "AZURE_TUNNEL" + CONFIG_TUNNEL_TABLE_NAME = "TUNNEL" ecn_modes_map = { "standard" : "SAI_TUNNEL_DECAP_ECN_MODE_STANDARD", @@ -30,6 +32,9 @@ class TestTunnelBase(object): "uniform" : "SAI_TUNNEL_TTL_MODE_UNIFORM_MODEL" } + # Define 2 dummy maps + DSCP_TO_TC_MAP = {str(i):str(1) for i in range(0, 64)} + TC_TO_PRIORITY_GROUP_MAP = {str(i):str(i) for i in range(0, 8)} def check_interface_exists_in_asicdb(self, asicdb, sai_oid): if_table = swsscommon.Table(asicdb, self.ASIC_RIF_TABLE) @@ -41,43 +46,59 @@ def check_vr_exists_in_asicdb(self, asicdb, sai_oid): status, fvs = vfr_table.get(sai_oid) return status - def check_tunnel_termination_entry_exists_in_asicdb(self, asicdb, tunnel_sai_oid, dst_ips): + def check_tunnel_termination_entry_exists_in_asicdb(self, asicdb, tunnel_sai_oid, dst_ips, src_ip=None): tunnel_term_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TERM_ENTRIES) tunnel_term_entries = tunnel_term_table.getKeys() assert len(tunnel_term_entries) == len(dst_ips) + expected_term_type = "SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2P" if src_ip else "SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP" + expected_len = 6 if src_ip else 5 for term_entry in tunnel_term_entries: status, fvs = tunnel_term_table.get(term_entry) assert status == True - assert len(fvs) == 5 + assert len(fvs) == expected_len for field, value in fvs: if field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_VR_ID": assert self.check_vr_exists_in_asicdb(asicdb, value) elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE": - assert value == "SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP" + assert value == expected_term_type elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE": assert value == "SAI_TUNNEL_TYPE_IPINIP" elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID": assert value == tunnel_sai_oid elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP": assert value in dst_ips + elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_SRC_IP" and src_ip: + assert value == src_ip else: assert False, "Field %s is not tested" % field def create_and_test_tunnel(self, db, asicdb, tunnel_name, **kwargs): """ Create tunnel and verify all needed enties in ASIC DB exists """ - is_symmetric_tunnel = "src_ip" in kwargs; - - # create tunnel entry in DB - ps = swsscommon.ProducerStateTable(db, self.APP_TUNNEL_DECAP_TABLE_NAME) - - fvs = create_fvs(**kwargs) - - ps.set(tunnel_name, fvs) + is_symmetric_tunnel = "src_ip" in kwargs + + decap_dscp_to_tc_map_oid = None + decap_tc_to_pg_map_oid = None + skip_tunnel_creation = False + + if "decap_dscp_to_tc_map_oid" in kwargs: + decap_dscp_to_tc_map_oid = kwargs.pop("decap_dscp_to_tc_map_oid") + + if "decap_tc_to_pg_map_oid" in kwargs: + decap_tc_to_pg_map_oid = kwargs.pop("decap_tc_to_pg_map_oid") + + if "skip_tunnel_creation" in kwargs: + skip_tunnel_creation = kwargs.pop("skip_tunnel_creation") + + if not skip_tunnel_creation: + fvs = create_fvs(**kwargs) + # create tunnel entry in DB + ps = swsscommon.ProducerStateTable(db, self.APP_TUNNEL_DECAP_TABLE_NAME) + ps.set(tunnel_name, fvs) # wait till config will be applied time.sleep(1) @@ -95,11 +116,18 @@ def create_and_test_tunnel(self, db, asicdb, tunnel_name, **kwargs): assert status == True # 6 parameters to check in case of decap tunnel # + 1 (SAI_TUNNEL_ATTR_ENCAP_SRC_IP) in case of symmetric tunnel - assert len(fvs) == 7 if is_symmetric_tunnel else 6 + expected_len = 7 if is_symmetric_tunnel else 6 expected_ecn_mode = self.ecn_modes_map[kwargs["ecn_mode"]] expected_dscp_mode = self.dscp_modes_map[kwargs["dscp_mode"]] expected_ttl_mode = self.ttl_modes_map[kwargs["ttl_mode"]] + + if decap_dscp_to_tc_map_oid: + expected_len += 1 + if decap_tc_to_pg_map_oid: + expected_len += 1 + + assert len(fvs) == expected_len for field, value in fvs: if field == "SAI_TUNNEL_ATTR_TYPE": @@ -116,10 +144,14 @@ def create_and_test_tunnel(self, db, asicdb, tunnel_name, **kwargs): assert self.check_interface_exists_in_asicdb(asicdb, value) elif field == "SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE": assert self.check_interface_exists_in_asicdb(asicdb, value) + elif field == "SAI_TUNNEL_ATTR_DECAP_QOS_DSCP_TO_TC_MAP": + assert value == decap_dscp_to_tc_map_oid + elif field == "SAI_TUNNEL_ATTR_DECAP_QOS_TC_TO_PRIORITY_GROUP_MAP": + assert value == decap_tc_to_pg_map_oid else: assert False, "Field %s is not tested" % field - - self.check_tunnel_termination_entry_exists_in_asicdb(asicdb, tunnel_sai_obj, kwargs["dst_ip"].split(",")) + src_ip = kwargs["src_ip"] if "src_ip" in kwargs else None + self.check_tunnel_termination_entry_exists_in_asicdb(asicdb, tunnel_sai_obj, kwargs["dst_ip"].split(","), src_ip) def remove_and_test_tunnel(self, db, asicdb, tunnel_name): """ Removes tunnel and checks that ASIC db is clear""" @@ -147,6 +179,26 @@ def remove_and_test_tunnel(self, db, asicdb, tunnel_name): assert len(tunnel_app_table.getKeys()) == 0 assert not self.check_interface_exists_in_asicdb(asicdb, overlay_infs_id) + def add_qos_map(self, configdb, asicdb, qos_map_type_name, qos_map_name, qos_map): + """ Add qos map for testing""" + qos_table = swsscommon.Table(asicdb, self.ASIC_QOS_MAP_TABLE_KEY) + current_oids = qos_table.getKeys() + + # Apply QoS map to config db + table = swsscommon.Table(configdb, qos_map_type_name) + fvs = swsscommon.FieldValuePairs(list(qos_map.items())) + table.set(qos_map_name, fvs) + time.sleep(1) + + diff = set(qos_table.getKeys()) - set(current_oids) + assert len(diff) == 1 + oid = diff.pop() + return oid + + def remove_qos_map(self, configdb, qos_map_type_name, qos_map_name): + """ Remove the testing qos map""" + table = swsscommon.Table(configdb, qos_map_type_name) + table._del(qos_map_name) def cleanup_left_over(self, db, asicdb): """ Cleanup APP and ASIC tables """ @@ -159,10 +211,9 @@ def cleanup_left_over(self, db, asicdb): for key in tunnel_term_table.getKeys(): tunnel_term_table._del(key) - tunnel_app_table = swsscommon.Table(asicdb, self.APP_TUNNEL_DECAP_TABLE_NAME) + tunnel_app_table = swsscommon.Table(db, self.APP_TUNNEL_DECAP_TABLE_NAME) for key in tunnel_app_table.getKeys(): - tunnel_table._del(key) - + tunnel_app_table._del(key) class TestDecapTunnel(TestTunnelBase): """ Tests for decap tunnel creation and removal """ @@ -194,7 +245,84 @@ def test_TunnelDecap_v6(self, dvs, testlog): dst_ip="2::2,3::3", dscp_mode="pipe", ecn_mode="copy_from_outer", ttl_mode="uniform") self.remove_and_test_tunnel(db, asicdb,"IPINIPv6Decap") + + def test_TunnelDecap_MuxTunnel(self, dvs, testlog): + """ Test MuxTunnel creation. """ + db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + asicdb = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + configdb = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + self.cleanup_left_over(db, asicdb) + + dscp_to_tc_map_oid = self.add_qos_map(configdb, asicdb, swsscommon.CFG_DSCP_TO_TC_MAP_TABLE_NAME, self.TUNNEL_QOS_MAP_NAME, self.DSCP_TO_TC_MAP) + tc_to_pg_map_oid = self.add_qos_map(configdb, asicdb, swsscommon.CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, self.TUNNEL_QOS_MAP_NAME, self.TC_TO_PRIORITY_GROUP_MAP) + + # Create MuxTunnel0 with QoS remapping attributes + params = { + "tunnel_type": "IPINIP", + "src_ip": "1.1.1.1", + "dst_ip": "1.1.1.2", + "dscp_mode": "pipe", + "ecn_mode": "copy_from_outer", + "ttl_mode": "uniform", + "decap_dscp_to_tc_map": "AZURE_TUNNEL", + "decap_dscp_to_tc_map_oid": dscp_to_tc_map_oid, + "decap_tc_to_pg_map": "AZURE_TUNNEL", + "decap_tc_to_pg_map_oid": tc_to_pg_map_oid + } + self.create_and_test_tunnel(db, asicdb, tunnel_name="MuxTunnel0", **params) + + # Remove Tunnel first + self.remove_and_test_tunnel(db, asicdb,"MuxTunnel0") + + self.remove_qos_map(configdb, swsscommon.CFG_DSCP_TO_TC_MAP_TABLE_NAME, self.TUNNEL_QOS_MAP_NAME) + self.remove_qos_map(configdb, swsscommon.CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, self.TUNNEL_QOS_MAP_NAME) + + def test_TunnelDecap_MuxTunnel_with_retry(self, dvs, testlog): + """ Test MuxTunnel creation. """ + db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + asicdb = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + configdb = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + self.cleanup_left_over(db, asicdb) + + # Create MuxTunnel0 with QoS remapping attributes + params = { + "tunnel_type": "IPINIP", + "src_ip": "1.1.1.1", + "dst_ip": "1.1.1.2", + "dscp_mode": "pipe", + "ecn_mode": "copy_from_outer", + "ttl_mode": "uniform", + "decap_dscp_to_tc_map": "AZURE_TUNNEL", + "decap_tc_to_pg_map": "AZURE_TUNNEL", + } + # Verify tunnel is not created when decap_dscp_to_tc_map/decap_tc_to_pg_map is specified while oid is not ready in qosorch + fvs = create_fvs(**params) + # create tunnel entry in DB + ps = swsscommon.ProducerStateTable(db, self.APP_TUNNEL_DECAP_TABLE_NAME) + ps.set("MuxTunnel0", fvs) + + time.sleep(1) + # check asic db table + tunnel_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TABLE) + tunnels = tunnel_table.getKeys() + assert len(tunnels) == 0 + + #Verify tunneldecaporch creates tunnel when qos map is available + dscp_to_tc_map_oid = self.add_qos_map(configdb, asicdb, swsscommon.CFG_DSCP_TO_TC_MAP_TABLE_NAME, self.TUNNEL_QOS_MAP_NAME, self.DSCP_TO_TC_MAP) + tc_to_pg_map_oid = self.add_qos_map(configdb, asicdb, swsscommon.CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, self.TUNNEL_QOS_MAP_NAME, self.TC_TO_PRIORITY_GROUP_MAP) + params.update({ + "decap_dscp_to_tc_map_oid": dscp_to_tc_map_oid, + "decap_tc_to_pg_map_oid": tc_to_pg_map_oid, + "skip_tunnel_creation": True + }) + self.create_and_test_tunnel(db, asicdb, tunnel_name="MuxTunnel0", **params) + + # Cleanup + self.remove_and_test_tunnel(db, asicdb,"MuxTunnel0") + self.remove_qos_map(configdb, swsscommon.CFG_DSCP_TO_TC_MAP_TABLE_NAME, dscp_to_tc_map_oid) + self.remove_qos_map(configdb, swsscommon.CFG_TC_TO_PRIORITY_GROUP_MAP_TABLE_NAME, tc_to_pg_map_oid) class TestSymmetricTunnel(TestTunnelBase): """ Tests for symmetric tunnel creation and removal """ diff --git a/tests/test_vnet.py b/tests/test_vnet.py index 41217de92e..60a2ed8c33 100644 --- a/tests/test_vnet.py +++ b/tests/test_vnet.py @@ -362,6 +362,9 @@ def create_vxlan_tunnel(dvs, name, src_ip): attrs, ) +def delete_vxlan_tunnel(dvs, name): + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + delete_entry_tbl(conf_db, "VXLAN_TUNNEL", name) def create_vxlan_tunnel_map(dvs, tunnel_name, tunnel_map_entry_name, vlan, vni_id): conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) @@ -447,7 +450,7 @@ def get_bfd_session_id(dvs, addr): status, fvs = tbl.get(entry) fvs = dict(fvs) assert status, "Got an error when get a key" - if fvs["SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS"] == addr: + if fvs["SAI_BFD_SESSION_ATTR_DST_IP_ADDRESS"] == addr and fvs["SAI_BFD_SESSION_ATTR_MULTIHOP"] == "true": return entry return None @@ -621,6 +624,18 @@ def check_vxlan_tunnel(self, dvs, tunnel_name, src_ip): self.tunnel_map_map[tunnel_name] = tunnel_map_id self.tunnel[tunnel_name] = tunnel_id + def check_del_vxlan_tunnel(self, dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + old_tunnel = get_deleted_entries(asic_db, self.ASIC_TUNNEL_TABLE, self.tunnel_ids, 1) + check_deleted_object(asic_db, self.ASIC_TUNNEL_TABLE, old_tunnel[0]) + self.tunnel_ids.remove(old_tunnel[0]) + + old_tunnel_maps = get_deleted_entries(asic_db, self.ASIC_TUNNEL_MAP, self.tunnel_map_ids, 4) + for old_tunnel_map in old_tunnel_maps: + check_deleted_object(asic_db, self.ASIC_TUNNEL_MAP, old_tunnel_map) + self.tunnel_map_ids.remove(old_tunnel_map) + def check_vxlan_tunnel_entry(self, dvs, tunnel_name, vnet_name, vni_id): asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) @@ -1049,6 +1064,9 @@ def test_vnet_orch_1(self, dvs, testlog): delete_vnet_entry(dvs, 'Vnet_2000') vnet_obj.check_del_vnet_entry(dvs, 'Vnet_2000') + delete_vxlan_tunnel(dvs, tunnel_name) + vnet_obj.check_del_vxlan_tunnel(dvs) + ''' Test 2 - Two VNets, One HSMs per VNet ''' @@ -1175,6 +1193,9 @@ def test_vnet_orch_2(self, dvs, testlog): delete_vnet_entry(dvs, 'Vnet_2') vnet_obj.check_del_vnet_entry(dvs, 'Vnet_2') + delete_vxlan_tunnel(dvs, tunnel_name) + vnet_obj.check_del_vxlan_tunnel(dvs) + ''' Test 3 - Two VNets, One HSMs per VNet, Peering ''' @@ -1255,6 +1276,9 @@ def test_vnet_orch_3(self, dvs, testlog): delete_vnet_entry(dvs, 'Vnet_20') vnet_obj.check_del_vnet_entry(dvs, 'Vnet_20') + delete_vxlan_tunnel(dvs, tunnel_name) + vnet_obj.check_del_vxlan_tunnel(dvs) + ''' Test 4 - IPv6 Vxlan tunnel test ''' @@ -1396,6 +1420,9 @@ def test_vnet_orch_4(self, dvs, testlog): delete_vnet_entry(dvs, 'Vnet3001') vnet_obj.check_del_vnet_entry(dvs, 'Vnet3001') + delete_vxlan_tunnel(dvs, tunnel_name) + vnet_obj.check_del_vxlan_tunnel(dvs) + ''' Test 5 - Default VNet test ''' @@ -1412,6 +1439,9 @@ def test_vnet_orch_5(self, dvs, testlog): vnet_obj.check_default_vnet_entry(dvs, 'Vnet_5') vnet_obj.check_vxlan_tunnel_entry(dvs, tunnel_name, 'Vnet_5', '4789') + delete_vnet_entry(dvs, 'Vnet_5') + vnet_obj.check_default_vnet_entry(dvs, 'Vnet_5') + ''' Test 6 - Test VxLAN tunnel with multiple maps ''' diff --git a/tests/test_vxlan_tunnel.py b/tests/test_vxlan_tunnel.py index 14fe28261f..d296fcc741 100644 --- a/tests/test_vxlan_tunnel.py +++ b/tests/test_vxlan_tunnel.py @@ -26,6 +26,18 @@ def create_entry_pst(db, table, separator, key, pairs): create_entry(tbl, key, pairs) +def delete_entry_pst(db, table, key): + tbl = swsscommon.ProducerStateTable(db, table) + tbl._del(key) + time.sleep(1) + + +def delete_entry_tbl(db, table, key): + tbl = swsscommon.Table(db, table) + tbl._del(key) + time.sleep(1) + + def how_many_entries_exist(db, table): tbl = swsscommon.Table(db, table) return len(tbl.getKeys()) @@ -324,6 +336,66 @@ def test_vxlan_term_orch(self, dvs, testlog): create_vxlan_tunnel_entry(dvs, 'tunnel_4', 'entry_2', tunnel_map_map, 'Vlan57', '857', tunnel_map_ids, tunnel_map_entry_ids, tunnel_ids, tunnel_term_ids) +def apply_test_vnet_cfg(cfg_db): + + # create VXLAN Tunnel + create_entry_tbl( + cfg_db, + "VXLAN_TUNNEL", '|', "tunnel1", + [ + ("src_ip", "1.1.1.1") + ], + ) + + # create VNET + create_entry_tbl( + cfg_db, + "VNET", '|', "tunnel1", + [ + ("vxlan_tunnel", "tunnel1"), + ("vni", "1") + ], + ) + + return + + +@pytest.fixture +def env_setup(dvs): + cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + + create_entry_pst( + app_db, + "SWITCH_TABLE", ':', "switch", + [ + ("vxlan_router_mac", "00:01:02:03:04:05") + ], + ) + + apply_test_vnet_cfg(cfg_db) + + yield + + delete_entry_pst(app_db, "SWITCH_TABLE", "switch") + delete_entry_tbl(cfg_db, "VXLAN_TUNNEL", "tunnel1") + delete_entry_tbl(cfg_db, "VNET", "Vnet1") + +def test_vnet_cleanup_config_reload(dvs, env_setup): + + # Restart vxlanmgrd Process + dvs.runcmd(["systemctl", "restart", "vxlanmgrd"]) + + # Reapply cfg to simulate cfg reload + cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + apply_test_vnet_cfg(cfg_db) + + time.sleep(0.5) + + # Check if the netdevices is created as expected + ret, stdout = dvs.runcmd(["ip", "link", "show"]) + assert "Vxlan1" in stdout + assert "Brvxlan1" in stdout # 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_warm_reboot.py b/tests/test_warm_reboot.py index 36028dfc69..cf525a64f3 100644 --- a/tests/test_warm_reboot.py +++ b/tests/test_warm_reboot.py @@ -237,6 +237,20 @@ def ping_new_ips(dvs): dvs.runcmd(['sh', '-c', "ping -c 1 -W 0 -q {}.0.0.{} > /dev/null 2>&1".format(i*4, j+NUM_NEIGH_PER_INTF+2)]) dvs.runcmd(['sh', '-c', "ping6 -c 1 -W 0 -q {}00::{} > /dev/null 2>&1".format(i*4, j+NUM_NEIGH_PER_INTF+2)]) +def warm_restart_set(dvs, app, enable): + db = swsscommon.DBConnector(6, dvs.redis_sock, 0) + tbl = swsscommon.Table(db, "WARM_RESTART_ENABLE_TABLE") + fvs = swsscommon.FieldValuePairs([("enable",enable)]) + tbl.set(app, fvs) + time.sleep(1) + + +def warm_restart_timer_set(dvs, app, timer, val): + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + tbl = swsscommon.Table(db, "WARM_RESTART") + fvs = swsscommon.FieldValuePairs([(timer, val)]) + tbl.set(app, fvs) + time.sleep(1) class TestWarmReboot(object): def test_PortSyncdWarmRestart(self, dvs, testlog): @@ -245,10 +259,10 @@ def test_PortSyncdWarmRestart(self, dvs, testlog): appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - dvs.runcmd("config warm_restart enable swss") + dvs.warm_restart_swss("true") - dvs.runcmd("config interface startup Ethernet16") - dvs.runcmd("config interface startup Ethernet20") + dvs.port_admin_set("Ethernet16", "up") + dvs.port_admin_set("Ethernet20", "up") time.sleep(1) @@ -259,8 +273,8 @@ def test_PortSyncdWarmRestart(self, dvs, testlog): intf_tbl.set("Ethernet20|11.0.0.9/29", fvs) intf_tbl.set("Ethernet16", fvs) intf_tbl.set("Ethernet20", fvs) - dvs.runcmd("config interface startup Ethernet16") - dvs.runcmd("config interface startup Ethernet20") + dvs.port_admin_set("Ethernet16", "up") + dvs.port_admin_set("Ethernet20", "up") dvs.servers[4].runcmd("ip link set down dev eth0") == 0 dvs.servers[4].runcmd("ip link set up dev eth0") == 0 @@ -339,12 +353,12 @@ def test_VlanMgrdWarmRestart(self, dvs, testlog): dvs.runcmd("ifconfig Ethernet16 0") dvs.runcmd("ifconfig Ethernet20 0") - dvs.runcmd("config interface startup Ethernet16 ") - dvs.runcmd("config interface startup Ethernet20 ") + dvs.port_admin_set("Ethernet16", "up") + dvs.port_admin_set("Ethernet20", "up") time.sleep(1) - dvs.runcmd("config warm_restart enable swss") + dvs.warm_restart_swss("true") # create vlan create_entry_tbl( @@ -387,8 +401,6 @@ def test_VlanMgrdWarmRestart(self, dvs, testlog): intf_tbl.set("Vlan20|11.0.0.9/29", fvs) intf_tbl.set("Vlan16", fvs) intf_tbl.set("Vlan20", fvs) - dvs.runcmd("config interface startup Vlan16") - dvs.runcmd("config interface startup Vlan20") dvs.servers[4].runcmd("ifconfig eth0 11.0.0.2/29") dvs.servers[4].runcmd("ip route add default via 11.0.0.1") @@ -453,7 +465,7 @@ def test_IntfMgrdWarmRestartNoInterfaces(self, dvs, testlog): state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) restore_count = swss_get_RestoreCount(dvs, state_db) - dvs.runcmd("config warm_restart enable swss") + dvs.warm_restart_swss("true") dvs.runcmd("supervisorctl restart intfmgrd") reached_desired_state = False @@ -474,7 +486,7 @@ def test_swss_neighbor_syncup(self, dvs, testlog): conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - dvs.runcmd("config warm_restart enable swss") + dvs.warm_restart_swss("true") # # Testcase1: @@ -503,8 +515,8 @@ def test_swss_neighbor_syncup(self, dvs, testlog): intf_tbl.set("{}".format(intfs[1]), fvs) intf_tbl.set("{}".format(intfs[0]), fvs) intf_tbl.set("{}".format(intfs[1]), fvs) - dvs.runcmd("config interface startup {}".format(intfs[0])) - dvs.runcmd("config interface startup {}".format(intfs[1])) + dvs.port_admin_set(intfs[0], "up") + dvs.port_admin_set(intfs[1], "up") ips = ["24.0.0.2", "24.0.0.3", "28.0.0.2", "28.0.0.3"] v6ips = ["2400::2", "2400::3", "2800::2", "2800::3"] @@ -748,7 +760,7 @@ def test_swss_neighbor_syncup(self, dvs, testlog): # setup timer in configDB timer_value = "15" - dvs.runcmd("config warm_restart neighsyncd_timer {}".format(timer_value)) + warm_restart_timer_set(dvs, "swss", "neighsyncd_timer", timer_value) # get restore_count restore_count = swss_get_RestoreCount(dvs, state_db) @@ -847,7 +859,7 @@ def test_OrchagentWarmRestartReadyCheck(self, dvs, testlog): time.sleep(1) - dvs.runcmd("config warm_restart enable swss") + dvs.warm_restart_swss("true") config_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) intf_tbl = swsscommon.Table(config_db, "INTERFACE") @@ -856,8 +868,8 @@ def test_OrchagentWarmRestartReadyCheck(self, dvs, testlog): intf_tbl.set("Ethernet4|10.0.0.2/31", fvs) intf_tbl.set("Ethernet0", fvs) intf_tbl.set("Ethernet4", fvs) - dvs.runcmd("config interface startup Ethernet0") - dvs.runcmd("config interface startup Ethernet4") + dvs.port_admin_set("Ethernet0", "up") + dvs.port_admin_set("Ethernet4", "up") dvs.servers[0].runcmd("ifconfig eth0 10.0.0.1/31") dvs.servers[0].runcmd("ip route add default via 10.0.0.0") @@ -916,7 +928,7 @@ def test_swss_port_state_syncup(self, dvs, testlog): conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - dvs.runcmd("config warm_restart enable swss") + dvs.warm_restart_swss("true") tbl = swsscommon.Table(appl_db, swsscommon.APP_PORT_TABLE_NAME) @@ -931,9 +943,9 @@ def test_swss_port_state_syncup(self, dvs, testlog): intf_tbl.set("Ethernet0", fvs) intf_tbl.set("Ethernet4", fvs) intf_tbl.set("Ethernet8", fvs) - dvs.runcmd("config interface startup Ethernet0") - dvs.runcmd("config interface startup Ethernet4") - dvs.runcmd("config interface startup Ethernet8") + dvs.port_admin_set("Ethernet0", "up") + dvs.port_admin_set("Ethernet4", "up") + dvs.port_admin_set("Ethernet8", "up") dvs.runcmd("arp -s 10.0.0.1 00:00:00:00:00:01") dvs.runcmd("arp -s 10.0.0.3 00:00:00:00:00:02") @@ -1102,9 +1114,9 @@ def test_routing_WarmRestart(self, dvs, testlog): intf_tbl.set("{}".format(intfs[1]), fvs) intf_tbl.set("{}".format(intfs[2]), fvs) intf_tbl.set("{}".format(intfs[2]), fvs) - dvs.runcmd("config interface startup {}".format(intfs[0])) - dvs.runcmd("config interface startup {}".format(intfs[1])) - dvs.runcmd("config interface startup {}".format(intfs[2])) + dvs.port_admin_set(intfs[0], "up") + dvs.port_admin_set(intfs[1], "up") + dvs.port_admin_set(intfs[2], "up") time.sleep(1) @@ -1199,8 +1211,8 @@ def test_routing_WarmRestart(self, dvs, testlog): # The following two instructions will be substituted by the commented ones # once the later ones are added to sonic-utilities repo. - dvs.runcmd("config warm_restart enable bgp") - dvs.runcmd("config warm_restart bgp_timer {}".format(restart_timer)) + warm_restart_set(dvs, "bgp", "true") + warm_restart_timer_set(dvs, "bgp", "bgp_timer", str(restart_timer)) time.sleep(1) @@ -1711,7 +1723,7 @@ def test_routing_WarmRestart(self, dvs, testlog): del_entry_tbl(state_db, "BGP_STATE_TABLE", "IPv4|eoiu") del_entry_tbl(state_db, "BGP_STATE_TABLE", "IPv6|eoiu") - dvs.runcmd("config warm_restart bgp_timer {}".format(restart_timer)) + warm_restart_timer_set(dvs, "bgp", "bgp_timer", str(restart_timer)) # Restart zebra dvs.stop_zebra() dvs.start_zebra() @@ -1854,7 +1866,7 @@ def test_system_warmreboot_neighbor_syncup(self, dvs, testlog): flush_neigh_entries(dvs) time.sleep(5) - dvs.runcmd("config warm_restart enable system") + warm_restart_set(dvs, "system", "true") # Test neighbors on NUM_INTF (e,g 8) interfaces # Ethernet32/36/.../60, with ip: 32.0.0.1/24... 60.0.0.1/24 @@ -1877,7 +1889,7 @@ def test_system_warmreboot_neighbor_syncup(self, dvs, testlog): intf_tbl.set("Ethernet{}|{}00::1/64".format(i*4, i*4), fvs) intf_tbl.set("Ethernet{}".format(i*4, i*4), fvs) intf_tbl.set("Ethernet{}".format(i*4, i*4), fvs) - dvs.runcmd("config interface startup Ethernet{}".format(i*4, i*4)) + dvs.port_admin_set("Ethernet{}".format(i*4), "up") dvs.servers[i].runcmd("ip link set up dev eth0") dvs.servers[i].runcmd("ip addr flush dev eth0") #result = dvs.servers[i].runcmd_output("ifconfig eth0 | grep HWaddr | awk '{print $NF}'") @@ -2103,7 +2115,7 @@ def test_system_warmreboot_neighbor_syncup(self, dvs, testlog): swss_app_check_RestoreCount_single(state_db, restore_count, "neighsyncd") # disable system warm restart - dvs.runcmd("config warm_restart disable system") + warm_restart_set(dvs, "system", "false") for i in range(8, 8+NUM_INTF): intf_tbl._del("Ethernet{}|{}.0.0.1/24".format(i*4, i*4)) @@ -2117,11 +2129,11 @@ def test_VrfMgrdWarmRestart(self, dvs, testlog): appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) - dvs.runcmd("config warm_restart enable swss") + dvs.warm_restart_swss("true") # bring up interface - dvs.runcmd("config interface startup Ethernet0 ") - dvs.runcmd("config interface startup Ethernet4 ") + dvs.port_admin_set("Ethernet0", "up") + dvs.port_admin_set("Ethernet4", "up") # create vrf create_entry_tbl(conf_db, "VRF", "Vrf_1", [('empty', 'empty')]) @@ -2285,7 +2297,7 @@ def test_MirrorSessionWarmReboot(self, dvs): # Monitor port should not change b/c routes are ECMP state_db.wait_for_field_match("MIRROR_SESSION_TABLE", "test_session", {"monitor_port": "Ethernet12"}) - dvs.runcmd("config warm_restart enable swss") + dvs.warm_restart_swss("true") dvs.stop_swss() dvs.start_swss() @@ -2332,7 +2344,7 @@ def test_EverflowWarmReboot(self, dvs, dvs_acl): asic_db.wait_for_n_keys("ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY", 1 + len(asic_db.default_acl_entries)) # Execute the warm reboot - dvs.runcmd("config warm_restart enable swss") + dvs.warm_restart_swss("true") dvs.stop_swss() dvs.start_swss() @@ -2365,6 +2377,35 @@ def test_EverflowWarmReboot(self, dvs, dvs_acl): dvs.start_swss() dvs.check_swss_ready() + def test_TunnelMgrdWarmRestart(self, dvs): + tunnel_name = "MuxTunnel0" + tunnel_table = "TUNNEL_DECAP_TABLE" + tunnel_params = { + "tunnel_type": "IPINIP", + "dst_ip": "10.1.0.32", + "dscp_mode": "uniform", + "ecn_mode": "standard", + "ttl_mode": "pipe" + } + + pubsub = dvs.SubscribeAppDbObject(tunnel_table) + + dvs.runcmd("config warm_restart enable swss") + config_db = dvs.get_config_db() + config_db.create_entry("TUNNEL", tunnel_name, tunnel_params) + + app_db = dvs.get_app_db() + app_db.wait_for_matching_keys(tunnel_table, [tunnel_name]) + + nadd, ndel = dvs.CountSubscribedObjects(pubsub) + assert nadd == len(tunnel_params) + assert ndel == 1 # Expect 1 deletion as part of table creation + + dvs.runcmd("supervisorctl restart tunnelmgrd") + dvs.check_services_ready() + nadd, ndel = dvs.CountSubscribedObjects(pubsub) + assert nadd == 0 + assert ndel == 0 # 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_watermark.py b/tests/test_watermark.py index 6d7c993125..23efedcb42 100644 --- a/tests/test_watermark.py +++ b/tests/test_watermark.py @@ -172,6 +172,12 @@ def set_up(self, dvs): tbl.set('', [(q, "SAI_QUEUE_TYPE_ALL")]) self.all_q.append(q) + def clear_watermark(self, dvs, data): + adb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + msg = json.dumps(data, separators=(',',':')) + adb.publish('WATERMARK_CLEAR_REQUEST', msg) + time.sleep(1) + def test_telemetry_period(self, dvs): self.setup_dbs(dvs) self.set_up(dvs) @@ -191,7 +197,10 @@ def test_telemetry_period(self, dvs): self.populate_asic_all(dvs, "123") - dvs.runcmd("config watermark telemetry interval {}".format(5)) + interval = {"interval": "5"} + self.config_db.create_entry("WATERMARK_TABLE", + "TELEMETRY_INTERVAL", + interval) time.sleep(self.DEFAULT_TELEMETRY_INTERVAL + 1) time.sleep(self.NEW_INTERVAL + 1) @@ -257,10 +266,7 @@ def test_clear(self, dvs): # clear pg shared watermark, and verify that headroom watermark and persistent watermarks are not affected - exitcode, output = dvs.runcmd("sonic-clear priority-group watermark shared") - time.sleep(1) - assert exitcode == 0, "CLI failure: %s" % output - # make sure it cleared + self.clear_watermark(dvs, ["USER", "PG_SHARED"]) self.verify_value(dvs, self.pgs, WmTables.user, SaiWmStats.pg_shared, "0") # make sure the rest is untouched @@ -271,9 +277,7 @@ def test_clear(self, dvs): # clear queue unicast persistent watermark, and verify that multicast watermark and user watermarks are not affected - exitcode, output = dvs.runcmd("sonic-clear queue persistent-watermark unicast") - time.sleep(1) - assert exitcode == 0, "CLI failure: %s" % output + self.clear_watermark(dvs, ["PERSISTENT", "Q_SHARED_UNI"]) # make sure it cleared self.verify_value(dvs, self.uc_q, WmTables.persistent, SaiWmStats.queue_shared, "0") @@ -289,16 +293,14 @@ def test_clear(self, dvs): # clear queue all watermark, and verify that multicast and unicast watermarks are not affected # clear persistent all watermark - exitcode, output = dvs.runcmd("sonic-clear queue persistent-watermark all") - time.sleep(1) - assert exitcode == 0, "CLI failure: %s" % output + self.clear_watermark(dvs, ["PERSISTENT", "Q_SHARED_ALL"]) + # make sure it cleared self.verify_value(dvs, self.all_q, WmTables.persistent, SaiWmStats.queue_shared, "0") # clear user all watermark - exitcode, output = dvs.runcmd("sonic-clear queue watermark all") - time.sleep(1) - assert exitcode == 0, "CLI failure: %s" % output + self.clear_watermark(dvs, ["USER", "Q_SHARED_ALL"]) + # make sure it cleared self.verify_value(dvs, self.all_q, WmTables.user, SaiWmStats.queue_shared, "0") diff --git a/tlm_teamd/Makefile.am b/tlm_teamd/Makefile.am index 6bf7574a8f..1c86c118ee 100644 --- a/tlm_teamd/Makefile.am +++ b/tlm_teamd/Makefile.am @@ -10,10 +10,15 @@ endif tlm_teamd_SOURCES = main.cpp teamdctl_mgr.cpp values_store.cpp -tlm_teamd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) -tlm_teamd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(JANSSON_CFLAGS) -tlm_teamd_LDADD = -lhiredis -lswsscommon -lteamdctl $(JANSSON_LIBS) +tlm_teamd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_ASAN) +tlm_teamd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(JANSSON_CFLAGS) $(CFLAGS_ASAN) +tlm_teamd_LDADD = $(LDFLAGS_ASAN) -lhiredis -lswsscommon -lteamdctl $(JANSSON_LIBS) if GCOV_ENABLED tlm_teamd_LDADD += -lgcovpreload endif + +if ASAN_ENABLED +tlm_teamd_SOURCES += $(top_srcdir)/lib/asan.cpp +endif +