diff --git a/.gitignore b/.gitignore index da78e5a98c..34034cfe0c 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,7 @@ debian/tmp/ aclocal.m4 config.h config.h.in +config.h.in~ config.log config.status configure @@ -46,11 +47,11 @@ fpmsyncd/fpmsyncd intfsyncd/intfsyncd cfgmgr/intfmgrd cfgmgr/vlanmgrd +cfgmgr/buffermanager neighsyncd/neighsyncd portsyncd/portsyncd orchagent/orchagent orchagent/routeresync swssconfig/swssconfig swssconfig/swssplayer - - +tests/tests diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index 4bcb57ade2..258d7659be 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -1,7 +1,7 @@ INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/orchagent CFLAGS_SAI = -I /usr/include/sai -bin_PROGRAMS = vlanmgrd intfmgrd +bin_PROGRAMS = vlanmgrd intfmgrd buffermgrd if DEBUG DBGFLAGS = -ggdb -DDEBUG @@ -9,12 +9,18 @@ else DBGFLAGS = -g endif -vlanmgrd_SOURCES = vlanmgrd.cpp vlanmgr.cpp $(top_srcdir)/orchagent/orch.cpp shellcmd.h +vlanmgrd_SOURCES = vlanmgrd.cpp vlanmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h vlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vlanmgrd_LDADD = -lswsscommon -intfmgrd_SOURCES = intfmgrd.cpp intfmgr.cpp $(top_srcdir)/orchagent/orch.cpp shellcmd.h +intfmgrd_SOURCES = intfmgrd.cpp intfmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h intfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) intfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) -intfmgrd_LDADD = -lswsscommon \ No newline at end of file +intfmgrd_LDADD = -lswsscommon + +buffermgrd_SOURCES = buffermgrd.cpp buffermgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h +buffermgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) +buffermgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) +buffermgrd_LDADD = -lswsscommon + diff --git a/cfgmgr/buffermgr.cpp b/cfgmgr/buffermgr.cpp new file mode 100644 index 0000000000..89b01970bc --- /dev/null +++ b/cfgmgr/buffermgr.cpp @@ -0,0 +1,244 @@ +#include +#include +#include +#include "logger.h" +#include "dbconnector.h" +#include "producerstatetable.h" +#include "tokenize.h" +#include "ipprefix.h" +#include "buffermgr.h" +#include "exec.h" +#include "shellcmd.h" + +using namespace std; +using namespace swss; + +BufferMgr::BufferMgr(DBConnector *cfgDb, DBConnector *stateDb, string pg_lookup_file, const vector &tableNames) : + Orch(cfgDb, tableNames), + m_statePortTable(stateDb, STATE_PORT_TABLE_NAME), + m_cfgPortTable(cfgDb, CFG_PORT_TABLE_NAME), + m_cfgCableLenTable(cfgDb, CFG_PORT_CABLE_LEN_TABLE_NAME), + m_cfgBufferProfileTable(cfgDb, CFG_BUFFER_PROFILE_TABLE_NAME), + m_cfgBufferPgTable(cfgDb, CFG_BUFFER_PG_TABLE_NAME), + m_cfgLosslessPgPoolTable(cfgDb, CFG_BUFFER_POOL_TABLE_NAME) +{ + readPgProfileLookupFile(pg_lookup_file); +} + +//# speed, cable, size, xon, xoff, threshold, xon_offset +// 40000 5m 34816 18432 16384 1 2496 +void BufferMgr::readPgProfileLookupFile(string file) +{ + SWSS_LOG_NOTICE("Read lookup configuration file..."); + + ifstream infile(file); + if (!infile.is_open()) + { + return; + } + + string line; + while (getline(infile, line)) + { + if (line.empty() || (line.at(0) == '#')) + { + continue; + } + + istringstream iss(line); + string speed, cable; + + iss >> speed; + iss >> cable; + iss >> m_pgProfileLookup[speed][cable].size; + iss >> m_pgProfileLookup[speed][cable].xon; + iss >> m_pgProfileLookup[speed][cable].xoff; + iss >> m_pgProfileLookup[speed][cable].threshold; + + // Not all lookup table contains xon_offset values. Set default to empty + m_pgProfileLookup[speed][cable].xon_offset = ""; + iss >> m_pgProfileLookup[speed][cable].xon_offset; + + SWSS_LOG_NOTICE("PG profile for speed %s and cable %s is: size:%s, xon:%s, xoff:%s, th:%s, xon_offset:%s", + speed.c_str(), cable.c_str(), + m_pgProfileLookup[speed][cable].size.c_str(), + m_pgProfileLookup[speed][cable].xon.c_str(), + m_pgProfileLookup[speed][cable].xoff.c_str(), + m_pgProfileLookup[speed][cable].threshold.c_str(), + m_pgProfileLookup[speed][cable].xon_offset.c_str() + ); + } + + infile.close(); +} + +task_process_status BufferMgr::doCableTask(string port, string cable_length) +{ + m_cableLenLookup[port] = cable_length; + return task_process_status::task_success; +} + +string BufferMgr::getPgPoolMode() +{ + vector pool_properties; + m_cfgLosslessPgPoolTable.get(INGRESS_LOSSLESS_PG_POOL_NAME, pool_properties); + for (auto& prop : pool_properties) + { + if (fvField(prop) == "mode") + return fvValue(prop); + } + return ""; +} + +/* +Create/update two tables: profile (in m_cfgBufferProfileTable) and port buffer (in m_cfgBufferPgTable): + + "BUFFER_PROFILE": { + "pg_lossless_100G_300m_profile": { + "pool":"[BUFFER_POOL_TABLE:ingress_lossless_pool]", + "xon":"18432", + "xon_offset":"2496", + "xoff":"165888", + "size":"184320", + "dynamic_th":"1" + } + } + "BUFFER_PG" :{ + Ethernet44|3-4": { + "profile" : "[BUFFER_PROFILE:pg_lossless_100000_300m_profile]" + } + } +*/ +task_process_status BufferMgr::doSpeedUpdateTask(string port, string speed) +{ + vector fvVector; + string cable; + + if (m_cableLenLookup.count(port) == 0) + { + SWSS_LOG_WARN("Unable to create/update PG profile for port %s. Cable length is not set", port.c_str()); + return task_process_status::task_need_retry; + } + + cable = m_cableLenLookup[port]; + + if (m_pgProfileLookup.count(speed) == 0 || m_pgProfileLookup[speed].count(cable) == 0) + { + SWSS_LOG_ERROR("Unable to create/update PG profile for port %s. No PG profile configured for speed %s and cable length %s", + port.c_str(), speed.c_str(), cable.c_str()); + return task_process_status::task_invalid_entry; + } + + // Crete record in BUFFER_PROFILE table + // key format is pg_lossless___profile + string buffer_profile_key = "pg_lossless_" + speed + "_" + cable + "_profile"; + + // check if profile already exists - if yes - skip creation + m_cfgBufferProfileTable.get(buffer_profile_key, fvVector); + if (fvVector.size() == 0) + { + SWSS_LOG_NOTICE("Creating new profile '%s'", buffer_profile_key.c_str()); + + string mode = getPgPoolMode(); + if (mode.empty()) + { + // this should never happen if switch initialized properly + SWSS_LOG_WARN("PG lossless pool is not yet created"); + return task_process_status::task_need_retry; + } + + // profile threshold field name + mode += "_th"; + string pg_pool_reference = string(CFG_BUFFER_POOL_TABLE_NAME) + + m_cfgBufferProfileTable.getTableNameSeparator() + + INGRESS_LOSSLESS_PG_POOL_NAME; + + fvVector.push_back(make_pair("pool", "[" + pg_pool_reference + "]")); + fvVector.push_back(make_pair("xon", m_pgProfileLookup[speed][cable].xon)); + if (m_pgProfileLookup[speed][cable].xon_offset.length() > 0) { + fvVector.push_back(make_pair("xon_offset", + m_pgProfileLookup[speed][cable].xon_offset)); + } + fvVector.push_back(make_pair("xoff", m_pgProfileLookup[speed][cable].xoff)); + fvVector.push_back(make_pair("size", m_pgProfileLookup[speed][cable].size)); + fvVector.push_back(make_pair(mode, m_pgProfileLookup[speed][cable].threshold)); + m_cfgBufferProfileTable.set(buffer_profile_key, fvVector); + } + else + { + SWSS_LOG_NOTICE("Reusing existing profile '%s'", buffer_profile_key.c_str()); + } + + fvVector.clear(); + + string buffer_pg_key = port + m_cfgBufferPgTable.getTableNameSeparator() + LOSSLESS_PGS; + + string profile_ref = string("[") + + CFG_BUFFER_PROFILE_TABLE_NAME + + m_cfgBufferPgTable.getTableNameSeparator() + + buffer_profile_key + + "]"; + + fvVector.push_back(make_pair("profile", profile_ref)); + m_cfgBufferPgTable.set(buffer_pg_key, fvVector); + return task_process_status::task_success; +} + +void BufferMgr::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + string table_name = consumer.getTableName(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string keySeparator = CONFIGDB_KEY_SEPARATOR; + vector keys = tokenize(kfvKey(t), keySeparator[0]); + string port(keys[0]); + + string op = kfvOp(t); + task_process_status task_status = task_process_status::task_success; + if (op == SET_COMMAND) + { + for (auto i : kfvFieldsValues(t)) + { + if (table_name == CFG_PORT_CABLE_LEN_TABLE_NAME) + { + // receive and cache cable length table + task_status = doCableTask(fvField(i), fvValue(i)); + } + // In case of PORT table update, Buffer Manager is interested in speed update only + if (table_name == CFG_PORT_TABLE_NAME && fvField(i) == "speed") + { + // create/update profile for port + task_status = doSpeedUpdateTask(port, fvValue(i)); + } + if (task_status != task_process_status::task_success) + { + 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 new file mode 100644 index 0000000000..3c00988e65 --- /dev/null +++ b/cfgmgr/buffermgr.h @@ -0,0 +1,55 @@ +#ifndef __BUFFMGR__ +#define __BUFFMGR__ + +#include "dbconnector.h" +#include "producerstatetable.h" +#include "orch.h" + +#include +#include + +namespace swss { + +#define INGRESS_LOSSLESS_PG_POOL_NAME "ingress_lossless_pool" +#define LOSSLESS_PGS "3-4" + +typedef struct{ + string size; + string xon; + string xon_offset; + string xoff; + string threshold; +} pg_profile_t; + +typedef map speed_map_t; +typedef map pg_profile_lookup_t; + +typedef map port_cable_length_t; + +class BufferMgr : public Orch +{ +public: + BufferMgr(DBConnector *cfgDb, DBConnector *stateDb, string pg_lookup_file, const vector &tableNames); + using Orch::doTask; + +private: + Table m_statePortTable; + Table m_cfgPortTable; + Table m_cfgCableLenTable; + Table m_cfgBufferProfileTable; + Table m_cfgBufferPgTable; + Table m_cfgLosslessPgPoolTable; + + pg_profile_lookup_t m_pgProfileLookup; + port_cable_length_t m_cableLenLookup; + std::string getPgPoolMode(); + void readPgProfileLookupFile(std::string); + task_process_status doCableTask(string port, string cable_length); + task_process_status doSpeedUpdateTask(string port, string speed); + + void doTask(Consumer &consumer); +}; + +} + +#endif /* __BUFFMGR__ */ diff --git a/cfgmgr/buffermgrd.cpp b/cfgmgr/buffermgrd.cpp new file mode 100644 index 0000000000..ac59b87bd8 --- /dev/null +++ b/cfgmgr/buffermgrd.cpp @@ -0,0 +1,122 @@ +#include +#include +#include +#include +#include "dbconnector.h" +#include "select.h" +#include "exec.h" +#include "schema.h" +#include "buffermgr.h" +#include +#include + +using namespace std; +using namespace swss; + +/* select() function timeout retry time, in millisecond */ +#define SELECT_TIMEOUT 1000 + +/* + * Following global variables are defined here for the purpose of + * using existing Orch class which is to be refactored soon to + * eliminate the direct exposure of the global variables. + * + * Once Orch class refactoring is done, these global variables + * should be removed from here. + */ +int gBatchSize = 0; +bool gSwssRecord = false; +bool gLogRotate = false; +ofstream gRecordOfs; +string gRecordFile; +/* Global database mutex */ +mutex gDbMutex; + +void usage() +{ + cout << "Usage: buffermgrd -l pg_lookup.ini" << endl; + cout << " -l pg_lookup.ini: PG profile look up table file (mandatory)" << endl; + cout << " format: csv" << endl; + cout << " values: 'speed, cable, size, xon, xoff, dynamic_threshold, xon_offset'" << endl; +} + +int main(int argc, char **argv) +{ + int opt; + string pg_lookup_file = ""; + Logger::linkToDbNative("buffermgrd"); + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("--- Starting buffermgrd ---"); + + while ((opt = getopt(argc, argv, "l:h")) != -1 ) + { + switch (opt) + { + case 'l': + pg_lookup_file = optarg; + break; + case 'h': + usage(); + return 1; + default: /* '?' */ + usage(); + return EXIT_FAILURE; + } + } + + if (pg_lookup_file.empty()) + { + usage(); + return EXIT_FAILURE; + } + + try + { + vector cfg_buffer_tables = { + CFG_PORT_TABLE_NAME, + CFG_PORT_CABLE_LEN_TABLE_NAME, + }; + + DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + + BufferMgr buffmgr(&cfgDb, &stateDb, pg_lookup_file, cfg_buffer_tables); + + // TODO: add tables in stateDB which interface depends on to monitor list + std::vector cfgOrchList = {&buffmgr}; + + swss::Select s; + for (Orch *o : cfgOrchList) + { + s.addSelectables(o->getSelectables()); + } + + SWSS_LOG_NOTICE("starting main loop"); + while (true) + { + Selectable *sel; + int ret; + + ret = s.select(&sel, SELECT_TIMEOUT); + if (ret == Select::ERROR) + { + SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); + continue; + } + if (ret == Select::TIMEOUT) + { + buffmgr.doTask(); + continue; + } + + auto *c = (Executor *)sel; + c->execute(); + } + } + catch(const std::exception &e) + { + SWSS_LOG_ERROR("Runtime error: %s", e.what()); + } + return -1; +} diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index c56dee07e9..7e30fa1bd9 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -16,11 +16,11 @@ using namespace swss; IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : Orch(cfgDb, tableNames), - m_cfgIntfTable(cfgDb, CFG_INTF_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), - m_cfgVlanIntfTable(cfgDb, CFG_VLAN_INTF_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), - m_statePortTable(stateDb, STATE_PORT_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), - m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), - m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), + m_cfgIntfTable(cfgDb, CFG_INTF_TABLE_NAME), + m_cfgVlanIntfTable(cfgDb, CFG_VLAN_INTF_TABLE_NAME), + m_statePortTable(stateDb, STATE_PORT_TABLE_NAME), + m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME), + m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME), m_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME) { } diff --git a/cfgmgr/intfmgrd.cpp b/cfgmgr/intfmgrd.cpp index 989099f915..8d7c2ef2fc 100644 --- a/cfgmgr/intfmgrd.cpp +++ b/cfgmgr/intfmgrd.cpp @@ -65,9 +65,9 @@ int main(int argc, char **argv) while (true) { Selectable *sel; - int fd, ret; + int ret; - ret = s.select(&sel, &fd, SELECT_TIMEOUT); + ret = s.select(&sel, SELECT_TIMEOUT); if (ret == Select::ERROR) { SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); diff --git a/cfgmgr/shellcmd.h b/cfgmgr/shellcmd.h index 520739c0df..9ee81297fe 100644 --- a/cfgmgr/shellcmd.h +++ b/cfgmgr/shellcmd.h @@ -4,12 +4,8 @@ #define IP_CMD "/sbin/ip" #define BRIDGE_CMD "/sbin/bridge" #define ECHO_CMD "/bin/echo" -#define XARGS_CMD "/usr/bin/xargs" +#define BASH_CMD "/bin/bash" #define GREP_CMD "/bin/grep" -#define AWK_CMD "/usr/bin/awk" -#define LS_CMD "/bin/ls" -#define PASTE_CMD "/usr/bin/paste" -#define SED_CMD "/bin/sed" #define EXEC_WITH_ERROR_THROW(cmd, res) ({ \ int ret = swss::exec(cmd, res); \ diff --git a/cfgmgr/vlanmgr.cpp b/cfgmgr/vlanmgr.cpp index e9f4eb650e..215fc67eae 100644 --- a/cfgmgr/vlanmgr.cpp +++ b/cfgmgr/vlanmgr.cpp @@ -13,7 +13,7 @@ using namespace swss; #define DOT1Q_BRIDGE_NAME "Bridge" #define VLAN_PREFIX "Vlan" #define LAG_PREFIX "PortChannel" -#define DEFAULT_VLAN_ID 1 +#define DEFAULT_VLAN_ID "1" #define MAX_MTU 9100 #define VLAN_HLEN 4 @@ -21,155 +21,171 @@ extern MacAddress gMacAddress; VlanMgr::VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : Orch(cfgDb, tableNames), - m_cfgVlanTable(cfgDb, CFG_VLAN_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), - m_cfgVlanMemberTable(cfgDb, CFG_VLAN_MEMBER_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), - m_statePortTable(stateDb, STATE_PORT_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), - m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), - m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), + m_cfgVlanTable(cfgDb, CFG_VLAN_TABLE_NAME), + m_cfgVlanMemberTable(cfgDb, CFG_VLAN_MEMBER_TABLE_NAME), + m_statePortTable(stateDb, STATE_PORT_TABLE_NAME), + m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME), + m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME), m_appVlanTableProducer(appDb, APP_VLAN_TABLE_NAME), m_appVlanMemberTableProducer(appDb, APP_VLAN_MEMBER_TABLE_NAME) { SWSS_LOG_ENTER(); // Initialize Linux dot1q bridge and enable vlan filtering - stringstream cmd; - string res; - - cmd << IP_CMD << " link del " << DOT1Q_BRIDGE_NAME; - swss::exec(cmd.str(), res); - - cmd.str(""); - cmd << IP_CMD << " link add " << DOT1Q_BRIDGE_NAME << " up type bridge"; - EXEC_WITH_ERROR_THROW(cmd.str(), res); - - cmd.str(""); - cmd << ECHO_CMD << " 1 > /sys/class/net/" << DOT1Q_BRIDGE_NAME << "/bridge/vlan_filtering"; - int ret = swss::exec(cmd.str(), res); + // The command should be generated as: + // /bin/bash -c "/sbin/ip link del Bridge 2>/dev/null ; + // /sbin/ip link add Bridge up type bridge && + // /sbin/bridge vlan del vid 1 dev Bridge self" + + const std::string cmds = std::string("") + + BASH_CMD + " -c \"" + + IP_CMD + " link del " + DOT1Q_BRIDGE_NAME + " 2>/dev/null; " + + IP_CMD + " link add " + DOT1Q_BRIDGE_NAME + " up type bridge && " + + BRIDGE_CMD + " vlan del vid " + DEFAULT_VLAN_ID + " dev " + DOT1Q_BRIDGE_NAME + " self\""; + + std::string res; + EXEC_WITH_ERROR_THROW(cmds, res); + + // The generated command is: + // /bin/echo 1 > /sys/class/net/Bridge/bridge/vlan_filtering + const std::string echo_cmd = std::string("") + + ECHO_CMD + " 1 > /sys/class/net/" + DOT1Q_BRIDGE_NAME + "/bridge/vlan_filtering"; + + int ret = swss::exec(echo_cmd, res); /* echo will fail in virtual switch since /sys directory is read-only. * need to use ip command to setup the vlan_filtering which is not available in debian 8. - * Once we move sonic to debian 9, we can use IP command by default */ + * Once we move sonic to debian 9, we can use IP command by default + * ip command available in Debian 9 to create a bridge with a vlan filtering: + * /sbin/ip link add Bridge up type bridge vlan_filtering 1 */ if (ret != 0) { - cmd.str(""); - cmd << IP_CMD << " link set " << DOT1Q_BRIDGE_NAME << " type bridge vlan_filtering 1"; - EXEC_WITH_ERROR_THROW(cmd.str(), res); - } + const std::string echo_cmd_backup = std::string("") + + IP_CMD + " link set " + DOT1Q_BRIDGE_NAME + " type bridge vlan_filtering 1"; - cmd.str(""); - cmd << BRIDGE_CMD << " vlan del vid " << DEFAULT_VLAN_ID << " dev " << DOT1Q_BRIDGE_NAME << " self"; - EXEC_WITH_ERROR_THROW(cmd.str(), res); + EXEC_WITH_ERROR_THROW(echo_cmd_backup, res); + } } bool VlanMgr::addHostVlan(int vlan_id) { - stringstream cmd; - string res; - - cmd << BRIDGE_CMD << " vlan add vid " << vlan_id << " dev " << DOT1Q_BRIDGE_NAME << " self"; - EXEC_WITH_ERROR_THROW(cmd.str(), res); - - cmd.str(""); - cmd << IP_CMD << " link add link " << DOT1Q_BRIDGE_NAME << " name " << VLAN_PREFIX << vlan_id << " type vlan id " << vlan_id; - EXEC_WITH_ERROR_THROW(cmd.str(), res); - - cmd.str(""); - cmd << IP_CMD << " link set " << VLAN_PREFIX << vlan_id << " address " << gMacAddress.to_string(); - EXEC_WITH_ERROR_THROW(cmd.str(), res); + SWSS_LOG_ENTER(); - // Bring up vlan port by default - cmd.str(""); - cmd << IP_CMD << " link set " << VLAN_PREFIX << vlan_id << " up"; - EXEC_WITH_ERROR_THROW(cmd.str(), res); + // The command should be generated as: + // /bin/bash -c "/sbin/bridge vlan add vid {{vlan_id}} dev Bridge self && + // /sbin/ip link add link Bridge up name Vlan{{vlan_id}} address {{gMacAddress}} type vlan id {{vlan_id}}" + const std::string cmds = std::string("") + + BASH_CMD + " -c \"" + + BRIDGE_CMD + " vlan add vid " + std::to_string(vlan_id) + " dev " + DOT1Q_BRIDGE_NAME + " self && " + + IP_CMD + " link add link " + DOT1Q_BRIDGE_NAME + + " up" + + " name " + VLAN_PREFIX + std::to_string(vlan_id) + + " address " + gMacAddress.to_string() + + " type vlan id " + std::to_string(vlan_id) + "\""; + + std::string res; + EXEC_WITH_ERROR_THROW(cmds, res); return true; } bool VlanMgr::removeHostVlan(int vlan_id) { - stringstream cmd; - string res; + SWSS_LOG_ENTER(); - cmd << IP_CMD << " link del " << VLAN_PREFIX << vlan_id; - EXEC_WITH_ERROR_THROW(cmd.str(), res); + // The command should be generated as: + // /bin/bash -c "/sbin/ip link del Vlan{{vlan_id}} && + // /sbin/bridge vlan del vid {{vlan_id}} dev Bridge self" + const std::string cmds = std::string("") + + BASH_CMD + " -c \"" + + IP_CMD + " link del " + VLAN_PREFIX + std::to_string(vlan_id) + " && " + + BRIDGE_CMD + " vlan del vid " + std::to_string(vlan_id) + " dev " + DOT1Q_BRIDGE_NAME + " self\""; - cmd.str(""); - cmd << BRIDGE_CMD << " vlan del vid " << vlan_id << " dev " << DOT1Q_BRIDGE_NAME << " self"; - EXEC_WITH_ERROR_THROW(cmd.str(), res); + std::string res; + EXEC_WITH_ERROR_THROW(cmds, res); return true; } bool VlanMgr::setHostVlanAdminState(int vlan_id, const string &admin_status) { - stringstream cmd; - string res; + SWSS_LOG_ENTER(); + + // The command should be generated as: + // /sbin/ip link set Vlan{{vlan_id}} {{admin_status}} + const std::string cmds = std::string("") + + IP_CMD + " link set " + VLAN_PREFIX + std::to_string(vlan_id) + " " + admin_status; + + std::string res; + EXEC_WITH_ERROR_THROW(cmds, res); - cmd << IP_CMD << " link set " << VLAN_PREFIX << vlan_id << " " << admin_status; - EXEC_WITH_ERROR_THROW(cmd.str(), res); return true; } bool VlanMgr::setHostVlanMtu(int vlan_id, uint32_t mtu) { - stringstream cmd; - string res; + SWSS_LOG_ENTER(); + + // The command should be generated as: + // /sbin/ip link set Vlan{{vlan_id}} mtu {{mtu}} + const std::string cmds = std::string("") + + IP_CMD + " link set " + VLAN_PREFIX + std::to_string(vlan_id) + " mtu " + std::to_string(mtu); - cmd << IP_CMD << " link set " << VLAN_PREFIX << vlan_id << " mtu " << mtu; - int ret = swss::exec(cmd.str(), res); + std::string res; + int ret = swss::exec(cmds, res); if (ret == 0) { return true; } + /* VLAN mtu should not be larger than member mtu */ return false; } bool VlanMgr::addHostVlanMember(int vlan_id, const string &port_alias, const string& tagging_mode) { - stringstream cmd; - string res; + SWSS_LOG_ENTER(); - // Should be ok to run set master command more than one time. - cmd << IP_CMD << " link set " << port_alias << " master " << DOT1Q_BRIDGE_NAME; - EXEC_WITH_ERROR_THROW(cmd.str(), res); - cmd.str(""); + std::string tagging_cmd; if (tagging_mode == "untagged" || tagging_mode == "priority_tagged") { - // We are setting pvid as untagged vlan id. - cmd << BRIDGE_CMD << " vlan add vid " << vlan_id << " dev " << port_alias << " pvid untagged"; - } - else - { - cmd << BRIDGE_CMD << " vlan add vid " << vlan_id << " dev " << port_alias; + tagging_cmd = "pvid untagged"; } - EXEC_WITH_ERROR_THROW(cmd.str(), res); - cmd.str(""); - // Bring up vlan member port and set MTU to 9100 by default - cmd << IP_CMD << " link set " << port_alias << " up mtu " << MAX_MTU; - EXEC_WITH_ERROR_THROW(cmd.str(), res); + // The command should be generated as: + // /bin/bash -c "/sbin/ip link set {{port_alias}} master Bridge && + // /sbin/bridge vlan add vid {{vlan_id}} dev {{port_alias}} {{tagging_mode}} + // /sbin/ip link set {{port_alias}} up mtu 9100" + const std::string cmds = std::string("") + + BASH_CMD + " -c \"" + + IP_CMD + " link set " + port_alias + " master " + DOT1Q_BRIDGE_NAME + " && " + + BRIDGE_CMD + " vlan add vid " + std::to_string(vlan_id) + " dev " + port_alias + " " + tagging_cmd + " && " + + IP_CMD + " link set " + port_alias + " up mtu " + std::to_string(MAX_MTU) + "\""; + + std::string res; + EXEC_WITH_ERROR_THROW(cmds, res); return true; } bool VlanMgr::removeHostVlanMember(int vlan_id, const string &port_alias) { - stringstream cmd; - string res; + SWSS_LOG_ENTER(); - cmd << BRIDGE_CMD << " vlan del vid " << vlan_id << " dev " << port_alias; - EXEC_WITH_ERROR_THROW(cmd.str(), res); + // The command should be generated as: + // /bin/bash -c "/sbin/bridge vlan del vid {{vlan_id}} dev {{port_alias}} && + // /sbin/bridge vlan show dev {{port_alias}} | /bin/grep -q None && + // /sbin/ip link set {{port_alias}} nomaster" - cmd.str(""); // When port is not member of any VLAN, it shall be detached from Dot1Q bridge! - cmd << BRIDGE_CMD << " vlan show dev " << port_alias << " | " << GREP_CMD << " None"; - EXEC_WITH_ERROR_THROW(cmd.str(), res); - if (!res.empty()) - { - cmd.str(""); - cmd << IP_CMD << " link set " << port_alias << " nomaster"; - EXEC_WITH_ERROR_THROW(cmd.str(), res); - } + const std::string cmds = std::string("") + + BASH_CMD + " -c \"" + + BRIDGE_CMD + " vlan del vid " + std::to_string(vlan_id) + " dev " + port_alias + " && " + + BRIDGE_CMD + " vlan show dev " + port_alias + " | " + + GREP_CMD + " -q None && " + + IP_CMD + " link set " + port_alias + " nomaster\""; + + std::string res; + EXEC_WITH_ERROR_THROW(cmds, res); return true; } diff --git a/cfgmgr/vlanmgrd.cpp b/cfgmgr/vlanmgrd.cpp index 6fc0ed2efd..2ba33f9dff 100644 --- a/cfgmgr/vlanmgrd.cpp +++ b/cfgmgr/vlanmgrd.cpp @@ -61,7 +61,7 @@ int main(int argc, char **argv) * switch_mac set. * Dynamic switch_mac update is not supported for now. */ - Table table(&cfgDb, "DEVICE_METADATA", CONFIGDB_TABLE_NAME_SEPARATOR); + Table table(&cfgDb, "DEVICE_METADATA"); std::vector ovalues; table.get("localhost", ovalues); auto it = std::find_if( ovalues.begin(), ovalues.end(), [](const FieldValueTuple& t){ return t.first == "mac";} ); @@ -84,9 +84,9 @@ int main(int argc, char **argv) while (true) { Selectable *sel; - int fd, ret; + int ret; - ret = s.select(&sel, &fd, SELECT_TIMEOUT); + ret = s.select(&sel, SELECT_TIMEOUT); if (ret == Select::ERROR) { SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); diff --git a/doc/swss-schema.md b/doc/swss-schema.md index 9dd91752cf..8ab08ff382 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -419,7 +419,7 @@ Stores information about ACL tables on the switch. Port names are defined in [p key = ACL_TABLE:name ; acl_table_name must be unique ;field = value policy_desc = 1*255VCHAR ; name of the ACL policy table description - type = "mirror"/"l3" ; type of acl table, every type of + type = "mirror"/"l3"/"l3v6" ; type of acl table, every type of ; table defines the match/action a ; specific set of match and actions. ports = [0-max_ports]*port_name ; the ports to which this ACL @@ -459,8 +459,7 @@ Stores rules associated with a specific ACL table on the switch. ether_type = h16 ; Ethernet type field ip_type = ip_types ; options of the l2_protocol_type - ; field. Only v4 is support for - ; this stage. + ; field. ip_protocol = h8 ; options of the l3_protocol_type field @@ -470,6 +469,12 @@ Stores rules associated with a specific ACL table on the switch. dst_ip = ipv4_prefix ; options of the destination ipv4 ; address (and mask) field + src_ipv6 = ipv6_prefix ; options of the source ipv6 + ; address (and mask) field + + dst_ipv6 = ipv6_prefix ; options of the destination ipv6 + ; address (and mask) field + l4_src_port = port_num ; source L4 port or the l4_dst_port = port_num ; destination L4 port diff --git a/fpmsyncd/fpmlink.cpp b/fpmsyncd/fpmlink.cpp index 9dfedb6c1b..33103e8b31 100644 --- a/fpmsyncd/fpmlink.cpp +++ b/fpmsyncd/fpmlink.cpp @@ -81,23 +81,12 @@ void FpmLink::accept() SWSS_LOG_INFO("New connection accepted from: %s\n", inet_ntoa(client_addr.sin_addr)); } -void FpmLink::addFd(fd_set *fd) +int FpmLink::getFd() { - FD_SET(m_connection_socket, fd); + return m_connection_socket; } -bool FpmLink::isMe(fd_set *fd) -{ - return FD_ISSET(m_connection_socket, fd); -} - -int FpmLink::readCache() -{ - /* FPM doesn't have any caching */ - return NODATA; -} - -void FpmLink::readMe() +void FpmLink::readData() { fpm_msg_hdr_t *hdr; size_t msg_len; diff --git a/fpmsyncd/fpmlink.h b/fpmsyncd/fpmlink.h index 6394b6b870..4081607cdc 100644 --- a/fpmsyncd/fpmlink.h +++ b/fpmsyncd/fpmlink.h @@ -25,11 +25,8 @@ class FpmLink : public Selectable { /* Wait for connection (blocking) */ void accept(); - virtual void addFd(fd_set *fd); - virtual bool isMe(fd_set *fd); - virtual int readCache(); - virtual void readMe(); - + int getFd() override; + void readData() override; /* readMe throws FpmConnectionClosedException when connection is lost */ class FpmConnectionClosedException : public std::exception { diff --git a/fpmsyncd/fpmsyncd.cpp b/fpmsyncd/fpmsyncd.cpp index 3a4abaa9b0..d5a54f8fee 100644 --- a/fpmsyncd/fpmsyncd.cpp +++ b/fpmsyncd/fpmsyncd.cpp @@ -33,9 +33,8 @@ int main(int argc, char **argv) while (true) { Selectable *temps; - int tempfd; - /* Reading FPM messages forever (and calling "readMe" to read them) */ - s.select(&temps, &tempfd); + /* Reading FPM messages forever (and calling "readData" to read them) */ + s.select(&temps); pipeline.flush(); SWSS_LOG_DEBUG("Pipeline flushed"); } diff --git a/intfsyncd/intfsyncd.cpp b/intfsyncd/intfsyncd.cpp index 0878a42943..82ffba58af 100644 --- a/intfsyncd/intfsyncd.cpp +++ b/intfsyncd/intfsyncd.cpp @@ -33,8 +33,7 @@ int main(int argc, char **argv) while (true) { Selectable *temps; - int tempfd; - s.select(&temps, &tempfd); + s.select(&temps); } } catch (const std::exception& e) diff --git a/neighsyncd/neighsyncd.cpp b/neighsyncd/neighsyncd.cpp index 9153216c67..3694ba8cbf 100644 --- a/neighsyncd/neighsyncd.cpp +++ b/neighsyncd/neighsyncd.cpp @@ -32,8 +32,7 @@ int main(int argc, char **argv) while (true) { Selectable *temps; - int tempfd; - s.select(&temps, &tempfd); + s.select(&temps); } } catch (const std::exception& e) diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index 998d2926fe..811bc79db6 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -35,9 +35,13 @@ orchagent_SOURCES = \ fdborch.cpp \ aclorch.cpp \ saihelper.cpp \ - switchorch.cpp \ + switchorch.cpp \ pfcwdorch.cpp \ pfcactionhandler.cpp \ + crmorch.cpp \ + request_parser.cpp \ + vrforch.cpp \ + countercheckorch.cpp \ acltable.h \ aclorch.h \ bufferorch.h \ @@ -57,9 +61,13 @@ orchagent_SOURCES = \ qosorch.h \ routeorch.h \ saihelper.h \ - switchorch.h \ + switchorch.h \ swssnet.h \ - tunneldecaporch.h + tunneldecaporch.h \ + crmorch.h + request_parser.h \ + vrforch.h \ + countercheckorch.h orchagent_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) orchagent_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) diff --git a/orchagent/aclorch.cpp b/orchagent/aclorch.cpp index 753d3b82ef..7dc2bc2afe 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -1,4 +1,5 @@ #include +#include #include #include "aclorch.h" #include "logger.h" @@ -6,6 +7,7 @@ #include "ipprefix.h" #include "converter.h" #include "timer.h" +#include "crmorch.h" using namespace std; using namespace swss; @@ -25,11 +27,14 @@ extern sai_port_api_t* sai_port_api; extern sai_switch_api_t* sai_switch_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; +extern CrmOrch *gCrmOrch; acl_rule_attr_lookup_t aclMatchLookup = { { MATCH_SRC_IP, SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP }, { MATCH_DST_IP, SAI_ACL_ENTRY_ATTR_FIELD_DST_IP }, + { MATCH_SRC_IPV6, SAI_ACL_ENTRY_ATTR_FIELD_SRC_IPV6 }, + { MATCH_DST_IPV6, SAI_ACL_ENTRY_ATTR_FIELD_DST_IPV6 }, { MATCH_L4_SRC_PORT, SAI_ACL_ENTRY_ATTR_FIELD_L4_SRC_PORT }, { MATCH_L4_DST_PORT, SAI_ACL_ENTRY_ATTR_FIELD_L4_DST_PORT }, { MATCH_ETHER_TYPE, SAI_ACL_ENTRY_ATTR_FIELD_ETHER_TYPE }, @@ -51,8 +56,10 @@ acl_rule_attr_lookup_t aclL3ActionLookup = static acl_table_type_lookup_t aclTableTypeLookUp = { - { TABLE_TYPE_L3, ACL_TABLE_L3 }, - { TABLE_TYPE_MIRROR, ACL_TABLE_MIRROR } + { TABLE_TYPE_L3, ACL_TABLE_L3 }, + { TABLE_TYPE_L3V6, ACL_TABLE_L3V6 }, + { TABLE_TYPE_MIRROR, ACL_TABLE_MIRROR }, + { TABLE_TYPE_CTRLPLANE, ACL_TABLE_CTRLPLANE } }; static acl_stage_type_lookup_t aclStageLookUp = @@ -206,16 +213,24 @@ bool AclRule::validateAddMatch(string attr_name, string attr_value) { IpPrefix ip(attr_value); - if (ip.isV4()) + if (!ip.isV4()) { - value.aclfield.data.ip4 = ip.getIp().getV4Addr(); - value.aclfield.mask.ip4 = ip.getMask().getV4Addr(); + SWSS_LOG_ERROR("IP type is not v4 type"); + return false; } - else + value.aclfield.data.ip4 = ip.getIp().getV4Addr(); + value.aclfield.mask.ip4 = ip.getMask().getV4Addr(); + } + else if (attr_name == MATCH_SRC_IPV6 || attr_name == MATCH_DST_IPV6) + { + IpPrefix ip(attr_value); + if (ip.isV4()) { - memcpy(value.aclfield.data.ip6, ip.getIp().getV6Addr(), 16); - memcpy(value.aclfield.mask.ip6, ip.getMask().getV6Addr(), 16); + SWSS_LOG_ERROR("IP type is not v6 type"); + return false; } + memcpy(value.aclfield.data.ip6, ip.getIp().getV6Addr(), 16); + memcpy(value.aclfield.mask.ip6, ip.getMask().getV6Addr(), 16); } else if ((attr_name == MATCH_L4_SRC_PORT_RANGE) || (attr_name == MATCH_L4_DST_PORT_RANGE)) { @@ -366,6 +381,8 @@ bool AclRule::create() decreaseNextHopRefCount(); } + gCrmOrch->incCrmAclTableUsedCounter(CrmResourceType::CRM_ACL_ENTRY, m_tableOid); + return (status == SAI_STATUS_SUCCESS); } @@ -410,6 +427,8 @@ bool AclRule::remove() return false; } + gCrmOrch->decCrmAclTableUsedCounter(CrmResourceType::CRM_ACL_ENTRY, m_tableOid); + m_ruleOid = SAI_NULL_OBJECT_ID; decreaseNextHopRefCount(); @@ -459,7 +478,7 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir throw runtime_error("ACL rule action is not found in rule " + rule); } - if (type != ACL_TABLE_L3 && type != ACL_TABLE_MIRROR) + if (type != ACL_TABLE_L3 && type != ACL_TABLE_L3V6 && type != ACL_TABLE_MIRROR) { throw runtime_error("Unknown table type."); } @@ -474,6 +493,16 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir { return make_shared(acl, rule, table, type); } + /* L3V6 rules can exist only in L3V6 table */ + else if (type == ACL_TABLE_L3V6) + { + return make_shared(acl, rule, table, type); + } + /* Pfcwd rules can exist only in PFCWD table */ + else if (type == ACL_TABLE_PFCWD) + { + return make_shared(acl, rule, table, type); + } throw runtime_error("Wrong combination of table type and action in rule " + rule); } @@ -503,6 +532,8 @@ bool AclRule::createCounter() return false; } + gCrmOrch->incCrmAclTableUsedCounter(CrmResourceType::CRM_ACL_COUNTER, m_tableOid); + return true; } @@ -535,6 +566,8 @@ bool AclRule::removeCounter() return false; } + gCrmOrch->decCrmAclTableUsedCounter(CrmResourceType::CRM_ACL_COUNTER, m_tableOid); + SWSS_LOG_INFO("Removing record about the counter %lX from the DB", m_counterOid); AclOrch::getCountersTable().del(getTableId() + ":" + getId()); @@ -685,6 +718,11 @@ bool AclRuleL3::validateAddMatch(string attr_name, string attr_value) SWSS_LOG_ERROR("DSCP match is not supported for the tables of type L3"); return false; } + if (attr_name == MATCH_SRC_IPV6 || attr_name == MATCH_DST_IPV6) + { + SWSS_LOG_ERROR("IPv6 address match is not supported for the tables of type L3"); + return false; + } return AclRule::validateAddMatch(attr_name, attr_value); } @@ -706,6 +744,46 @@ void AclRuleL3::update(SubjectType, void *) // Do nothing } + +AclRulePfcwd::AclRulePfcwd(AclOrch *aclOrch, string rule, string table, acl_table_type_t type) : + AclRuleL3(aclOrch, rule, table, type) +{ +} + +bool AclRulePfcwd::validateAddMatch(string attr_name, string attr_value) +{ + if (attr_name != MATCH_TC) + { + SWSS_LOG_ERROR("%s is not supported for the tables of type Pfcwd", attr_name.c_str()); + return false; + } + + return AclRule::validateAddMatch(attr_name, attr_value); +} + +AclRuleL3V6::AclRuleL3V6(AclOrch *aclOrch, string rule, string table, acl_table_type_t type) : + AclRuleL3(aclOrch, rule, table, type) +{ +} + + +bool AclRuleL3V6::validateAddMatch(string attr_name, string attr_value) +{ + if (attr_name == MATCH_DSCP) + { + SWSS_LOG_ERROR("DSCP match is not supported for the tables of type L3V6"); + return false; + } + if (attr_name == MATCH_SRC_IP || attr_name == MATCH_DST_IP) + { + SWSS_LOG_ERROR("IPv4 address match is not supported for the tables of type L3V6"); + return false; + } + + return AclRule::validateAddMatch(attr_name, attr_value); +} + + AclRuleMirror::AclRuleMirror(AclOrch *aclOrch, MirrorOrch *mirror, string rule, string table, acl_table_type_t type) : AclRule(aclOrch, rule, table, type), m_state(false), @@ -734,7 +812,8 @@ bool AclRuleMirror::validateAddAction(string attr_name, string attr_value) bool AclRuleMirror::validateAddMatch(string attr_name, string attr_value) { - if (m_tableType == ACL_TABLE_L3 && attr_name == MATCH_DSCP) + if ((m_tableType == ACL_TABLE_L3 || m_tableType == ACL_TABLE_L3V6) + && attr_name == MATCH_DSCP) { SWSS_LOG_ERROR("DSCP match is not supported for the tables of type L3"); return false; @@ -855,7 +934,8 @@ void AclRuleMirror::update(SubjectType type, void *cntx) bool AclTable::validate() { - if (type == ACL_TABLE_UNKNOWN) return false; + // Control plane ACLs are handled by a separate process + if (type == ACL_TABLE_UNKNOWN || type == ACL_TABLE_CTRLPLANE) return false; if (ports.empty()) return false; return true; } @@ -878,13 +958,62 @@ bool AclTable::create() SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE }; - attr.id = SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST; + set binds; + for (const auto& portid_pair : ports) + { + Port port; + if (!gPortsOrch->getPort(portid_pair.first, port)) + { + continue; + } + + switch (port.m_type) + { + case Port::PHY: + binds.insert(SAI_ACL_BIND_POINT_TYPE_PORT); + break; + case Port::VLAN: + binds.insert(SAI_ACL_BIND_POINT_TYPE_VLAN); + break; + case Port::LAG: + binds.insert(SAI_ACL_BIND_POINT_TYPE_LAG); + break; + default: + return SAI_STATUS_FAILURE; + } + } + vector bpoint_list; - bpoint_list.push_back(SAI_ACL_BIND_POINT_TYPE_PORT); - attr.value.s32list.count = 1; + for (auto bind : binds) + { + bpoint_list.push_back(bind); + } + + attr.id = SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST; + attr.value.s32list.count = static_cast(bpoint_list.size()); attr.value.s32list.list = bpoint_list.data(); table_attrs.push_back(attr); + if (type == ACL_TABLE_PFCWD) + { + attr.id = SAI_ACL_TABLE_ATTR_FIELD_TC; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_ACL_STAGE; + attr.value.s32 = stage == ACL_STAGE_INGRESS ? SAI_ACL_STAGE_INGRESS : SAI_ACL_STAGE_EGRESS; + table_attrs.push_back(attr); + + sai_status_t status = sai_acl_api->create_acl_table(&m_oid, gSwitchId, (uint32_t)table_attrs.size(), table_attrs.data()); + + if (status == SAI_STATUS_SUCCESS) + { + gCrmOrch->incCrmAclUsedCounter(CrmResourceType::CRM_ACL_TABLE, (sai_acl_stage_t) attr.value.s32, SAI_ACL_BIND_POINT_TYPE_PORT); + } + + return status == SAI_STATUS_SUCCESS; + } + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE; attr.value.booldata = true; table_attrs.push_back(attr); @@ -897,14 +1026,26 @@ bool AclTable::create() attr.value.booldata = true; table_attrs.push_back(attr); - attr.id = SAI_ACL_TABLE_ATTR_FIELD_SRC_IP; - attr.value.booldata = true; - table_attrs.push_back(attr); + if (type == ACL_TABLE_L3V6) + { + attr.id = SAI_ACL_TABLE_ATTR_FIELD_SRC_IPV6; + attr.value.booldata = true; + table_attrs.push_back(attr); - attr.id = SAI_ACL_TABLE_ATTR_FIELD_DST_IP; - attr.value.booldata = true; - table_attrs.push_back(attr); + attr.id = SAI_ACL_TABLE_ATTR_FIELD_DST_IPV6; + attr.value.booldata = true; + table_attrs.push_back(attr); + } + else + { + attr.id = SAI_ACL_TABLE_ATTR_FIELD_SRC_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + attr.id = SAI_ACL_TABLE_ATTR_FIELD_DST_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + } attr.id = SAI_ACL_TABLE_ATTR_FIELD_L4_SRC_PORT; attr.value.booldata = true; table_attrs.push_back(attr); @@ -941,6 +1082,12 @@ bool AclTable::create() } sai_status_t status = sai_acl_api->create_acl_table(&m_oid, gSwitchId, (uint32_t)table_attrs.size(), table_attrs.data()); + + if (status == SAI_STATUS_SUCCESS) + { + gCrmOrch->incCrmAclUsedCounter(CrmResourceType::CRM_ACL_TABLE, (sai_acl_stage_t) attr.value.s32, SAI_ACL_BIND_POINT_TYPE_PORT); + } + return status == SAI_STATUS_SUCCESS; } @@ -1356,6 +1503,10 @@ bool AclOrch::removeAclTable(string table_id) { SWSS_LOG_NOTICE("Successfully deleted ACL table %s", table_id.c_str()); m_AclTables.erase(table_oid); + + sai_acl_stage_t stage = (m_AclTables[table_oid].stage == ACL_STAGE_INGRESS) ? SAI_ACL_STAGE_INGRESS : SAI_ACL_STAGE_EGRESS; + gCrmOrch->decCrmAclUsedCounter(CrmResourceType::CRM_ACL_TABLE, stage, SAI_ACL_BIND_POINT_TYPE_PORT, table_oid); + return true; } else @@ -1427,6 +1578,8 @@ void AclOrch::doAclTableTask(Consumer &consumer) if (!processAclTableType(attr_value, newTable.type)) { SWSS_LOG_ERROR("Failed to process table type for table %s", table_id.c_str()); + bAllAttributesOk = false; + break; } } else if (attr_name == TABLE_PORTS) @@ -1438,6 +1591,8 @@ void AclOrch::doAclTableTask(Consumer &consumer) if (!suc) { SWSS_LOG_ERROR("Failed to process table ports for table %s", table_id.c_str()); + bAllAttributesOk = false; + break; } } else if (attr_name == TABLE_STAGE) @@ -1445,6 +1600,8 @@ void AclOrch::doAclTableTask(Consumer &consumer) if (!processAclTableStage(attr_value, newTable.stage)) { SWSS_LOG_ERROR("Failed to process table stage for table %s", table_id.c_str()); + bAllAttributesOk = false; + break; } } else @@ -1514,35 +1671,32 @@ void AclOrch::doAclRuleTask(Consumer &consumer) continue; } - if (bAllAttributesOk) - { - newRule = AclRule::makeShared(m_AclTables[table_oid].type, this, m_mirrorOrch, rule_id, table_id, t); + newRule = AclRule::makeShared(m_AclTables[table_oid].type, this, m_mirrorOrch, rule_id, table_id, t); - for (const auto& itr : kfvFieldsValues(t)) - { - string attr_name = toUpper(fvField(itr)); - string attr_value = fvValue(itr); + for (const auto& itr : kfvFieldsValues(t)) + { + string attr_name = toUpper(fvField(itr)); + string attr_value = fvValue(itr); - SWSS_LOG_INFO("ATTRIBUTE: %s %s", attr_name.c_str(), attr_value.c_str()); + SWSS_LOG_INFO("ATTRIBUTE: %s %s", attr_name.c_str(), attr_value.c_str()); - if (newRule->validateAddPriority(attr_name, attr_value)) - { - SWSS_LOG_INFO("Added priority attribute"); - } - else if (newRule->validateAddMatch(attr_name, attr_value)) - { - SWSS_LOG_INFO("Added match attribute '%s'", attr_name.c_str()); - } - else if (newRule->validateAddAction(attr_name, attr_value)) - { - SWSS_LOG_INFO("Added action attribute '%s'", attr_name.c_str()); - } - else - { - SWSS_LOG_ERROR("Unknown or invalid rule attribute '%s : %s'", attr_name.c_str(), attr_value.c_str()); - bAllAttributesOk = false; - break; - } + if (newRule->validateAddPriority(attr_name, attr_value)) + { + SWSS_LOG_INFO("Added priority attribute"); + } + else if (newRule->validateAddMatch(attr_name, attr_value)) + { + SWSS_LOG_INFO("Added match attribute '%s'", attr_name.c_str()); + } + else if (newRule->validateAddAction(attr_name, attr_value)) + { + SWSS_LOG_INFO("Added action attribute '%s'", attr_name.c_str()); + } + else + { + SWSS_LOG_ERROR("Unknown or invalid rule attribute '%s : %s'", attr_name.c_str(), attr_value.c_str()); + bAllAttributesOk = false; + break; } } @@ -1608,13 +1762,26 @@ bool AclOrch::processPorts(string portsList, std::function acl_table_type_lookup_t; @@ -190,10 +198,25 @@ class AclRuleL3: public AclRule bool validateAddMatch(string attr_name, string attr_value); bool validate(); void update(SubjectType, void *); -private: +protected: sai_object_id_t getRedirectObjectId(const string& redirect_param); }; +class AclRuleL3V6: public AclRuleL3 +{ +public: + AclRuleL3V6(AclOrch *m_pAclOrch, string rule, string table, acl_table_type_t type); + bool validateAddMatch(string attr_name, string attr_value); +}; + +class AclRulePfcwd: public AclRuleL3 +{ +public: + AclRulePfcwd(AclOrch *m_pAclOrch, string rule, string table, acl_table_type_t type); + bool validateAddMatch(string attr_name, string attr_value); +}; + + class AclRuleMirror: public AclRule { public: diff --git a/orchagent/bufferorch.cpp b/orchagent/bufferorch.cpp index 27474b789c..170fa178b6 100644 --- a/orchagent/bufferorch.cpp +++ b/orchagent/bufferorch.cpp @@ -17,12 +17,12 @@ extern sai_object_id_t gSwitchId; using namespace std; type_map BufferOrch::m_buffer_type_maps = { - {APP_BUFFER_POOL_TABLE_NAME, new object_map()}, - {APP_BUFFER_PROFILE_TABLE_NAME, new object_map()}, - {APP_BUFFER_QUEUE_TABLE_NAME, new object_map()}, - {APP_BUFFER_PG_TABLE_NAME, new object_map()}, - {APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, new object_map()}, - {APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME, new object_map()} + {CFG_BUFFER_POOL_TABLE_NAME, new object_map()}, + {CFG_BUFFER_PROFILE_TABLE_NAME, new object_map()}, + {CFG_BUFFER_QUEUE_TABLE_NAME, new object_map()}, + {CFG_BUFFER_PG_TABLE_NAME, new object_map()}, + {CFG_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, new object_map()}, + {CFG_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME, new object_map()} }; BufferOrch::BufferOrch(DBConnector *db, vector &tableNames) : Orch(db, tableNames) @@ -34,12 +34,12 @@ BufferOrch::BufferOrch(DBConnector *db, vector &tableNames) : Orch(db, t void BufferOrch::initTableHandlers() { SWSS_LOG_ENTER(); - m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_POOL_TABLE_NAME, &BufferOrch::processBufferPool)); - m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_PROFILE_TABLE_NAME, &BufferOrch::processBufferProfile)); - m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_QUEUE_TABLE_NAME, &BufferOrch::processQueue)); - m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_PG_TABLE_NAME, &BufferOrch::processPriorityGroup)); - m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, &BufferOrch::processIngressBufferProfileList)); - m_bufferHandlerMap.insert(buffer_handler_pair(APP_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME, &BufferOrch::processEgressBufferProfileList)); + m_bufferHandlerMap.insert(buffer_handler_pair(CFG_BUFFER_POOL_TABLE_NAME, &BufferOrch::processBufferPool)); + m_bufferHandlerMap.insert(buffer_handler_pair(CFG_BUFFER_PROFILE_TABLE_NAME, &BufferOrch::processBufferProfile)); + m_bufferHandlerMap.insert(buffer_handler_pair(CFG_BUFFER_QUEUE_TABLE_NAME, &BufferOrch::processQueue)); + m_bufferHandlerMap.insert(buffer_handler_pair(CFG_BUFFER_PG_TABLE_NAME, &BufferOrch::processPriorityGroup)); + m_bufferHandlerMap.insert(buffer_handler_pair(CFG_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, &BufferOrch::processIngressBufferProfileList)); + m_bufferHandlerMap.insert(buffer_handler_pair(CFG_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME, &BufferOrch::processEgressBufferProfileList)); } task_process_status BufferOrch::processBufferPool(Consumer &consumer) @@ -220,6 +220,12 @@ task_process_status BufferOrch::processBufferProfile(Consumer &consumer) attr.id = SAI_BUFFER_PROFILE_ATTR_XON_TH; attribs.push_back(attr); } + else if (field == buffer_xon_offset_field_name) + { + attr.value.u32 = (uint32_t)stoul(value); + attr.id = SAI_BUFFER_PROFILE_ATTR_XON_OFFSET_TH; + attribs.push_back(attr); + } else if (field == buffer_xoff_field_name) { attr.value.u32 = (uint32_t)stoul(value); @@ -239,7 +245,7 @@ task_process_status BufferOrch::processBufferProfile(Consumer &consumer) attribs.push_back(attr); attr.id = SAI_BUFFER_PROFILE_ATTR_SHARED_DYNAMIC_TH; - attr.value.u32 = (uint32_t)stoul(value); + attr.value.s8 = (sai_int8_t)stol(value); attribs.push_back(attr); } else if (field == buffer_static_th_field_name) @@ -315,7 +321,7 @@ task_process_status BufferOrch::processQueue(Consumer &consumer) sai_uint32_t range_low, range_high; SWSS_LOG_DEBUG("Processing:%s", key.c_str()); - tokens = tokenize(key, delimiter); + tokens = tokenize(key, config_db_key_delimiter); if (tokens.size() != 2) { SWSS_LOG_ERROR("malformed key:%s. Must contain 2 tokens", key.c_str()); @@ -372,7 +378,7 @@ task_process_status BufferOrch::processQueue(Consumer &consumer) } /* -Input sample "BUFFER_PG_TABLE:Ethernet4,Ethernet45:10-15" +Input sample "BUFFER_PG_TABLE|Ethernet4,Ethernet45|10-15" */ task_process_status BufferOrch::processPriorityGroup(Consumer &consumer) { @@ -385,8 +391,13 @@ task_process_status BufferOrch::processPriorityGroup(Consumer &consumer) vector tokens; sai_uint32_t range_low, range_high; + if (op != SET_COMMAND) + { + return task_process_status::task_success; + } + SWSS_LOG_DEBUG("processing:%s", key.c_str()); - tokens = tokenize(key, delimiter); + tokens = tokenize(key, config_db_key_delimiter); if (tokens.size() != 2) { SWSS_LOG_ERROR("malformed key:%s. Must contain 2 tokens", key.c_str()); @@ -456,9 +467,9 @@ task_process_status BufferOrch::processIngressBufferProfileList(Consumer &consum string op = kfvOp(tuple); SWSS_LOG_DEBUG("processing:%s", key.c_str()); - if (consumer.getTableName() != APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME) + if (consumer.getTableName() != CFG_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME) { - SWSS_LOG_ERROR("Key with invalid table type passed in %s, expected:%s", key.c_str(), APP_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME); + SWSS_LOG_ERROR("Key with invalid table type passed in %s, expected:%s", key.c_str(), CFG_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME); return task_process_status::task_invalid_entry; } vector port_names = tokenize(key, list_item_delimiter); diff --git a/orchagent/bufferorch.h b/orchagent/bufferorch.h index 048fad6cf5..f05f8071aa 100644 --- a/orchagent/bufferorch.h +++ b/orchagent/bufferorch.h @@ -13,6 +13,7 @@ const string buffer_pool_mode_dynamic_value = "dynamic"; const string buffer_pool_mode_static_value = "static"; const string buffer_pool_xoff_field_name = "xoff"; const string buffer_xon_field_name = "xon"; +const string buffer_xon_offset_field_name = "xon_offset"; const string buffer_xoff_field_name = "xoff"; const string buffer_dynamic_th_field_name = "dynamic_th"; const string buffer_static_th_field_name = "static_th"; diff --git a/orchagent/copporch.cpp b/orchagent/copporch.cpp index b7471d6329..29bbc8eb2f 100644 --- a/orchagent/copporch.cpp +++ b/orchagent/copporch.cpp @@ -67,7 +67,8 @@ map trap_id_map = { {"snmp", SAI_HOSTIF_TRAP_TYPE_SNMP}, {"router_custom_range", SAI_HOSTIF_TRAP_TYPE_ROUTER_CUSTOM_RANGE_BASE}, {"l3_mtu_error", SAI_HOSTIF_TRAP_TYPE_L3_MTU_ERROR}, - {"ttl_error", SAI_HOSTIF_TRAP_TYPE_TTL_ERROR} + {"ttl_error", SAI_HOSTIF_TRAP_TYPE_TTL_ERROR}, + {"udld", SAI_HOSTIF_TRAP_TYPE_UDLD} }; map packet_action_map = { diff --git a/orchagent/countercheckorch.cpp b/orchagent/countercheckorch.cpp new file mode 100644 index 0000000000..f404c07e51 --- /dev/null +++ b/orchagent/countercheckorch.cpp @@ -0,0 +1,246 @@ +#include "countercheckorch.h" +#include "portsorch.h" +#include "select.h" +#include "notifier.h" +#include "redisclient.h" +#include "sai_serialize.h" + +#define COUNTER_CHECK_POLL_TIMEOUT_SEC (5 * 60) + +extern sai_port_api_t *sai_port_api; + +extern PortsOrch *gPortsOrch; + +CounterCheckOrch& CounterCheckOrch::getInstance(DBConnector *db) +{ + SWSS_LOG_ENTER(); + + static vector tableNames = {}; + static CounterCheckOrch *wd = new CounterCheckOrch(db, tableNames); + + return *wd; +} + +CounterCheckOrch::CounterCheckOrch(DBConnector *db, vector &tableNames): + Orch(db, tableNames), + m_countersDb(new DBConnector(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_countersTable(new Table(m_countersDb.get(), COUNTERS_TABLE)) +{ + SWSS_LOG_ENTER(); + + auto interv = timespec { .tv_sec = COUNTER_CHECK_POLL_TIMEOUT_SEC, .tv_nsec = 0 }; + auto timer = new SelectableTimer(interv); + auto executor = new ExecutableTimer(timer, this); + Orch::addExecutor("MC_COUNTERS_POLL", executor); + timer->start(); +} + +CounterCheckOrch::~CounterCheckOrch(void) +{ + SWSS_LOG_ENTER(); +} + +void CounterCheckOrch::doTask(SelectableTimer &timer) +{ + SWSS_LOG_ENTER(); + + mcCounterCheck(); + pfcFrameCounterCheck(); +} + +void CounterCheckOrch::mcCounterCheck() +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL; + + for (auto& i : m_mcCountersMap) + { + auto oid = i.first; + auto mcCounters = i.second; + + Port port; + if (!gPortsOrch->getPort(oid, port)) + { + SWSS_LOG_ERROR("Invalid port oid 0x%lx", oid); + continue; + } + + auto newMcCounters = getQueueMcCounters(port); + + sai_status_t status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get PFC mask on port %s: %d", port.m_alias.c_str(), status); + continue; + } + + uint8_t pfcMask = attr.value.u8; + + for (size_t prio = 0; prio != mcCounters.size(); prio++) + { + bool isLossy = ((1 << prio) & pfcMask) == 0; + if (newMcCounters[prio] == numeric_limits::max()) + { + SWSS_LOG_WARN("Could not retreive MC counters on queue %lu port %s", + prio, + port.m_alias.c_str()); + } + else if (!isLossy && mcCounters[prio] < newMcCounters[prio]) + { + SWSS_LOG_WARN("Got Multicast %lu frame(s) on lossless queue %lu port %s", + newMcCounters[prio] - mcCounters[prio], + prio, + port.m_alias.c_str()); + } + } + + i.second= newMcCounters; + } +} + +void CounterCheckOrch::pfcFrameCounterCheck() +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL; + + for (auto& i : m_pfcFrameCountersMap) + { + auto oid = i.first; + auto counters = i.second; + auto newCounters = getPfcFrameCounters(oid); + + Port port; + if (!gPortsOrch->getPort(oid, port)) + { + SWSS_LOG_ERROR("Invalid port oid 0x%lx", oid); + continue; + } + + sai_status_t status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get PFC mask on port %s: %d", port.m_alias.c_str(), status); + continue; + } + + uint8_t pfcMask = attr.value.u8; + + for (size_t prio = 0; prio != counters.size(); prio++) + { + bool isLossy = ((1 << prio) & pfcMask) == 0; + if (newCounters[prio] == numeric_limits::max()) + { + SWSS_LOG_WARN("Could not retreive PFC frame count on queue %lu port %s", + prio, + port.m_alias.c_str()); + } + else if (isLossy && counters[prio] < newCounters[prio]) + { + SWSS_LOG_WARN("Got PFC %lu frame(s) on lossy queue %lu port %s", + newCounters[prio] - counters[prio], + prio, + port.m_alias.c_str()); + } + } + + i.second = newCounters; + } +} + + +PfcFrameCounters CounterCheckOrch::getPfcFrameCounters(sai_object_id_t portId) +{ + SWSS_LOG_ENTER(); + + vector fieldValues; + PfcFrameCounters counters; + counters.fill(numeric_limits::max()); + + static const array counterNames = + { + "SAI_PORT_STAT_PFC_0_RX_PKTS", + "SAI_PORT_STAT_PFC_1_RX_PKTS", + "SAI_PORT_STAT_PFC_2_RX_PKTS", + "SAI_PORT_STAT_PFC_3_RX_PKTS", + "SAI_PORT_STAT_PFC_4_RX_PKTS", + "SAI_PORT_STAT_PFC_5_RX_PKTS", + "SAI_PORT_STAT_PFC_6_RX_PKTS", + "SAI_PORT_STAT_PFC_7_RX_PKTS" + }; + + if (!m_countersTable->get(sai_serialize_object_id(portId), fieldValues)) + { + return move(counters); + } + + for (const auto& fv : fieldValues) + { + const auto field = fvField(fv); + const auto value = fvValue(fv); + + + for (size_t prio = 0; prio != counterNames.size(); prio++) + { + if (field == counterNames[prio]) + { + counters[prio] = stoul(value); + } + } + } + + return move(counters); +} + +QueueMcCounters CounterCheckOrch::getQueueMcCounters( + const Port& port) +{ + SWSS_LOG_ENTER(); + + vector fieldValues; + QueueMcCounters counters; + RedisClient redisClient(m_countersDb.get()); + + for (uint8_t prio = 0; prio < port.m_queue_ids.size(); prio++) + { + sai_object_id_t queueId = port.m_queue_ids[prio]; + auto queueIdStr = sai_serialize_object_id(queueId); + auto queueType = redisClient.hget(COUNTERS_QUEUE_TYPE_MAP, queueIdStr); + + if (queueType.get() == nullptr || *queueType != "SAI_QUEUE_TYPE_MULTICAST" || !m_countersTable->get(queueIdStr, fieldValues)) + { + continue; + } + + uint64_t pkts = numeric_limits::max(); + for (const auto& fv : fieldValues) + { + const auto field = fvField(fv); + const auto value = fvValue(fv); + + if (field == "SAI_QUEUE_STAT_PACKETS") + { + pkts = stoul(value); + } + } + counters.push_back(pkts); + } + + return move(counters); +} + + +void CounterCheckOrch::addPort(const Port& port) +{ + m_mcCountersMap.emplace(port.m_port_id, getQueueMcCounters(port)); + m_pfcFrameCountersMap.emplace(port.m_port_id, getPfcFrameCounters(port.m_port_id)); +} + +void CounterCheckOrch::removePort(const Port& port) +{ + m_mcCountersMap.erase(port.m_port_id); + m_pfcFrameCountersMap.erase(port.m_port_id); +} diff --git a/orchagent/countercheckorch.h b/orchagent/countercheckorch.h new file mode 100644 index 0000000000..7d951728bd --- /dev/null +++ b/orchagent/countercheckorch.h @@ -0,0 +1,42 @@ +#ifndef COUNTERCHECK_ORCH_H +#define COUNTERCHECK_ORCH_H + +#include "orch.h" +#include "port.h" +#include "timer.h" +#include + +#define PFC_WD_TC_MAX 8 + +extern "C" { +#include "sai.h" +} + +typedef vector QueueMcCounters; +typedef array PfcFrameCounters; + +class CounterCheckOrch: public Orch +{ +public: + static CounterCheckOrch& getInstance(DBConnector *db = nullptr); + virtual void doTask(SelectableTimer &timer); + virtual void doTask(Consumer &consumer) {} + void addPort(const Port& port); + void removePort(const Port& port); + +private: + CounterCheckOrch(DBConnector *db, vector &tableNames); + virtual ~CounterCheckOrch(void); + QueueMcCounters getQueueMcCounters(const Port& port); + PfcFrameCounters getPfcFrameCounters(sai_object_id_t portId); + void mcCounterCheck(); + void pfcFrameCounterCheck(); + + map m_mcCountersMap; + map m_pfcFrameCountersMap; + + shared_ptr m_countersDb = nullptr; + shared_ptr m_countersTable = nullptr; +}; + +#endif diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp new file mode 100644 index 0000000000..976d915a4a --- /dev/null +++ b/orchagent/crmorch.cpp @@ -0,0 +1,622 @@ +#include + +#include "crmorch.h" +#include "converter.h" +#include "timer.h" + +#define CRM_POLLING_INTERVAL "polling_interval" +#define CRM_COUNTERS_TABLE_KEY "STATS" + +#define CRM_POLLING_INTERVAL_DEFAULT (5 * 60) +#define CRM_THRESHOLD_TYPE_DEFAULT CrmThresholdType::CRM_PERCENTAGE +#define CRM_THRESHOLD_LOW_DEFAULT 70 +#define CRM_THRESHOLD_HIGH_DEFAULT 85 +#define CRM_EXCEEDED_MSG_MAX 10 + +extern sai_object_id_t gSwitchId; +extern sai_switch_api_t *sai_switch_api; +extern sai_acl_api_t *sai_acl_api; + +using namespace std; + + +const map crmResTypeNameMap = +{ + { CrmResourceType::CRM_IPV4_ROUTE, "IPV4_ROUTE" }, + { CrmResourceType::CRM_IPV6_ROUTE, "IPV6_ROUTE" }, + { CrmResourceType::CRM_IPV4_NEXTHOP, "IPV4_NEXTHOP" }, + { CrmResourceType::CRM_IPV6_NEXTHOP, "IPV6_NEXTHOP" }, + { CrmResourceType::CRM_IPV4_NEIGHBOR, "IPV4_NEIGHBOR" }, + { CrmResourceType::CRM_IPV6_NEIGHBOR, "IPV6_Neighbor" }, + { CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER, "NEXTHOP_GROUP_MEMBER" }, + { CrmResourceType::CRM_NEXTHOP_GROUP, "NEXTHOP_GROUP" }, + { CrmResourceType::CRM_ACL_TABLE, "ACL_TABLE" }, + { CrmResourceType::CRM_ACL_GROUP, "ACL_GROUP" }, + { CrmResourceType::CRM_ACL_ENTRY, "ACL_ENTRY" }, + { CrmResourceType::CRM_ACL_COUNTER, "ACL_COUNTER" }, + { CrmResourceType::CRM_FDB_ENTRY, "FDB_ENTRY" } +}; + +const map crmResSaiAvailAttrMap = +{ + { CrmResourceType::CRM_IPV4_ROUTE, SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY }, + { CrmResourceType::CRM_IPV6_ROUTE, SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY }, + { CrmResourceType::CRM_IPV4_NEXTHOP, SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY }, + { CrmResourceType::CRM_IPV6_NEXTHOP, SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY }, + { CrmResourceType::CRM_IPV4_NEIGHBOR, SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY }, + { CrmResourceType::CRM_IPV6_NEIGHBOR, SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY }, + { CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER, SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY }, + { CrmResourceType::CRM_NEXTHOP_GROUP, SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY }, + { CrmResourceType::CRM_ACL_TABLE, SAI_SWITCH_ATTR_AVAILABLE_ACL_TABLE }, + { CrmResourceType::CRM_ACL_GROUP, SAI_SWITCH_ATTR_AVAILABLE_ACL_TABLE_GROUP }, + { CrmResourceType::CRM_ACL_ENTRY, SAI_ACL_TABLE_ATTR_AVAILABLE_ACL_ENTRY }, + { CrmResourceType::CRM_ACL_COUNTER, SAI_ACL_TABLE_ATTR_AVAILABLE_ACL_COUNTER }, + { CrmResourceType::CRM_FDB_ENTRY, SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY } +}; + +const map crmThreshTypeResMap = +{ + { "ipv4_route_threshold_type", CrmResourceType::CRM_IPV4_ROUTE }, + { "ipv6_route_threshold_type", CrmResourceType::CRM_IPV6_ROUTE }, + { "ipv4_nexthop_threshold_type", CrmResourceType::CRM_IPV4_NEXTHOP }, + { "ipv6_nexthop_threshold_type", CrmResourceType::CRM_IPV6_NEXTHOP }, + { "ipv4_neighbor_threshold_type", CrmResourceType::CRM_IPV4_NEIGHBOR }, + { "ipv6_neighbor_threshold_type", CrmResourceType::CRM_IPV6_NEIGHBOR }, + { "nexthop_group_member_threshold_type", CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER }, + { "nexthop_group_threshold_type", CrmResourceType::CRM_NEXTHOP_GROUP }, + { "acl_table_threshold_type", CrmResourceType::CRM_ACL_TABLE }, + { "acl_group_threshold_type", CrmResourceType::CRM_ACL_GROUP }, + { "acl_entry_threshold_type", CrmResourceType::CRM_ACL_ENTRY }, + { "acl_counter_threshold_type", CrmResourceType::CRM_ACL_COUNTER }, + { "fdb_entry_threshold_type", CrmResourceType::CRM_FDB_ENTRY } +}; + +const map crmThreshLowResMap = +{ + {"ipv4_route_low_threshold", CrmResourceType::CRM_IPV4_ROUTE }, + {"ipv6_route_low_threshold", CrmResourceType::CRM_IPV6_ROUTE }, + {"ipv4_nexthop_low_threshold", CrmResourceType::CRM_IPV4_NEXTHOP }, + {"ipv6_nexthop_low_threshold", CrmResourceType::CRM_IPV6_NEXTHOP }, + {"ipv4_neighbor_low_threshold", CrmResourceType::CRM_IPV4_NEIGHBOR }, + {"ipv6_neighbor_low_threshold", CrmResourceType::CRM_IPV6_NEIGHBOR }, + {"nexthop_group_member_low_threshold", CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER }, + {"nexthop_group_low_threshold", CrmResourceType::CRM_NEXTHOP_GROUP }, + {"acl_table_low_threshold", CrmResourceType::CRM_ACL_TABLE }, + {"acl_group_low_threshold", CrmResourceType::CRM_ACL_GROUP }, + {"acl_entry_low_threshold", CrmResourceType::CRM_ACL_ENTRY }, + {"acl_counter_low_threshold", CrmResourceType::CRM_ACL_COUNTER }, + {"fdb_entry_low_threshold", CrmResourceType::CRM_FDB_ENTRY }, +}; + +const map crmThreshHighResMap = +{ + {"ipv4_route_high_threshold", CrmResourceType::CRM_IPV4_ROUTE }, + {"ipv6_route_high_threshold", CrmResourceType::CRM_IPV6_ROUTE }, + {"ipv4_nexthop_high_threshold", CrmResourceType::CRM_IPV4_NEXTHOP }, + {"ipv6_nexthop_high_threshold", CrmResourceType::CRM_IPV6_NEXTHOP }, + {"ipv4_neighbor_high_threshold", CrmResourceType::CRM_IPV4_NEIGHBOR }, + {"ipv6_neighbor_high_threshold", CrmResourceType::CRM_IPV6_NEIGHBOR }, + {"nexthop_group_member_high_threshold", CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER }, + {"nexthop_group_high_threshold", CrmResourceType::CRM_NEXTHOP_GROUP }, + {"acl_table_high_threshold", CrmResourceType::CRM_ACL_TABLE }, + {"acl_group_high_threshold", CrmResourceType::CRM_ACL_GROUP }, + {"acl_entry_high_threshold", CrmResourceType::CRM_ACL_ENTRY }, + {"acl_counter_high_threshold", CrmResourceType::CRM_ACL_COUNTER }, + {"fdb_entry_high_threshold", CrmResourceType::CRM_FDB_ENTRY } +}; + +const map crmThreshTypeMap = +{ + { "percentage", CrmThresholdType::CRM_PERCENTAGE }, + { "used", CrmThresholdType::CRM_USED }, + { "free", CrmThresholdType::CRM_FREE } +}; + +const map crmAvailCntsTableMap = +{ + { "crm_stats_ipv4_route_available", CrmResourceType::CRM_IPV4_ROUTE }, + { "crm_stats_ipv6_route_available", CrmResourceType::CRM_IPV6_ROUTE }, + { "crm_stats_ipv4_nexthop_available", CrmResourceType::CRM_IPV4_NEXTHOP }, + { "crm_stats_ipv6_nexthop_available", CrmResourceType::CRM_IPV6_NEXTHOP }, + { "crm_stats_ipv4_neighbor_available", CrmResourceType::CRM_IPV4_NEIGHBOR }, + { "crm_stats_ipv6_neighbor_available", CrmResourceType::CRM_IPV6_NEIGHBOR }, + { "crm_stats_nexthop_group_member_available", CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER }, + { "crm_stats_nexthop_group_available", CrmResourceType::CRM_NEXTHOP_GROUP }, + { "crm_stats_acl_table_available", CrmResourceType::CRM_ACL_TABLE }, + { "crm_stats_acl_group_available", CrmResourceType::CRM_ACL_GROUP }, + { "crm_stats_acl_entry_available", CrmResourceType::CRM_ACL_ENTRY }, + { "crm_stats_acl_counter_available", CrmResourceType::CRM_ACL_COUNTER }, + { "crm_stats_fdb_entry_available", CrmResourceType::CRM_FDB_ENTRY } +}; + +const map crmUsedCntsTableMap = +{ + { "crm_stats_ipv4_route_used", CrmResourceType::CRM_IPV4_ROUTE }, + { "crm_stats_ipv6_route_used", CrmResourceType::CRM_IPV6_ROUTE }, + { "crm_stats_ipv4_nexthop_used", CrmResourceType::CRM_IPV4_NEXTHOP }, + { "crm_stats_ipv6_nexthop_used", CrmResourceType::CRM_IPV6_NEXTHOP }, + { "crm_stats_ipv4_neighbor_used", CrmResourceType::CRM_IPV4_NEIGHBOR }, + { "crm_stats_ipv6_neighbor_used", CrmResourceType::CRM_IPV6_NEIGHBOR }, + { "crm_stats_nexthop_group_member_used", CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER }, + { "crm_stats_nexthop_group_used", CrmResourceType::CRM_NEXTHOP_GROUP }, + { "crm_stats_acl_table_used", CrmResourceType::CRM_ACL_TABLE }, + { "crm_stats_acl_group_used", CrmResourceType::CRM_ACL_GROUP }, + { "crm_stats_acl_entry_used", CrmResourceType::CRM_ACL_ENTRY }, + { "crm_stats_acl_counter_used", CrmResourceType::CRM_ACL_COUNTER }, + { "crm_stats_fdb_entry_used", CrmResourceType::CRM_FDB_ENTRY } +}; + +CrmOrch::CrmOrch(DBConnector *db, string tableName): + Orch(db, tableName), + m_countersDb(new DBConnector(COUNTERS_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_countersCrmTable(new Table(m_countersDb.get(), COUNTERS_CRM_TABLE)), + m_timer(new SelectableTimer(timespec { .tv_sec = CRM_POLLING_INTERVAL_DEFAULT, .tv_nsec = 0 })) +{ + SWSS_LOG_ENTER(); + + m_pollingInterval = chrono::seconds(CRM_POLLING_INTERVAL_DEFAULT); + + for (const auto &res : crmResTypeNameMap) + { + m_resourcesMap.emplace(res.first, CrmResourceEntry(res.second, CRM_THRESHOLD_TYPE_DEFAULT, CRM_THRESHOLD_LOW_DEFAULT, CRM_THRESHOLD_HIGH_DEFAULT)); + } + + auto executor = new ExecutableTimer(m_timer.get(), this); + Orch::addExecutor("CRM_COUNTERS_POLL", executor); + m_timer->start(); +} + +CrmOrch::CrmResourceEntry::CrmResourceEntry(string name, CrmThresholdType thresholdType, uint32_t lowThreshold, uint32_t highThreshold): + name(name), + thresholdType(thresholdType), + lowThreshold(lowThreshold), + highThreshold(highThreshold) +{ + if ((thresholdType == CrmThresholdType::CRM_PERCENTAGE) && ((lowThreshold > 100) || (highThreshold > 100))) + { + throw runtime_error("CRM percentage threshold value must be <= 100%%"); + } + + if (!(lowThreshold < highThreshold)) + { + throw runtime_error("CRM low threshold must be less then high threshold"); + } + +} + +void CrmOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + string table_name = consumer.getTableName(); + + if (table_name != CFG_CRM_TABLE_NAME) + { + SWSS_LOG_ERROR("Invalid table %s", table_name.c_str()); + } + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string key = kfvKey(t); + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + handleSetCommand(key, kfvFieldsValues(t)); + } + else if (op == DEL_COMMAND) + { + SWSS_LOG_ERROR("Unsupported operation type %s\n", op.c_str()); + it = consumer.m_toSync.erase(it); + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s\n", op.c_str()); + } + + consumer.m_toSync.erase(it++); + } +} + +void CrmOrch::handleSetCommand(const string& key, const vector& data) +{ + SWSS_LOG_ENTER(); + + for (auto i : data) + { + const auto &field = fvField(i); + const auto &value = fvValue(i); + + try + { + if (field == CRM_POLLING_INTERVAL) + { + m_pollingInterval = chrono::seconds(to_uint(value)); + auto interv = timespec { .tv_sec = m_pollingInterval.count(), .tv_nsec = 0 }; + m_timer->setInterval(interv); + m_timer->reset(); + } + else if (crmThreshTypeResMap.find(field) != crmThreshTypeResMap.end()) + { + auto resourceType = crmThreshTypeResMap.at(field); + auto thresholdType = crmThreshTypeMap.at(value); + + m_resourcesMap.at(resourceType).thresholdType = thresholdType; + } + else if (crmThreshLowResMap.find(field) != crmThreshLowResMap.end()) + { + auto resourceType = crmThreshLowResMap.at(field); + auto thresholdValue = to_uint(value); + + m_resourcesMap.at(resourceType).lowThreshold = thresholdValue; + } + else if (crmThreshHighResMap.find(field) != crmThreshHighResMap.end()) + { + auto resourceType = crmThreshHighResMap.at(field); + auto thresholdValue = to_uint(value); + + m_resourcesMap.at(resourceType).highThreshold = thresholdValue; + } + else + { + SWSS_LOG_ERROR("Failed to parse CRM %s configuration. Unknown attribute %s.\n", key.c_str(), field.c_str()); + return; + } + } + catch (const exception& e) + { + SWSS_LOG_ERROR("Failed to parse CRM %s attribute %s error: %s.", key.c_str(), field.c_str(), e.what()); + return; + } + catch (...) + { + SWSS_LOG_ERROR("Failed to parse CRM %s attribute %s. Unknown error has been occurred", key.c_str(), field.c_str()); + return; + } + } +} + +void CrmOrch::incCrmResUsedCounter(CrmResourceType resource) +{ + SWSS_LOG_ENTER(); + + try + { + m_resourcesMap.at(resource).countersMap[CRM_COUNTERS_TABLE_KEY].usedCounter++; + } + catch (...) + { + SWSS_LOG_ERROR("Failed to increment \"used\" counter for the %s CRM resource.", crmResTypeNameMap.at(resource).c_str()); + return; + } +} + +void CrmOrch::decCrmResUsedCounter(CrmResourceType resource) +{ + SWSS_LOG_ENTER(); + + try + { + m_resourcesMap.at(resource).countersMap[CRM_COUNTERS_TABLE_KEY].usedCounter--; + } + catch (...) + { + SWSS_LOG_ERROR("Failed to decrement \"used\" counter for the %s CRM resource.", crmResTypeNameMap.at(resource).c_str()); + return; + } +} + +void CrmOrch::incCrmAclUsedCounter(CrmResourceType resource, sai_acl_stage_t stage, sai_acl_bind_point_type_t point) +{ + SWSS_LOG_ENTER(); + + try + { + m_resourcesMap.at(resource).countersMap[getCrmAclKey(stage, point)].usedCounter++; + } + catch (...) + { + SWSS_LOG_ERROR("Failed to increment \"used\" counter for the %s CRM resource.", crmResTypeNameMap.at(resource).c_str()); + return; + } +} + +void CrmOrch::decCrmAclUsedCounter(CrmResourceType resource, sai_acl_stage_t stage, sai_acl_bind_point_type_t point, sai_object_id_t oid) +{ + SWSS_LOG_ENTER(); + + try + { + m_resourcesMap.at(resource).countersMap[getCrmAclKey(stage, point)].usedCounter--; + + // Remove ACL table related counters + if (resource == CrmResourceType::CRM_ACL_TABLE) + { + auto & cntMap = m_resourcesMap.at(CrmResourceType::CRM_ACL_TABLE).countersMap; + for (auto it = cntMap.begin(); it != cntMap.end();) + { + if (it->second.id == oid) + { + it = cntMap.erase(it); + } + else + { + ++it; + } + } + } + } + catch (...) + { + SWSS_LOG_ERROR("Failed to decrement \"used\" counter for the %s CRM resource.", crmResTypeNameMap.at(resource).c_str()); + return; + } +} + +void CrmOrch::incCrmAclTableUsedCounter(CrmResourceType resource, sai_object_id_t tableId) +{ + SWSS_LOG_ENTER(); + + try + { + m_resourcesMap.at(resource).countersMap[getCrmAclTableKey(tableId)].usedCounter++; + m_resourcesMap.at(resource).countersMap[getCrmAclTableKey(tableId)].id = tableId; + } + catch (...) + { + SWSS_LOG_ERROR("Failed to increment \"used\" counter for the %s CRM resource (tableId:%lx).", crmResTypeNameMap.at(resource).c_str(), tableId); + return; + } +} + +void CrmOrch::decCrmAclTableUsedCounter(CrmResourceType resource, sai_object_id_t tableId) +{ + SWSS_LOG_ENTER(); + + try + { + m_resourcesMap.at(resource).countersMap[getCrmAclTableKey(tableId)].usedCounter--; + } + catch (...) + { + SWSS_LOG_ERROR("Failed to decrement \"used\" counter for the %s CRM resource (tableId:%lx).", crmResTypeNameMap.at(resource).c_str(), tableId); + return; + } +} + +void CrmOrch::doTask(SelectableTimer &timer) +{ + SWSS_LOG_ENTER(); + + getResAvailableCounters(); + updateCrmCountersTable(); + checkCrmThresholds(); + + auto interv = timespec { .tv_sec = m_pollingInterval.count(), .tv_nsec = 0 }; + timer.setInterval(interv); + timer.reset(); +} + +void CrmOrch::getResAvailableCounters() +{ + SWSS_LOG_ENTER(); + + for (auto &res : m_resourcesMap) + { + sai_attribute_t attr; + attr.id = crmResSaiAvailAttrMap.at(res.first); + + switch (attr.id) + { + case SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY: + case SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY: + case SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY: + case SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY: + case SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY: + case SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY: + case SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY: + case SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY: + case SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY: + { + sai_status_t status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get switch attribute %u , rv:%d", attr.id, status); + break; + } + + res.second.countersMap[CRM_COUNTERS_TABLE_KEY].availableCounter = attr.value.u32; + + break; + } + + case SAI_SWITCH_ATTR_AVAILABLE_ACL_TABLE: + case SAI_SWITCH_ATTR_AVAILABLE_ACL_TABLE_GROUP: + { + attr.value.aclresource.count = 0; + attr.value.aclresource.list = NULL; + + sai_status_t status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + if ((status != SAI_STATUS_SUCCESS) && (status != SAI_STATUS_BUFFER_OVERFLOW)) + { + SWSS_LOG_ERROR("Failed to get switch attribute %u , rv:%d", attr.id, status); + break; + } + + vector resources(attr.value.aclresource.count); + attr.value.aclresource.list = resources.data(); + + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get switch attribute %u , rv:%d", attr.id, status); + break; + } + + for (uint32_t i = 0; i < attr.value.aclresource.count; i++) + { + string key = getCrmAclKey(attr.value.aclresource.list[i].stage, attr.value.aclresource.list[i].bind_point); + res.second.countersMap[key].availableCounter = attr.value.aclresource.list[i].avail_num; + } + + break; + } + + case SAI_ACL_TABLE_ATTR_AVAILABLE_ACL_ENTRY: + case SAI_ACL_TABLE_ATTR_AVAILABLE_ACL_COUNTER: + { + for (auto &cnt : res.second.countersMap) + { + sai_status_t status = sai_acl_api->get_acl_table_attribute(cnt.second.id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get ACL table attribute %u , rv:%d", attr.id, status); + break; + } + + cnt.second.availableCounter = attr.value.u32; + } + + break; + } + + default: + SWSS_LOG_ERROR("Failed to get CRM attribute %u. Unknown attribute.\n", attr.id); + return; + } + } +} + +void CrmOrch::updateCrmCountersTable() +{ + SWSS_LOG_ENTER(); + + // Update CRM used counters in COUNTERS_DB + for (const auto &i : crmUsedCntsTableMap) + { + for (const auto &cnt : m_resourcesMap.at(i.second).countersMap) + { + FieldValueTuple attr(i.first, to_string(cnt.second.usedCounter)); + vector attrs = { attr }; + m_countersCrmTable->set(cnt.first, attrs); + } + } + + // Update CRM available counters in COUNTERS_DB + for (const auto &i : crmAvailCntsTableMap) + { + for (const auto &cnt : m_resourcesMap.at(i.second).countersMap) + { + FieldValueTuple attr(i.first, to_string(cnt.second.availableCounter)); + vector attrs = { attr }; + m_countersCrmTable->set(cnt.first, attrs); + } + } +} + +void CrmOrch::checkCrmThresholds() +{ + SWSS_LOG_ENTER(); + + for (auto &i : m_resourcesMap) + { + auto &res = i.second; + + for (const auto &j : i.second.countersMap) + { + auto &cnt = j.second; + uint64_t utilization = 0; + uint32_t percentageUtil = 0; + string threshType = ""; + + if (cnt.usedCounter != 0) + { + percentageUtil = (cnt.usedCounter * 100) / (cnt.usedCounter + cnt.availableCounter); + } + + switch (res.thresholdType) + { + case CrmThresholdType::CRM_PERCENTAGE: + utilization = percentageUtil; + threshType = "TH_PERCENTAGE"; + break; + case CrmThresholdType::CRM_USED: + utilization = cnt.usedCounter; + threshType = "TH_USED"; + break; + case CrmThresholdType::CRM_FREE: + utilization = cnt.availableCounter; + threshType = "TH_FREE"; + break; + default: + throw runtime_error("Unknown threshold type for CRM resource"); + } + + if ((utilization >= res.highThreshold) && (res.exceededLogCounter < CRM_EXCEEDED_MSG_MAX)) + { + SWSS_LOG_WARN("%s THRESHOLD_EXCEEDED for %s %u%% Used count %u free count %u", + res.name.c_str(), threshType.c_str(), percentageUtil, cnt.usedCounter, cnt.availableCounter); + + res.exceededLogCounter++; + } + else if ((utilization <= res.lowThreshold) && (res.exceededLogCounter > 0)) + { + SWSS_LOG_WARN("%s THRESHOLD_CLEAR for %s %u%% Used count %u free count %u", + res.name.c_str(), threshType.c_str(), percentageUtil, cnt.usedCounter, cnt.availableCounter); + + res.exceededLogCounter = 0; + } + } // end of counters loop + } // end of resources loop +} + + +string CrmOrch::getCrmAclKey(sai_acl_stage_t stage, sai_acl_bind_point_type_t bindPoint) +{ + string key = "ACL_STATS"; + + switch(stage) + { + case SAI_ACL_STAGE_INGRESS: + key += ":INGRESS"; + break; + case SAI_ACL_STAGE_EGRESS: + key += ":EGRESS"; + break; + default: + return ""; + } + + switch(bindPoint) + { + case SAI_ACL_BIND_POINT_TYPE_PORT: + key += ":PORT"; + break; + case SAI_ACL_BIND_POINT_TYPE_LAG: + key += ":LAG"; + break; + case SAI_ACL_BIND_POINT_TYPE_VLAN: + key += ":VLAN"; + break; + case SAI_ACL_BIND_POINT_TYPE_ROUTER_INTERFACE: + key += ":RIF"; + break; + case SAI_ACL_BIND_POINT_TYPE_SWITCH: + key += ":SWITCH"; + break; + default: + return ""; + } + + return key; +} + +string CrmOrch::getCrmAclTableKey(sai_object_id_t id) +{ + std::stringstream ss; + ss << "ACL_TABLE_STATS:" << "0x" << std::hex << id; + return ss.str(); +} diff --git a/orchagent/crmorch.h b/orchagent/crmorch.h new file mode 100644 index 0000000000..fd850349d5 --- /dev/null +++ b/orchagent/crmorch.h @@ -0,0 +1,91 @@ +#pragma once + +#include +#include +#include +#include "orch.h" +#include "port.h" + +extern "C" { +#include "sai.h" +} + +enum class CrmResourceType +{ + CRM_IPV4_ROUTE, + CRM_IPV6_ROUTE, + CRM_IPV4_NEXTHOP, + CRM_IPV6_NEXTHOP, + CRM_IPV4_NEIGHBOR, + CRM_IPV6_NEIGHBOR, + CRM_NEXTHOP_GROUP_MEMBER, + CRM_NEXTHOP_GROUP, + CRM_ACL_TABLE, + CRM_ACL_GROUP, + CRM_ACL_ENTRY, + CRM_ACL_COUNTER, + CRM_FDB_ENTRY, +}; + +enum class CrmThresholdType +{ + CRM_PERCENTAGE, + CRM_USED, + CRM_FREE, +}; + +class CrmOrch : public Orch +{ +public: + CrmOrch(DBConnector *db, string tableName); + void incCrmResUsedCounter(CrmResourceType resource); + void decCrmResUsedCounter(CrmResourceType resource); + // Increment "used" counter for the ACL table/group CRM resources + void incCrmAclUsedCounter(CrmResourceType resource, sai_acl_stage_t stage, sai_acl_bind_point_type_t point); + // Decrement "used" counter for the ACL table/group CRM resources + void decCrmAclUsedCounter(CrmResourceType resource, sai_acl_stage_t stage, sai_acl_bind_point_type_t point, sai_object_id_t oid); + // Increment "used" counter for the per ACL table CRM resources (ACL entry/counter) + void incCrmAclTableUsedCounter(CrmResourceType resource, sai_object_id_t tableId); + // Decrement "used" counter for the per ACL table CRM resources (ACL entry/counter) + void decCrmAclTableUsedCounter(CrmResourceType resource, sai_object_id_t tableId); + +private: + shared_ptr m_countersDb = nullptr; + shared_ptr
m_countersCrmTable = nullptr; + shared_ptr m_timer = nullptr; + + struct CrmResourceCounter + { + sai_object_id_t id = 0; + uint32_t availableCounter = 0; + uint32_t usedCounter = 0; + }; + + struct CrmResourceEntry + { + CrmResourceEntry(string name, CrmThresholdType thresholdType, uint32_t lowThreshold, uint32_t highThreshold); + + string name; + + CrmThresholdType thresholdType = CrmThresholdType::CRM_PERCENTAGE; + uint32_t lowThreshold = 70; + uint32_t highThreshold = 85; + + map countersMap; + + uint32_t exceededLogCounter = 0; + }; + + chrono::seconds m_pollingInterval; + + map m_resourcesMap; + + void doTask(Consumer &consumer); + void handleSetCommand(const string& key, const vector& data); + void doTask(SelectableTimer &timer); + void getResAvailableCounters(); + void updateCrmCountersTable(); + void checkCrmThresholds(); + string getCrmAclKey(sai_acl_stage_t stage, sai_acl_bind_point_type_t bindPoint); + string getCrmAclTableKey(sai_object_id_t id); +}; diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index bd40479d37..cffdbdb548 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -1,15 +1,40 @@ #include #include #include +#include +#include #include "logger.h" #include "tokenize.h" #include "fdborch.h" +#include "crmorch.h" +#include "notifier.h" +#include "sai_serialize.h" extern sai_fdb_api_t *sai_fdb_api; extern sai_object_id_t gSwitchId; extern PortsOrch* gPortsOrch; +extern CrmOrch * gCrmOrch; + +const int fdborch_pri = 20; + +FdbOrch::FdbOrch(DBConnector *db, string tableName, PortsOrch *port) : + Orch(db, tableName, fdborch_pri), + m_portsOrch(port), + m_table(Table(db, tableName)) +{ + m_portsOrch->attach(this); + m_flushNotificationsConsumer = new NotificationConsumer(db, "FLUSHFDBREQUEST"); + auto flushNotifier = new Notifier(m_flushNotificationsConsumer, this); + Orch::addExecutor("", flushNotifier); + + /* Add FDB notifications support from ASIC */ + DBConnector *notificationsDb = new DBConnector(ASIC_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + m_fdbNotificationConsumer = new swss::NotificationConsumer(notificationsDb, "NOTIFICATIONS"); + auto fdbNotifier = new Notifier(m_fdbNotificationConsumer, this); + Orch::addExecutor("FDB_NOTIFICATIONS", fdbNotifier); +} void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_object_id_t bridge_port_id) { @@ -17,45 +42,144 @@ void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_obj FdbUpdate update; update.entry.mac = entry->mac_address; - update.entry.vlan = entry->vlan_id; + update.entry.bv_id = entry->bv_id; switch (type) { case SAI_FDB_EVENT_LEARNED: if (!m_portsOrch->getPortByBridgePortId(bridge_port_id, update.port)) { - SWSS_LOG_ERROR("Failed to get port by bridge port ID %lu", bridge_port_id); + SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%lx", bridge_port_id); return; } update.add = true; - (void)m_entries.insert(update.entry); - SWSS_LOG_DEBUG("FdbOrch notification: mac %s was inserted into vlan %d", update.entry.mac.to_string().c_str(), entry->vlan_id); + { + auto ret = m_entries.insert(update.entry); + + SWSS_LOG_DEBUG("FdbOrch notification: mac %s was inserted into bv_id 0x%lx", + update.entry.mac.to_string().c_str(), entry->bv_id); + + if (ret.second) + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + } + else + { + SWSS_LOG_INFO("FdbOrch notification: mac %s is duplicate", update.entry.mac.to_string().c_str()); + } + } + + for (auto observer: m_observers) + { + observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + } + break; + case SAI_FDB_EVENT_AGED: - case SAI_FDB_EVENT_FLUSHED: case SAI_FDB_EVENT_MOVE: update.add = false; - (void)m_entries.erase(update.entry); - SWSS_LOG_DEBUG("FdbOrch notification: mac %s was removed from vlan %d", update.entry.mac.to_string().c_str(), entry->vlan_id); + { + auto ret = m_entries.erase(update.entry); + SWSS_LOG_DEBUG("FdbOrch notification: mac %s was removed from bv_id 0x%lx", update.entry.mac.to_string().c_str(), entry->bv_id); + + if (ret) + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + } + } + + for (auto observer: m_observers) + { + observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + } + + break; + + case SAI_FDB_EVENT_FLUSHED: + if (bridge_port_id == SAI_NULL_OBJECT_ID && entry->bv_id == SAI_NULL_OBJECT_ID) + { + 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->mac; + update.entry.bv_id = itr->bv_id; + update.add = false; + + itr = m_entries.erase(itr); + + SWSS_LOG_DEBUG("FdbOrch notification: mac %s was removed", update.entry.mac.to_string().c_str()); + + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + + for (auto observer: m_observers) + { + observer->update(SUBJECT_TYPE_FDB_CHANGE, &update); + } + } + } + else if (bridge_port_id && entry->bv_id == SAI_NULL_OBJECT_ID) + { + /*this is a placeholder for flush port fdb case, not supported yet.*/ + SWSS_LOG_ERROR("FdbOrch notification: not supported flush port fdb action, port_id = 0x%lx, bv_id = 0x%lx.", bridge_port_id, entry->bv_id); + } + else if (bridge_port_id == SAI_NULL_OBJECT_ID && entry->bv_id != SAI_NULL_OBJECT_ID) + { + /*this is a placeholder for flush vlan fdb case, not supported yet.*/ + SWSS_LOG_ERROR("FdbOrch notification: not supported flush vlan fdb action, port_id = 0x%lx, bv_id = 0x%lx.", bridge_port_id, entry->bv_id); + } + else + { + SWSS_LOG_ERROR("FdbOrch notification: not supported flush fdb action, port_id = 0x%lx, bv_id = 0x%lx.", bridge_port_id, entry->bv_id); + } break; } - for (auto observer: m_observers) - { - observer->update(SUBJECT_TYPE_FDB_CHANGE, static_cast(&update)); + return; +} + +void FdbOrch::update(SubjectType type, void *cntx) +{ + SWSS_LOG_ENTER(); + + assert(cntx); + + switch(type) { + case SUBJECT_TYPE_VLAN_MEMBER_CHANGE: + { + VlanMemberUpdate *update = reinterpret_cast(cntx); + updateVlanMember(*update); + break; + } + default: + break; } + + return; } bool FdbOrch::getPort(const MacAddress& mac, uint16_t vlan, Port& port) { SWSS_LOG_ENTER(); + if (!m_portsOrch->getVlanByVlanId(vlan, port)) + { + SWSS_LOG_ERROR("Failed to get vlan by vlan ID %d", vlan); + return false; + } + sai_fdb_entry_t entry; + entry.switch_id = gSwitchId; memcpy(entry.mac_address, mac.getMac(), sizeof(sai_mac_t)); - entry.vlan_id = vlan; + entry.bv_id = port.m_vlan_info.vlan_oid; sai_attribute_t attr; attr.id = SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID; @@ -70,7 +194,7 @@ bool FdbOrch::getPort(const MacAddress& mac, uint16_t vlan, Port& port) if (!m_portsOrch->getPortByBridgePortId(attr.value.oid, port)) { - SWSS_LOG_ERROR("Failed to get port by bridge port ID %lu", attr.value.oid); + SWSS_LOG_ERROR("Failed to get port by bridge port ID 0x%lx", attr.value.oid); return false; } @@ -105,7 +229,7 @@ void FdbOrch::doTask(Consumer& consumer) FdbEntry entry; entry.mac = MacAddress(keys[1]); - entry.vlan = vlan.m_vlan_info.vlan_id; + entry.bv_id = vlan.m_vlan_info.vlan_oid; if (op == SET_COMMAND) { @@ -157,6 +281,105 @@ void FdbOrch::doTask(Consumer& consumer) } } +void FdbOrch::doTask(NotificationConsumer& consumer) +{ + SWSS_LOG_ENTER(); + + if (!gPortsOrch->isInitDone()) + { + return; + } + + sai_status_t status; + std::string op; + std::string data; + std::vector values; + + consumer.pop(op, data, values); + + if (&consumer == m_flushNotificationsConsumer) + { + if (op == "ALL") + { + /* + * so far only support flush all the FDB entris + * flush per port and flush per vlan will be added later. + */ + status = sai_fdb_api->flush_fdb_entries(gSwitchId, 0, NULL); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Flush fdb failed, return code %x", status); + } + + return; + } + else if (op == "PORT") + { + /*place holder for flush port fdb*/ + SWSS_LOG_ERROR("Received unsupported flush port fdb request"); + return; + } + else if (op == "VLAN") + { + /*place holder for flush vlan fdb*/ + SWSS_LOG_ERROR("Received unsupported flush vlan fdb request"); + return; + } + else + { + SWSS_LOG_ERROR("Received unknown flush fdb request"); + return; + } + } + else if (&consumer == m_fdbNotificationConsumer && op == "fdb_event") + { + uint32_t count; + sai_fdb_event_notification_data_t *fdbevent = nullptr; + + sai_deserialize_fdb_event_ntf(data, count, &fdbevent); + + for (uint32_t i = 0; i < count; ++i) + { + sai_object_id_t oid = SAI_NULL_OBJECT_ID; + + for (uint32_t j = 0; j < fdbevent[i].attr_count; ++j) + { + if (fdbevent[i].attr[j].id == SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID) + { + oid = fdbevent[i].attr[j].value.oid; + break; + } + } + + this->update(fdbevent[i].event_type, &fdbevent[i].fdb_entry, oid); + + sai_deserialize_free_fdb_event_ntf(count, fdbevent); + } + } +} + +void FdbOrch::updateVlanMember(const VlanMemberUpdate& update) +{ + SWSS_LOG_ENTER(); + + if (!update.add) + { + return; // we need additions only + } + + string port_name = update.member.m_alias; + auto fdb_list = std::move(saved_fdb_entries[port_name]); + if(!fdb_list.empty()) + { + for (const auto& fdb: fdb_list) + { + // try to insert an FDB entry. If the FDB entry is not ready to be inserted yet, + // it would be added back to the saved_fdb_entries structure by addFDBEntry() + (void)addFdbEntry(fdb.entry, port_name, fdb.type); + } + } +} + bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const string& type) { SWSS_LOG_ENTER(); @@ -165,7 +388,7 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const { // FIXME: should we check that the entry are moving to another port? // FIXME: should we check that the entry are changing its type? - SWSS_LOG_ERROR("FDB entry already exists. mac=%s vlan=%d", entry.mac.to_string().c_str(), entry.vlan); + SWSS_LOG_ERROR("FDB entry already exists. mac=%s bv_id=0x%lx", entry.mac.to_string().c_str(), entry.bv_id); return true; } @@ -173,23 +396,25 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const fdb_entry.switch_id = gSwitchId; memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t)); - fdb_entry.bridge_type = SAI_FDB_ENTRY_BRIDGE_TYPE_1Q; - fdb_entry.vlan_id = entry.vlan; - fdb_entry.bridge_id = SAI_NULL_OBJECT_ID; + fdb_entry.bv_id = entry.bv_id; Port port; /* Retry until port is created */ if (!m_portsOrch->getPort(port_name, port)) { - SWSS_LOG_INFO("Failed to locate port %s", port_name.c_str()); - return false; + SWSS_LOG_DEBUG("Saving a fdb entry until port %s becomes active", port_name.c_str()); + saved_fdb_entries[port_name].push_back({entry, type}); + + return true; } /* Retry until port is added to the VLAN */ if (!port.m_bridge_port_id) { - SWSS_LOG_INFO("Port %s does not have a bridge port ID", port_name.c_str()); - return false; + SWSS_LOG_DEBUG("Saving a fdb entry until port %s has got a bridge port ID", port_name.c_str()); + saved_fdb_entries[port_name].push_back({entry, type}); + + return true; } sai_attribute_t attr; @@ -212,13 +437,15 @@ bool FdbOrch::addFdbEntry(const FdbEntry& entry, const string& port_name, const { SWSS_LOG_ERROR("Failed to create %s FDB %s on %s, rv:%d", type.c_str(), entry.mac.to_string().c_str(), port_name.c_str(), status); - return false; + return false; //FIXME: it should be based on status. Some could be retried, some not } SWSS_LOG_NOTICE("Create %s FDB %s on %s", type.c_str(), entry.mac.to_string().c_str(), port_name.c_str()); (void) m_entries.insert(entry); + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + return true; } @@ -228,24 +455,27 @@ bool FdbOrch::removeFdbEntry(const FdbEntry& entry) if (m_entries.count(entry) == 0) { - SWSS_LOG_ERROR("FDB entry isn't found. mac=%s vlan=%d", entry.mac.to_string().c_str(), entry.vlan); + SWSS_LOG_ERROR("FDB entry isn't found. mac=%s bv_id=0x%lx", entry.mac.to_string().c_str(), entry.bv_id); return true; } sai_status_t status; sai_fdb_entry_t fdb_entry; + fdb_entry.switch_id = gSwitchId; memcpy(fdb_entry.mac_address, entry.mac.getMac(), sizeof(sai_mac_t)); - fdb_entry.vlan_id = entry.vlan; + fdb_entry.bv_id = entry.bv_id; status = sai_fdb_api->remove_fdb_entry(&fdb_entry); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove FDB entry. mac=%s, vlan=%d", - entry.mac.to_string().c_str(), entry.vlan); - return true; + SWSS_LOG_ERROR("Failed to remove FDB entry. mac=%s, bv_id=0x%lx", + entry.mac.to_string().c_str(), entry.bv_id); + return true; //FIXME: it should be based on status. Some could be retried. some not } (void)m_entries.erase(entry); + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_FDB_ENTRY); + return true; } diff --git a/orchagent/fdborch.h b/orchagent/fdborch.h index 58b4cf07e7..4fd6b0d01e 100644 --- a/orchagent/fdborch.h +++ b/orchagent/fdborch.h @@ -8,11 +8,11 @@ struct FdbEntry { MacAddress mac; - sai_vlan_id_t vlan; + sai_object_id_t bv_id; bool operator<(const FdbEntry& other) const { - return tie(mac, vlan) < tie(other.mac, other.vlan); + return tie(mac, bv_id) < tie(other.mac, other.bv_id); } }; @@ -23,26 +23,41 @@ struct FdbUpdate bool add; }; -class FdbOrch: public Orch, public Subject +struct SavedFdbEntry +{ + FdbEntry entry; + string type; +}; + +typedef unordered_map> fdb_entries_by_port_t; + +class FdbOrch: public Orch, public Subject, public Observer { public: - FdbOrch(DBConnector *db, string tableName, PortsOrch *port) : - Orch(db, tableName), - m_portsOrch(port), - m_table(Table(db, tableName)) + + FdbOrch(DBConnector *db, string tableName, PortsOrch *port); + + ~FdbOrch() { + m_portsOrch->detach(this); } void update(sai_fdb_event_t, const sai_fdb_entry_t *, sai_object_id_t); + void update(SubjectType type, void *cntx); bool getPort(const MacAddress&, uint16_t, Port&); private: PortsOrch *m_portsOrch; set m_entries; + fdb_entries_by_port_t saved_fdb_entries; Table m_table; + NotificationConsumer* m_flushNotificationsConsumer; + NotificationConsumer* m_fdbNotificationConsumer; void doTask(Consumer& consumer); + void doTask(NotificationConsumer& consumer); + void updateVlanMember(const VlanMemberUpdate&); bool addFdbEntry(const FdbEntry&, const string&, const string&); bool removeFdbEntry(const FdbEntry&); }; diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index 3ca0e57fab..e97aed0192 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -9,17 +9,22 @@ #include "logger.h" #include "swssnet.h" #include "tokenize.h" +#include "crmorch.h" extern sai_object_id_t gVirtualRouterId; extern sai_router_interface_api_t* sai_router_intfs_api; extern sai_route_api_t* sai_route_api; +extern sai_neighbor_api_t* sai_neighbor_api; extern PortsOrch *gPortsOrch; extern sai_object_id_t gSwitchId; +extern CrmOrch *gCrmOrch; + +const int intfsorch_pri = 35; IntfsOrch::IntfsOrch(DBConnector *db, string tableName) : - Orch(db, tableName) + Orch(db, tableName, intfsorch_pri) { SWSS_LOG_ENTER(); } @@ -145,6 +150,10 @@ void IntfsOrch::doTask(Consumer &consumer) addSubnetRoute(port, ip_prefix); addIp2MeRoute(ip_prefix); + if(port.m_type == Port::VLAN && ip_prefix.isV4()) + { + addDirectedBroadcast(port, ip_prefix.getBroadcastIp()); + } m_syncdIntfses[alias].ip_addresses.insert(ip_prefix); it = consumer.m_toSync.erase(it); @@ -172,6 +181,10 @@ void IntfsOrch::doTask(Consumer &consumer) { removeSubnetRoute(port, ip_prefix); removeIp2MeRoute(ip_prefix); + if(port.m_type == Port::VLAN && ip_prefix.isV4()) + { + removeDirectedBroadcast(port, ip_prefix.getBroadcastIp()); + } m_syncdIntfses[alias].ip_addresses.erase(ip_prefix); } @@ -332,6 +345,15 @@ void IntfsOrch::addSubnetRoute(const Port &port, const IpPrefix &ip_prefix) SWSS_LOG_NOTICE("Create subnet route to %s from %s", ip_prefix.to_string().c_str(), port.m_alias.c_str()); increaseRouterIntfsRefCount(port.m_alias); + + if (unicast_route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); + } + else + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + } } void IntfsOrch::removeSubnetRoute(const Port &port, const IpPrefix &ip_prefix) @@ -353,6 +375,15 @@ void IntfsOrch::removeSubnetRoute(const Port &port, const IpPrefix &ip_prefix) SWSS_LOG_NOTICE("Remove subnet route to %s from %s", ip_prefix.to_string().c_str(), port.m_alias.c_str()); decreaseRouterIntfsRefCount(port.m_alias); + + if (unicast_route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); + } + else + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + } } void IntfsOrch::addIp2MeRoute(const IpPrefix &ip_prefix) @@ -384,6 +415,15 @@ void IntfsOrch::addIp2MeRoute(const IpPrefix &ip_prefix) } SWSS_LOG_NOTICE("Create IP2me route ip:%s", ip_prefix.getIp().to_string().c_str()); + + if (unicast_route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); + } + else + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + } } void IntfsOrch::removeIp2MeRoute(const IpPrefix &ip_prefix) @@ -401,4 +441,62 @@ void IntfsOrch::removeIp2MeRoute(const IpPrefix &ip_prefix) } SWSS_LOG_NOTICE("Remove packet action trap route ip:%s", ip_prefix.getIp().to_string().c_str()); + + if (unicast_route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); + } + else + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + } +} + +void IntfsOrch::addDirectedBroadcast(const Port &port, const IpAddress &ip_addr) +{ + sai_status_t status; + sai_neighbor_entry_t neighbor_entry; + neighbor_entry.rif_id = port.m_rif_id; + neighbor_entry.switch_id = gSwitchId; + copy(neighbor_entry.ip_address, ip_addr); + + sai_attribute_t neighbor_attr; + neighbor_attr.id = SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS; + memcpy(neighbor_attr.value.mac, MacAddress("ff:ff:ff:ff:ff:ff").getMac(), 6); + + status = sai_neighbor_api->create_neighbor_entry(&neighbor_entry, 1, &neighbor_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create broadcast entry %s rv:%d", + ip_addr.to_string().c_str(), status); + return; + } + + SWSS_LOG_NOTICE("Add broadcast route for ip:%s", ip_addr.to_string().c_str()); +} + +void IntfsOrch::removeDirectedBroadcast(const Port &port, const IpAddress &ip_addr) +{ + sai_status_t status; + sai_neighbor_entry_t neighbor_entry; + neighbor_entry.rif_id = port.m_rif_id; + neighbor_entry.switch_id = gSwitchId; + copy(neighbor_entry.ip_address, ip_addr); + + status = sai_neighbor_api->remove_neighbor_entry(&neighbor_entry); + if (status != SAI_STATUS_SUCCESS) + { + if (status == SAI_STATUS_ITEM_NOT_FOUND) + { + SWSS_LOG_ERROR("No broadcast entry found for %s", ip_addr.to_string().c_str()); + } + else + { + SWSS_LOG_ERROR("Failed to remove broadcast entry %s rv:%d", + ip_addr.to_string().c_str(), status); + } + return; + } + + SWSS_LOG_NOTICE("Remove broadcast route ip:%s", ip_addr.to_string().c_str()); } diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index d35ac28ddd..0c08350d33 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -45,6 +45,9 @@ class IntfsOrch : public Orch void addIp2MeRoute(const IpPrefix &ip_prefix); void removeIp2MeRoute(const IpPrefix &ip_prefix); + + void addDirectedBroadcast(const Port &port, const IpAddress &ip_addr); + void removeDirectedBroadcast(const Port &port, const IpAddress &ip_addr); }; #endif /* SWSS_INTFSORCH_H */ diff --git a/orchagent/main.cpp b/orchagent/main.cpp index 7f252b7ee6..f88ad3589b 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -5,8 +5,8 @@ extern "C" { #include #include +#include #include -#include #include #include #include @@ -46,9 +46,6 @@ bool gLogRotate = false; ofstream gRecordOfs; string gRecordFile; -/* Global database mutex */ -mutex gDbMutex; - void usage() { cout << "usage: orchagent [-h] [-r record_type] [-d record_location] [-b batch_size] [-m MAC]" << endl; diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index e6c26f10bd..bfd53636fc 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -1,5 +1,6 @@ #include +#include #include #include @@ -353,6 +354,7 @@ bool MirrorOrch::getNeighborInfo(const string& name, MirrorEntry& session, const if (session.neighborInfo.port.m_type == Port::VLAN) { session.neighborInfo.vlanId = session.neighborInfo.port.m_vlan_info.vlan_id; + session.neighborInfo.vlanOid = session.neighborInfo.port.m_vlan_info.vlan_oid; Port member; if (!m_fdbOrch->getPort(session.neighborInfo.mac, session.neighborInfo.vlanId, member)) @@ -715,7 +717,7 @@ void MirrorOrch::updateFdb(const FdbUpdate& update) // It is possible to have few session that points to one FDB entry if (sessionIter->second.neighborInfo.mac != update.entry.mac || - sessionIter->second.neighborInfo.vlanId != update.entry.vlan) + sessionIter->second.neighborInfo.vlanOid != update.entry.bv_id) { continue; } @@ -849,7 +851,8 @@ void MirrorOrch::updateVlanMember(const VlanMemberUpdate& update) } // It is possible to have few session that points to one VLAN member - if (sessionIter->second.neighborInfo.port != update.vlan || sessionIter->second.neighborInfo.portId != update.member.m_port_id) + if (sessionIter->second.neighborInfo.port != update.vlan || + sessionIter->second.neighborInfo.portId != update.member.m_port_id) { continue; } diff --git a/orchagent/mirrororch.h b/orchagent/mirrororch.h index 3bdc9d9a60..8bfaebd136 100644 --- a/orchagent/mirrororch.h +++ b/orchagent/mirrororch.h @@ -46,6 +46,7 @@ struct MirrorEntry MacAddress mac; Port port; sai_vlan_id_t vlanId; + sai_object_id_t vlanOid; sai_object_id_t portId; } neighborInfo; diff --git a/orchagent/neighorch.cpp b/orchagent/neighorch.cpp index 4aeee9f281..585f91b967 100644 --- a/orchagent/neighorch.cpp +++ b/orchagent/neighorch.cpp @@ -2,15 +2,21 @@ #include "neighorch.h" #include "logger.h" #include "swssnet.h" +#include "crmorch.h" +#include "routeorch.h" extern sai_neighbor_api_t* sai_neighbor_api; extern sai_next_hop_api_t* sai_next_hop_api; extern PortsOrch *gPortsOrch; extern sai_object_id_t gSwitchId; +extern CrmOrch *gCrmOrch; +extern RouteOrch *gRouteOrch; + +const int neighorch_pri = 30; NeighOrch::NeighOrch(DBConnector *db, string tableName, IntfsOrch *intfsOrch) : - Orch(db, tableName), m_intfsOrch(intfsOrch) + Orch(db, tableName, neighorch_pri), m_intfsOrch(intfsOrch) { SWSS_LOG_ENTER(); } @@ -57,13 +63,132 @@ bool NeighOrch::addNextHop(IpAddress ipAddress, string alias) NextHopEntry next_hop_entry; next_hop_entry.next_hop_id = next_hop_id; next_hop_entry.ref_count = 0; + next_hop_entry.nh_flags = 0; + next_hop_entry.if_alias = alias; m_syncdNextHops[ipAddress] = next_hop_entry; m_intfsOrch->increaseRouterIntfsRefCount(alias); + if (ipAddress.isV4()) + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_NEXTHOP); + } + else + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_NEXTHOP); + } + return true; } +bool NeighOrch::setNextHopFlag(const IpAddress &ipaddr, const uint32_t nh_flag) +{ + SWSS_LOG_ENTER(); + + auto nhop = m_syncdNextHops.find(ipaddr); + bool rc = false; + + assert(nhop != m_syncdNextHops.end()); + + if (nhop->second.nh_flags & nh_flag) + { + return true; + } + + nhop->second.nh_flags |= nh_flag; + + switch (nh_flag) + { + case NHFLAGS_IFDOWN: + rc = gRouteOrch->invalidnexthopinNextHopGroup(ipaddr); + break; + default: + assert(0); + break; + } + + return rc; +} + +bool NeighOrch::clearNextHopFlag(const IpAddress &ipaddr, const uint32_t nh_flag) +{ + SWSS_LOG_ENTER(); + + auto nhop = m_syncdNextHops.find(ipaddr); + bool rc = false; + + assert(nhop != m_syncdNextHops.end()); + + if (!(nhop->second.nh_flags & nh_flag)) + { + return true; + } + + nhop->second.nh_flags &= ~nh_flag; + + switch (nh_flag) + { + case NHFLAGS_IFDOWN: + rc = gRouteOrch->validnexthopinNextHopGroup(ipaddr); + break; + default: + assert(0); + break; + } + + return rc; +} + +bool NeighOrch::isNextHopFlagSet(const IpAddress &ipaddr, const uint32_t nh_flag) +{ + SWSS_LOG_ENTER(); + + auto nhop = m_syncdNextHops.find(ipaddr); + + assert(nhop != m_syncdNextHops.end()); + + if (nhop->second.nh_flags & nh_flag) + { + return true; + } + + return false; +} + +bool NeighOrch::ifChangeInformNextHop(const string &alias, bool if_up) +{ + SWSS_LOG_ENTER(); + bool rc = true; + + for (auto nhop = m_syncdNextHops.begin(); nhop != m_syncdNextHops.end(); ++nhop) + { + if (nhop->second.if_alias != alias) + { + continue; + } + + if (if_up) + { + rc = clearNextHopFlag(nhop->first, NHFLAGS_IFDOWN); + } + else + { + rc = setNextHopFlag(nhop->first, NHFLAGS_IFDOWN); + } + + if (rc == true) + { + continue; + } + else + { + break; + } + } + + return rc; +} + bool NeighOrch::removeNextHop(IpAddress ipAddress, string alias) { SWSS_LOG_ENTER(); @@ -252,6 +377,15 @@ bool NeighOrch::addNeighbor(NeighborEntry neighborEntry, MacAddress macAddress) SWSS_LOG_NOTICE("Created neighbor %s on %s", macAddress.to_string().c_str(), alias.c_str()); m_intfsOrch->increaseRouterIntfsRefCount(alias); + if (neighbor_entry.ip_address.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_NEIGHBOR); + } + else + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_NEIGHBOR); + } + if (!addNextHop(ip_address, alias)) { status = sai_neighbor_api->remove_neighbor_entry(&neighbor_entry); @@ -262,6 +396,16 @@ bool NeighOrch::addNeighbor(NeighborEntry neighborEntry, MacAddress macAddress) return false; } m_intfsOrch->decreaseRouterIntfsRefCount(alias); + + if (neighbor_entry.ip_address.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_NEIGHBOR); + } + else + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_NEIGHBOR); + } + return false; } } @@ -331,6 +475,18 @@ bool NeighOrch::removeNeighbor(NeighborEntry neighborEntry) SWSS_LOG_NOTICE("Removed next hop %s on %s", ip_address.to_string().c_str(), alias.c_str()); + if (status != SAI_STATUS_ITEM_NOT_FOUND) + { + if (neighbor_entry.ip_address.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_NEXTHOP); + } + else + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_NEXTHOP); + } + } + status = sai_neighbor_api->remove_neighbor_entry(&neighbor_entry); if (status != SAI_STATUS_SUCCESS) { @@ -351,6 +507,15 @@ bool NeighOrch::removeNeighbor(NeighborEntry neighborEntry) SWSS_LOG_NOTICE("Removed neighbor %s on %s", m_syncdNeighbors[neighborEntry].to_string().c_str(), alias.c_str()); + if (neighbor_entry.ip_address.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_NEIGHBOR); + } + else + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_NEIGHBOR); + } + NeighborUpdate update = { neighborEntry, MacAddress(), false }; notify(SUBJECT_TYPE_NEIGH_CHANGE, static_cast(&update)); diff --git a/orchagent/neighorch.h b/orchagent/neighorch.h index 47cbd1f59f..71257d1415 100644 --- a/orchagent/neighorch.h +++ b/orchagent/neighorch.h @@ -8,6 +8,8 @@ #include "ipaddress.h" +#define NHFLAGS_IFDOWN 0x1 // nexthop's outbound i/f is down + struct NeighborEntry { IpAddress ip_address; // neighbor IP address @@ -33,6 +35,8 @@ struct NextHopEntry { sai_object_id_t next_hop_id; // next hop id int ref_count; // reference count + uint32_t nh_flags; // flags + string if_alias; // i/f name alias }; /* NeighborTable: NeighborEntry, neighbor MAC address */ @@ -62,6 +66,9 @@ class NeighOrch : public Orch, public Subject bool getNeighborEntry(const IpAddress&, NeighborEntry&, MacAddress&); + bool ifChangeInformNextHop(const string &, bool); + bool isNextHopFlagSet(const IpAddress &, const uint32_t); + private: IntfsOrch *m_intfsOrch; @@ -74,6 +81,9 @@ class NeighOrch : public Orch, public Subject bool addNeighbor(NeighborEntry, MacAddress); bool removeNeighbor(NeighborEntry); + bool setNextHopFlag(const IpAddress &, const uint32_t); + bool clearNextHopFlag(const IpAddress &, const uint32_t); + void doTask(Consumer &consumer); }; diff --git a/orchagent/notifications.cpp b/orchagent/notifications.cpp index d6df8b63cb..209c03d83b 100644 --- a/orchagent/notifications.cpp +++ b/orchagent/notifications.cpp @@ -1,9 +1,3 @@ -#include -#include - -#include "portsorch.h" -#include "fdborch.h" - extern "C" { #include "sai.h" } @@ -11,61 +5,16 @@ extern "C" { #include "logger.h" #include "notifications.h" -extern mutex gDbMutex; -extern PortsOrch *gPortsOrch; -extern FdbOrch *gFdbOrch; - void on_fdb_event(uint32_t count, sai_fdb_event_notification_data_t *data) { - SWSS_LOG_ENTER(); - - lock_guard lock(gDbMutex); - - if (!gFdbOrch) - { - SWSS_LOG_NOTICE("gFdbOrch is not initialized"); - return; - } - - for (uint32_t i = 0; i < count; ++i) - { - sai_object_id_t oid = SAI_NULL_OBJECT_ID; - - for (uint32_t j = 0; j < data[i].attr_count; ++j) - { - if (data[i].attr[j].id == SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID) - { - oid = data[i].attr[j].value.oid; - break; - } - } - - gFdbOrch->update(data[i].event_type, &data[i].fdb_entry, oid); - } + // don't use this event handler, because it runs by libsairedis in a separate thread + // which causes concurrency access to the DB } void on_port_state_change(uint32_t count, sai_port_oper_status_notification_t *data) { - SWSS_LOG_ENTER(); - - lock_guard lock(gDbMutex); - - if (!gPortsOrch) - { - SWSS_LOG_NOTICE("gPortsOrch is not initialized"); - return; - } - - for (uint32_t i = 0; i < count; i++) - { - sai_object_id_t id = data[i].port_id; - sai_port_oper_status_t status = data[i].port_state; - - SWSS_LOG_NOTICE("Get port state change notification id:%lx status:%d", id, status); - - gPortsOrch->updateDbPortOperStatus(id, status); - gPortsOrch->setHostIntfsOperStatus(id, status == SAI_PORT_OPER_STATUS_UP); - } + // don't use this event handler, because it runs by libsairedis in a separate thread + // which causes concurrency access to the DB } void on_switch_shutdown_request() diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index 1f7e81a613..ce6258e543 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include "timestamp.h" #include "orch.h" @@ -15,23 +14,29 @@ using namespace swss; extern int gBatchSize; -extern mutex gDbMutex; - extern bool gSwssRecord; extern ofstream gRecordOfs; extern bool gLogRotate; extern string gRecordFile; -Orch::Orch(DBConnector *db, const string tableName) +Orch::Orch(DBConnector *db, const string tableName, int pri) { - addConsumer(db, tableName); + addConsumer(db, tableName, pri); } Orch::Orch(DBConnector *db, const vector &tableNames) { for(auto it : tableNames) { - addConsumer(db, it); + addConsumer(db, it, default_orch_pri); + } +} + +Orch::Orch(DBConnector *db, const vector &tableNames_with_pri) +{ + for(const auto& it : tableNames_with_pri) + { + addConsumer(db, it.first, it.second); } } @@ -65,9 +70,6 @@ void Consumer::execute() { SWSS_LOG_ENTER(); - // TODO: remove DbMutex when there is only single thread - lock_guard lock(gDbMutex); - std::deque entries; getConsumerTable()->pops(entries); @@ -356,15 +358,15 @@ bool Orch::parseIndexRange(const string &input, sai_uint32_t &range_low, sai_uin return true; } -void Orch::addConsumer(DBConnector *db, string tableName) +void Orch::addConsumer(DBConnector *db, string tableName, int pri) { - if (db->getDB() == CONFIG_DB) + if (db->getDbId() == CONFIG_DB) { - addExecutor(tableName, new Consumer(new SubscriberStateTable(db, tableName), this)); + addExecutor(tableName, new Consumer(new SubscriberStateTable(db, tableName, TableConsumable::DEFAULT_POP_BATCH_SIZE, pri), this)); } else { - addExecutor(tableName, new Consumer(new ConsumerStateTable(db, tableName, gBatchSize), this)); + addExecutor(tableName, new Consumer(new ConsumerStateTable(db, tableName, gBatchSize, pri), this)); } } @@ -374,3 +376,70 @@ void Orch::addExecutor(string executorName, Executor* executor) std::forward_as_tuple(executorName), std::forward_as_tuple(executor)); } + +Executor *Orch::getExecutor(string executorName) +{ + auto it = m_consumerMap.find(executorName); + if (it != m_consumerMap.end()) + { + return it->second.get(); + } + + return NULL; +} + +void Orch2::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + bool erase_from_queue = true; + try + { + request_.parse(it->second); + + auto op = request_.getOperation(); + if (op == SET_COMMAND) + { + erase_from_queue = addOperation(request_); + } + else if (op == DEL_COMMAND) + { + erase_from_queue = delOperation(request_); + } + else + { + SWSS_LOG_ERROR("Wrong operation. Check RequestParser: %s", op.c_str()); + } + } + catch (const std::invalid_argument& e) + { + SWSS_LOG_ERROR("Parse error: %s", e.what()); + } + catch (const std::logic_error& e) + { + SWSS_LOG_ERROR("Logic error: %s", e.what()); + } + catch (const std::exception& e) + { + SWSS_LOG_ERROR("Exception was catched in the request parser: %s", e.what()); + } + catch (...) + { + SWSS_LOG_ERROR("Unknown exception was catched in the request parser"); + } + request_.clear(); + + if (erase_from_queue) + { + it = consumer.m_toSync.erase(it); + } + else + { + ++it; + } + } +} + diff --git a/orchagent/orch.h b/orchagent/orch.h index b6ab60d8d5..cd724ce1c1 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -1,8 +1,11 @@ #ifndef SWSS_ORCH_H #define SWSS_ORCH_H +#include +#include #include #include +#include extern "C" { #include "sai.h" @@ -15,6 +18,7 @@ extern "C" { #include "consumerstatetable.h" #include "notificationconsumer.h" #include "selectabletimer.h" +#include "macaddress.h" using namespace std; using namespace swss; @@ -34,6 +38,8 @@ const char config_db_key_delimiter = '|'; #define CONFIGDB_KEY_SEPARATOR "|" #define DEFAULT_KEY_SEPARATOR ":" +const int default_orch_pri = 0; + typedef enum { task_success, @@ -50,6 +56,8 @@ typedef map type_map; typedef pair type_map_pair; typedef map SyncMap; +typedef pair table_name_with_pri_t; + class Orch; // Design assumption @@ -68,10 +76,11 @@ class Executor : public Selectable virtual ~Executor() { delete m_selectable; } // Decorating Selectable - virtual void addFd(fd_set *fd) { return m_selectable->addFd(fd); } - virtual bool isMe(fd_set *fd) { return m_selectable->isMe(fd); } - virtual int readCache() { return m_selectable->readCache(); } - virtual void readMe() { return m_selectable->readMe(); } + int getFd() override { return m_selectable->getFd(); } + void readData() override { m_selectable->readData(); } + bool hasCachedData() override { return m_selectable->hasCachedData(); } + bool initializedWithData() override { return m_selectable->initializedWithData(); } + void updateAfterRead() override { m_selectable->updateAfterRead(); } // Disable copying Executor(const Executor&) = delete; @@ -131,8 +140,9 @@ typedef pair> TablesConnector; class Orch { public: - Orch(DBConnector *db, const string tableName); + Orch(DBConnector *db, const string tableName, int pri = default_orch_pri); Orch(DBConnector *db, const vector &tableNames); + Orch(DBConnector *db, const vector &tableNameWithPri); Orch(const vector& tables); virtual ~Orch(); @@ -160,8 +170,29 @@ class Orch /* Note: consumer will be owned by this class */ void addExecutor(string executorName, Executor* executor); + Executor *getExecutor(string executorName); +private: + void addConsumer(DBConnector *db, string tableName, int pri = default_orch_pri); +}; + +#include "request_parser.h" + +class Orch2 : public Orch +{ +public: + Orch2(DBConnector *db, const std::string& tableName, Request& request, int pri=default_orch_pri) + : Orch(db, tableName, pri), request_(request) + { + } + +protected: + virtual void doTask(Consumer& consumer); + + virtual bool addOperation(const Request& request)=0; + virtual bool delOperation(const Request& request)=0; + private: - void addConsumer(DBConnector *db, string tableName); + Request& request_; }; #endif /* SWSS_ORCH_H */ diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 9dfae2556a..849083d1c9 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -1,4 +1,5 @@ #include +#include #include "orchdaemon.h" #include "logger.h" #include @@ -11,22 +12,24 @@ using namespace swss; /* select() function timeout retry time */ #define SELECT_TIMEOUT 1000 -#define FLEX_COUNTER_POLL_MSECS 100 +#define PFC_WD_POLL_MSECS 100 extern sai_switch_api_t* sai_switch_api; extern sai_object_id_t gSwitchId; -/* Global variable gPortsOrch declared */ +/* + * Global orch daemon variables + */ PortsOrch *gPortsOrch; -/* Global variable gFdbOrch declared */ FdbOrch *gFdbOrch; -/*Global variable gAclOrch declared*/ +NeighOrch *gNeighOrch; +RouteOrch *gRouteOrch; AclOrch *gAclOrch; +CrmOrch *gCrmOrch; OrchDaemon::OrchDaemon(DBConnector *applDb, DBConnector *configDb) : m_applDb(applDb), m_configDb(configDb) - { SWSS_LOG_ENTER(); } @@ -49,19 +52,22 @@ bool OrchDaemon::init() SwitchOrch *switch_orch = new SwitchOrch(m_applDb, APP_SWITCH_TABLE_NAME); - vector ports_tables = { - APP_PORT_TABLE_NAME, - APP_VLAN_TABLE_NAME, - APP_VLAN_MEMBER_TABLE_NAME, - APP_LAG_TABLE_NAME, - APP_LAG_MEMBER_TABLE_NAME + 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 + 2 }, + { APP_LAG_MEMBER_TABLE_NAME, portsorch_base_pri } }; + gCrmOrch = new CrmOrch(m_configDb, CFG_CRM_TABLE_NAME); gPortsOrch = new PortsOrch(m_applDb, ports_tables); gFdbOrch = new FdbOrch(m_applDb, APP_FDB_TABLE_NAME, gPortsOrch); IntfsOrch *intfs_orch = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME); - NeighOrch *neigh_orch = new NeighOrch(m_applDb, APP_NEIGH_TABLE_NAME, intfs_orch); - RouteOrch *route_orch = new RouteOrch(m_applDb, APP_ROUTE_TABLE_NAME, neigh_orch); + gNeighOrch = new NeighOrch(m_applDb, APP_NEIGH_TABLE_NAME, intfs_orch); + gRouteOrch = new RouteOrch(m_applDb, APP_ROUTE_TABLE_NAME, gNeighOrch); CoppOrch *copp_orch = new CoppOrch(m_applDb, APP_COPP_TABLE_NAME); TunnelDecapOrch *tunnel_decap_orch = new TunnelDecapOrch(m_applDb, APP_TUNNEL_DECAP_TABLE_NAME); @@ -79,30 +85,31 @@ bool OrchDaemon::init() QosOrch *qos_orch = new QosOrch(m_configDb, qos_tables); 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 + CFG_BUFFER_POOL_TABLE_NAME, + CFG_BUFFER_PROFILE_TABLE_NAME, + CFG_BUFFER_QUEUE_TABLE_NAME, + CFG_BUFFER_PG_TABLE_NAME, + CFG_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, + CFG_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; - BufferOrch *buffer_orch = new BufferOrch(m_applDb, buffer_tables); + BufferOrch *buffer_orch = new BufferOrch(m_configDb, buffer_tables); TableConnector appDbMirrorSession(m_applDb, APP_MIRROR_SESSION_TABLE_NAME); TableConnector confDbMirrorSession(m_configDb, CFG_MIRROR_SESSION_TABLE_NAME); - MirrorOrch *mirror_orch = new MirrorOrch(appDbMirrorSession, confDbMirrorSession, gPortsOrch, route_orch, neigh_orch, gFdbOrch); + MirrorOrch *mirror_orch = new MirrorOrch(appDbMirrorSession, confDbMirrorSession, gPortsOrch, gRouteOrch, gNeighOrch, gFdbOrch); + VRFOrch *vrf_orch = new VRFOrch(m_configDb, CFG_VRF_TABLE_NAME); vector acl_tables = { CFG_ACL_TABLE_NAME, CFG_ACL_RULE_TABLE_NAME }; - gAclOrch = new AclOrch(m_configDb, acl_tables, gPortsOrch, mirror_orch, neigh_orch, route_orch); + gAclOrch = new AclOrch(m_configDb, acl_tables, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch); - m_orchList = { switch_orch, gPortsOrch, intfs_orch, neigh_orch, route_orch, copp_orch, tunnel_decap_orch, qos_orch, buffer_orch, mirror_orch, gAclOrch, gFdbOrch}; + m_orchList = { switch_orch, /*gCrmOrch,*/ gPortsOrch, intfs_orch, gNeighOrch, gRouteOrch, copp_orch, tunnel_decap_orch, qos_orch, buffer_orch, mirror_orch, gAclOrch, gFdbOrch, vrf_orch }; m_select = new Select(); vector pfc_wd_tables = { - APP_PFC_WD_TABLE_NAME + CFG_PFC_WD_TABLE_NAME }; if (platform == MLNX_PLATFORM_SUBSTRING) @@ -142,7 +149,7 @@ bool OrchDaemon::init() portStatIds, queueStatIds, queueAttrIds, - FLEX_COUNTER_POLL_MSECS)); + PFC_WD_POLL_MSECS)); } else if (platform == BRCM_PLATFORM_SUBSTRING) { @@ -183,9 +190,11 @@ bool OrchDaemon::init() portStatIds, queueStatIds, queueAttrIds, - FLEX_COUNTER_POLL_MSECS)); + PFC_WD_POLL_MSECS)); } + m_orchList.push_back(&CounterCheckOrch::getInstance(m_configDb)); + return true; } @@ -216,9 +225,9 @@ void OrchDaemon::start() while (true) { Selectable *s; - int fd, ret; + int ret; - ret = m_select->select(&s, &fd, SELECT_TIMEOUT); + ret = m_select->select(&s, SELECT_TIMEOUT); if (ret == Select::ERROR) { diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 4ea5099846..c7a96a2b0b 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -19,6 +19,9 @@ #include "aclorch.h" #include "pfcwdorch.h" #include "switchorch.h" +#include "crmorch.h" +#include "vrforch.h" +#include "countercheckorch.h" using namespace swss; diff --git a/orchagent/pfc_detect_broadcom.lua b/orchagent/pfc_detect_broadcom.lua index 892be8b5fc..f75bdc6ee0 100644 --- a/orchagent/pfc_detect_broadcom.lua +++ b/orchagent/pfc_detect_broadcom.lua @@ -21,70 +21,80 @@ for i = n, 1, -1 do local is_deadlock = false local pfc_wd_status = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_STATUS') local pfc_wd_action = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_ACTION') - if pfc_wd_status == 'operational' or pfc_wd_action == 'alert' then - local detection_time = tonumber(redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME')) - local time_left = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT') - if not time_left then - time_left = detection_time - else - time_left = tonumber(time_left) - end + local big_red_switch_mode = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'BIG_RED_SWITCH_MODE') + if not big_red_switch_mode and (pfc_wd_status == 'operational' or pfc_wd_action == 'alert') then + local detection_time = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME') + if detection_time then + detection_time = tonumber(detection_time) + local time_left = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT') + if not time_left then + time_left = detection_time + else + time_left = tonumber(time_left) + end - local queue_index = redis.call('HGET', 'COUNTERS_QUEUE_INDEX_MAP', KEYS[i]) - local port_id = redis.call('HGET', 'COUNTERS_QUEUE_PORT_MAP', KEYS[i]) - local pfc_rx_pkt_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PKTS' - local pfc_on2off_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_ON2OFF_RX_PKTS' - + local queue_index = redis.call('HGET', 'COUNTERS_QUEUE_INDEX_MAP', KEYS[i]) + local port_id = redis.call('HGET', 'COUNTERS_QUEUE_PORT_MAP', KEYS[i]) + local pfc_rx_pkt_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PKTS' + local pfc_on2off_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_ON2OFF_RX_PKTS' - -- Get all counters - local occupancy_bytes = tonumber(redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_CURR_OCCUPANCY_BYTES')) - local packets = tonumber(redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS')) - local pfc_rx_packets = tonumber(redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key)) - local pfc_on2off = tonumber(redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_on2off_key)) - local queue_pause_status = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_ATTR_PAUSE_STATUS') - - local packets_last = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last') - local pfc_rx_packets_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last') - local pfc_on2off_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_on2off_key .. '_last') - local queue_pause_status_last = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_ATTR_PAUSE_STATUS_last') + -- Get all counters + local occupancy_bytes = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_CURR_OCCUPANCY_BYTES') + local packets = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS') + local pfc_rx_packets = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key) + local pfc_on2off = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_on2off_key) + local queue_pause_status = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_ATTR_PAUSE_STATUS') - -- DEBUG CODE START. Uncomment to enable - local debug_storm = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'DEBUG_STORM') - -- DEBUG CODE END. + if occupancy_bytes and packets and pfc_rx_packets and pfc_on2off and queue_pause_status then + occupancy_bytes = tonumber(occupancy_bytes) + packets = tonumber(packets) + pfc_rx_packets = tonumber(pfc_rx_packets) + pfc_on2off = tonumber(pfc_on2off) - -- If this is not a first run, then we have last values available - if packets_last and pfc_rx_packets_last and pfc_on2off_last and queue_pause_status_last then - packets_last = tonumber(packets_last) - pfc_rx_packets_last = tonumber(pfc_rx_packets_last) - pfc_on2off_last = tonumber(pfc_on2off_last) + local packets_last = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last') + local pfc_rx_packets_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last') + local pfc_on2off_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_on2off_key .. '_last') + local queue_pause_status_last = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_ATTR_PAUSE_STATUS_last') - -- Check actual condition of queue being in PFC storm - if (occupancy_bytes > 0 and packets - packets_last == 0 and pfc_rx_packets - pfc_rx_packets_last > 0) or -- DEBUG CODE START. Uncomment to enable - (debug_storm == "enabled") or + local debug_storm = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'DEBUG_STORM') -- DEBUG CODE END. - (occupancy_bytes == 0 and pfc_rx_packets - pfc_rx_packets_last > 0 and pfc_on2off - pfc_on2off_last == 0 and queue_pause_status_last == 'true' and queue_pause_status == 'true') then - if time_left <= poll_time then - redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","storm"]') - is_deadlock = true - time_left = detection_time - else - time_left = time_left - poll_time - end - else - if pfc_wd_action == 'alert' and pfc_wd_status ~= 'operational' then - redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","restore"]') + + -- If this is not a first run, then we have last values available + if packets_last and pfc_rx_packets_last and pfc_on2off_last and queue_pause_status_last then + packets_last = tonumber(packets_last) + pfc_rx_packets_last = tonumber(pfc_rx_packets_last) + pfc_on2off_last = tonumber(pfc_on2off_last) + + -- Check actual condition of queue being in PFC storm + if (occupancy_bytes > 0 and packets - packets_last == 0 and pfc_rx_packets - pfc_rx_packets_last > 0) or + -- DEBUG CODE START. Uncomment to enable + (debug_storm == "enabled") or + -- DEBUG CODE END. + (occupancy_bytes == 0 and pfc_rx_packets - pfc_rx_packets_last > 0 and pfc_on2off - pfc_on2off_last == 0 and queue_pause_status_last == 'true' and queue_pause_status == 'true') then + if time_left <= poll_time then + redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","storm"]') + is_deadlock = true + time_left = detection_time + else + time_left = time_left - poll_time + end + else + if pfc_wd_action == 'alert' and pfc_wd_status ~= 'operational' then + redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","restore"]') + end + time_left = detection_time + end end - time_left = detection_time + + -- Save values for next run + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_ATTR_PAUSE_STATUS_last', queue_pause_status) + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last', packets) + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT', time_left) + redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last', pfc_rx_packets) + redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_on2off_key .. '_last', pfc_on2off) end end - - -- Save values for next run - redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_ATTR_PAUSE_STATUS_last', queue_pause_status) - redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last', packets) - redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT', time_left) - redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last', pfc_rx_packets) - redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_on2off_key .. '_last', pfc_on2off) end end diff --git a/orchagent/pfc_detect_mellanox.lua b/orchagent/pfc_detect_mellanox.lua index 64244292d8..b450859438 100644 --- a/orchagent/pfc_detect_mellanox.lua +++ b/orchagent/pfc_detect_mellanox.lua @@ -21,68 +21,77 @@ for i = n, 1, -1 do local is_deadlock = false local pfc_wd_status = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_STATUS') local pfc_wd_action = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_ACTION') - if pfc_wd_status == 'operational' or pfc_wd_action == 'alert' then - local detection_time = tonumber(redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME')) - local time_left = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT') - if not time_left then - time_left = detection_time - else - time_left = tonumber(time_left) - end - local queue_index = redis.call('HGET', 'COUNTERS_QUEUE_INDEX_MAP', KEYS[i]) - local port_id = redis.call('HGET', 'COUNTERS_QUEUE_PORT_MAP', KEYS[i]) - local pfc_rx_pkt_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PKTS' - local pfc_duration_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PAUSE_DURATION' + local big_red_switch_mode = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'BIG_RED_SWITCH_MODE') + if not big_red_switch_mode and (pfc_wd_status == 'operational' or pfc_wd_action == 'alert') then + local detection_time = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME') + if detection_time then + detection_time = tonumber(detection_time) + local time_left = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT') + if not time_left then + time_left = detection_time + else + time_left = tonumber(time_left) + end - -- Get all counters - local occupancy_bytes = tonumber(redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_CURR_OCCUPANCY_BYTES')) - local packets = tonumber(redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS')) - local pfc_rx_packets = tonumber(redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key)) - local pfc_duration = tonumber(redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_duration_key)) + local queue_index = redis.call('HGET', 'COUNTERS_QUEUE_INDEX_MAP', KEYS[i]) + local port_id = redis.call('HGET', 'COUNTERS_QUEUE_PORT_MAP', KEYS[i]) + local pfc_rx_pkt_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PKTS' + local pfc_duration_key = 'SAI_PORT_STAT_PFC_' .. queue_index .. '_RX_PAUSE_DURATION' - local packets_last = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last') - local pfc_rx_packets_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last') - local pfc_duration_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last') - -- DEBUG CODE START. Uncomment to enable - local debug_storm = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'DEBUG_STORM') - -- DEBUG CODE END. + -- Get all counters + local occupancy_bytes = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_CURR_OCCUPANCY_BYTES') + local packets = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS') + local pfc_rx_packets = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key) + local pfc_duration = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_duration_key) - -- If this is not a first run, then we have last values available - if packets_last and pfc_rx_packets_last and pfc_duration_last then - packets_last = tonumber(packets_last) - pfc_rx_packets_last = tonumber(pfc_rx_packets_last) - pfc_duration_last = tonumber(pfc_duration_last) + if occupancy_bytes and packets and pfc_rx_packets and pfc_duration then + occupancy_bytes = tonumber(occupancy_bytes) + packets = tonumber(packets) + pfc_rx_packets = tonumber(pfc_rx_packets) + pfc_duration = tonumber(pfc_duration) - -- Check actual condition of queue being in PFC storm - if (occupancy_bytes > 0 and packets - packets_last == 0 and pfc_rx_packets - pfc_rx_packets_last > 0) or + local packets_last = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last') + local pfc_rx_packets_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last') + local pfc_duration_last = redis.call('HGET', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last') -- DEBUG CODE START. Uncomment to enable - (debug_storm == "enabled") or + local debug_storm = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'DEBUG_STORM') -- DEBUG CODE END. - (occupancy_bytes == 0 and packets - packets_last == 0 and (pfc_duration - pfc_duration_last) > poll_time * 0.8) then - if time_left <= poll_time then - redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","storm"]') - is_deadlock = true - time_left = detection_time - else - time_left = time_left - poll_time + + -- If this is not a first run, then we have last values available + if packets_last and pfc_rx_packets_last and pfc_duration_last then + packets_last = tonumber(packets_last) + pfc_rx_packets_last = tonumber(pfc_rx_packets_last) + pfc_duration_last = tonumber(pfc_duration_last) + + -- Check actual condition of queue being in PFC storm + if (occupancy_bytes > 0 and packets - packets_last == 0 and pfc_rx_packets - pfc_rx_packets_last > 0) or + -- DEBUG CODE START. Uncomment to enable + (debug_storm == "enabled") or + -- DEBUG CODE END. + (occupancy_bytes == 0 and packets - packets_last == 0 and (pfc_duration - pfc_duration_last) > poll_time * 0.8) then + if time_left <= poll_time then + redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","storm"]') + is_deadlock = true + time_left = detection_time + else + time_left = time_left - poll_time + end + else + if pfc_wd_action == 'alert' and pfc_wd_status ~= 'operational' then + redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","restore"]') + end + time_left = detection_time + end end - else - if pfc_wd_action == 'alert' and pfc_wd_status ~= 'operational' then - redis.call('PUBLISH', 'PFC_WD', '["' .. KEYS[i] .. '","restore"]') - end - time_left = detection_time - end - end - -- Save values for next run - redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last', packets) - redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT', time_left) - redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last', pfc_rx_packets) - if is_deadlock then - redis.call('HDEL', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last') - else - redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last', pfc_duration) + -- Save values for next run + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'SAI_QUEUE_STAT_PACKETS_last', packets) + redis.call('HSET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_DETECTION_TIME_LEFT', time_left) + redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_rx_pkt_key .. '_last', pfc_rx_packets) + redis.call('HDEL', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last') + redis.call('HSET', counters_table_name .. ':' .. port_id, pfc_duration_key .. '_last', pfc_duration) + end end end end diff --git a/orchagent/pfc_restore.lua b/orchagent/pfc_restore.lua index beb814dad9..5b3e4ed046 100644 --- a/orchagent/pfc_restore.lua +++ b/orchagent/pfc_restore.lua @@ -20,10 +20,11 @@ for i = n, 1, -1 do local pfc_wd_status = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_STATUS') local restoration_time = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_RESTORATION_TIME') local pfc_wd_action = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_ACTION') - if pfc_wd_status ~= 'operational' and pfc_wd_action ~= 'alert' and restoration_time ~= '' then + local big_red_switch_mode = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'BIG_RED_SWITCH_MODE') + if not big_red_switch_mode and pfc_wd_status ~= 'operational' and pfc_wd_action ~= 'alert' and restoration_time and restoration_time ~= '' then restoration_time = tonumber(restoration_time) local time_left = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'PFC_WD_RESTORATION_TIME_LEFT') - if time_left == nil then + if not time_left then time_left = restoration_time else time_left = tonumber(time_left) diff --git a/orchagent/pfcactionhandler.cpp b/orchagent/pfcactionhandler.cpp index c8ff7e0084..538b4d2e06 100644 --- a/orchagent/pfcactionhandler.cpp +++ b/orchagent/pfcactionhandler.cpp @@ -1,6 +1,7 @@ +#include #include "pfcactionhandler.h" #include "logger.h" -#include "saiserialize.h" +#include "sai_serialize.h" #include "portsorch.h" #include @@ -11,8 +12,15 @@ #define PFC_WD_QUEUE_STATS_DEADLOCK_DETECTED "PFC_WD_QUEUE_STATS_DEADLOCK_DETECTED" #define PFC_WD_QUEUE_STATS_DEADLOCK_RESTORED "PFC_WD_QUEUE_STATS_DEADLOCK_RESTORED" -#define SAI_QUEUE_STAT_PACKETS_STR "SAI_QUEUE_STAT_PACKETS" -#define SAI_QUEUE_STAT_DROPPED_PACKETS_STR "SAI_QUEUE_STAT_DROPPED_PACKETS" +#define PFC_WD_QUEUE_STATS_TX_PACKETS "PFC_WD_QUEUE_STATS_TX_PACKETS" +#define PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS "PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS" +#define PFC_WD_QUEUE_STATS_RX_PACKETS "PFC_WD_QUEUE_STATS_RX_PACKETS" +#define PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS "PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS" + +#define PFC_WD_QUEUE_STATS_TX_PACKETS_LAST "PFC_WD_QUEUE_STATS_TX_PACKETS_LAST" +#define PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS_LAST "PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS_LAST" +#define PFC_WD_QUEUE_STATS_RX_PACKETS_LAST "PFC_WD_QUEUE_STATS_RX_PACKETS_LAST" +#define PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS_LAST "PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS_LAST" extern sai_object_id_t gSwitchId; extern PortsOrch *gPortsOrch; @@ -27,36 +35,67 @@ PfcWdActionHandler::PfcWdActionHandler(sai_object_id_t port, sai_object_id_t que m_queue(queue), m_queueId(queueId), m_countersTable(countersTable) +{ + SWSS_LOG_ENTER(); +} + +PfcWdActionHandler::~PfcWdActionHandler(void) +{ + SWSS_LOG_ENTER(); + +} + +void PfcWdActionHandler::initCounters(void) { SWSS_LOG_ENTER(); - SWSS_LOG_NOTICE( - "PFC Watchdog detected PFC storm on queue 0x%lx port 0x%lx", - m_queue, - m_port); + if (!getHwCounters(m_hwStats)) + { + return; + } - m_stats = getQueueStats(m_countersTable, sai_serialize_object_id(m_queue)); - m_stats.detectCount++; - m_stats.operational = false; + auto wdQueueStats = getQueueStats(m_countersTable, sai_serialize_object_id(m_queue)); + wdQueueStats.detectCount++; + wdQueueStats.operational = false; - updateWdCounters(sai_serialize_object_id(m_queue), m_stats); + wdQueueStats.txPktLast = 0; + wdQueueStats.txDropPktLast = 0; + wdQueueStats.rxPktLast = 0; + wdQueueStats.rxDropPktLast = 0; + + updateWdCounters(sai_serialize_object_id(m_queue), wdQueueStats); } -PfcWdActionHandler::~PfcWdActionHandler(void) +void PfcWdActionHandler::commitCounters(bool periodic /* = false */) { SWSS_LOG_ENTER(); + PfcWdHwStats hwStats; + + if (!getHwCounters(hwStats)) + { + return; + } + auto finalStats = getQueueStats(m_countersTable, sai_serialize_object_id(m_queue)); - SWSS_LOG_NOTICE( - "Queue 0x%lx port 0x%lx restored from PFC storm. Tx packets: %lu. Dropped packets: %lu", - m_queue, - m_port, - finalStats.txPkt - m_stats.txPkt, - finalStats.txDropPkt - m_stats.txDropPkt); + if (!periodic) + { + finalStats.restoreCount++; + } + finalStats.operational = !periodic; - finalStats.restoreCount++; - finalStats.operational = true; + finalStats.txPktLast += hwStats.txPkt - m_hwStats.txPkt; + finalStats.txDropPktLast += hwStats.txDropPkt - m_hwStats.txDropPkt; + finalStats.rxPktLast += hwStats.rxPkt - m_hwStats.rxPkt; + finalStats.rxDropPktLast += hwStats.rxDropPkt - m_hwStats.rxDropPkt; + + finalStats.txPkt += hwStats.txPkt - m_hwStats.txPkt; + finalStats.txDropPkt += hwStats.txDropPkt - m_hwStats.txDropPkt; + finalStats.rxPkt += hwStats.rxPkt - m_hwStats.rxPkt; + finalStats.rxDropPkt += hwStats.rxDropPkt - m_hwStats.rxDropPkt; + + m_hwStats = hwStats; updateWdCounters(sai_serialize_object_id(m_queue), finalStats); } @@ -66,6 +105,8 @@ PfcWdActionHandler::PfcWdQueueStats PfcWdActionHandler::getQueueStats(shared_ptr SWSS_LOG_ENTER(); PfcWdQueueStats stats; + memset(&stats, 0, sizeof(PfcWdQueueStats)); + stats.operational = true; vector fieldValues; if (!countersTable->get(queueIdStr, fieldValues)) @@ -90,14 +131,38 @@ PfcWdActionHandler::PfcWdQueueStats PfcWdActionHandler::getQueueStats(shared_ptr { stats.operational = value == PFC_WD_QUEUE_STATUS_OPERATIONAL ? true : false; } - else if (field == SAI_QUEUE_STAT_PACKETS_STR) + else if (field == PFC_WD_QUEUE_STATS_TX_PACKETS) { stats.txPkt = stoul(value); } - else if (field == SAI_QUEUE_STAT_DROPPED_PACKETS_STR) + else if (field == PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS) { stats.txDropPkt = stoul(value); } + else if (field == PFC_WD_QUEUE_STATS_RX_PACKETS) + { + stats.rxPkt = stoul(value); + } + else if (field == PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS) + { + stats.rxDropPkt = stoul(value); + } + else if (field == PFC_WD_QUEUE_STATS_TX_PACKETS_LAST) + { + stats.txPktLast = stoul(value); + } + else if (field == PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS_LAST) + { + stats.txDropPktLast = stoul(value); + } + else if (field == PFC_WD_QUEUE_STATS_RX_PACKETS_LAST) + { + stats.rxPktLast = stoul(value); + } + else if (field == PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS_LAST) + { + stats.rxDropPktLast = stoul(value); + } } return move(stats); @@ -126,6 +191,17 @@ void PfcWdActionHandler::updateWdCounters(const string& queueIdStr, const PfcWdQ resultFvValues.emplace_back(PFC_WD_QUEUE_STATS_DEADLOCK_DETECTED, to_string(stats.detectCount)); resultFvValues.emplace_back(PFC_WD_QUEUE_STATS_DEADLOCK_RESTORED, to_string(stats.restoreCount)); + + resultFvValues.emplace_back(PFC_WD_QUEUE_STATS_TX_PACKETS, to_string(stats.txPkt)); + resultFvValues.emplace_back(PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS, to_string(stats.txDropPkt)); + resultFvValues.emplace_back(PFC_WD_QUEUE_STATS_RX_PACKETS, to_string(stats.rxPkt)); + resultFvValues.emplace_back(PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS, to_string(stats.rxDropPkt)); + + resultFvValues.emplace_back(PFC_WD_QUEUE_STATS_TX_PACKETS_LAST, to_string(stats.txPktLast)); + resultFvValues.emplace_back(PFC_WD_QUEUE_STATS_TX_DROPPED_PACKETS_LAST, to_string(stats.txDropPktLast)); + resultFvValues.emplace_back(PFC_WD_QUEUE_STATS_RX_PACKETS_LAST, to_string(stats.rxPktLast)); + resultFvValues.emplace_back(PFC_WD_QUEUE_STATS_RX_DROPPED_PACKETS_LAST, to_string(stats.rxDropPktLast)); + resultFvValues.emplace_back(PFC_WD_QUEUE_STATUS, stats.operational ? PFC_WD_QUEUE_STATUS_OPERATIONAL : PFC_WD_QUEUE_STATUS_STORMED); @@ -135,11 +211,11 @@ void PfcWdActionHandler::updateWdCounters(const string& queueIdStr, const PfcWdQ PfcWdAclHandler::PfcWdAclHandler(sai_object_id_t port, sai_object_id_t queue, uint8_t queueId, shared_ptr
countersTable): - PfcWdActionHandler(port, queue, queueId, countersTable) + PfcWdLossyHandler(port, queue, queueId, countersTable) { SWSS_LOG_ENTER(); - acl_table_type_t table_type = ACL_TABLE_L3; + acl_table_type_t table_type = ACL_TABLE_PFCWD; // There is one handler instance per queue ID string queuestr = to_string(queueId); @@ -152,7 +228,7 @@ PfcWdAclHandler::PfcWdAclHandler(sai_object_id_t port, sai_object_id_t queue, { // First time of handling PFC for this queue, create ACL table, and bind createPfcAclTable(port, m_strIngressTable, true); - shared_ptr newRule = make_shared(gAclOrch, m_strRule, m_strIngressTable, table_type); + shared_ptr newRule = make_shared(gAclOrch, m_strRule, m_strIngressTable, table_type); createPfcAclRule(newRule, queueId, m_strIngressTable); } else @@ -166,7 +242,7 @@ PfcWdAclHandler::PfcWdAclHandler(sai_object_id_t port, sai_object_id_t queue, { // First time of handling PFC for this queue, create ACL table, and bind createPfcAclTable(port, m_strEgressTable, false); - shared_ptr newRule = make_shared(gAclOrch, m_strRule, m_strEgressTable, table_type); + shared_ptr newRule = make_shared(gAclOrch, m_strRule, m_strEgressTable, table_type); createPfcAclRule(newRule, queueId, m_strEgressTable); } else @@ -209,14 +285,14 @@ void PfcWdAclHandler::createPfcAclTable(sai_object_id_t port, string strTable, b assert(inserted.second); AclTable& aclTable = inserted.first->second; - aclTable.type = ACL_TABLE_L3; + aclTable.type = ACL_TABLE_PFCWD; aclTable.link(port); aclTable.id = strTable; aclTable.stage = ingress ? ACL_STAGE_INGRESS : ACL_STAGE_EGRESS; gAclOrch->addAclTable(aclTable, strTable); } -void PfcWdAclHandler::createPfcAclRule(shared_ptr rule, uint8_t queueId, string strTable) +void PfcWdAclHandler::createPfcAclRule(shared_ptr rule, uint8_t queueId, string strTable) { SWSS_LOG_ENTER(); @@ -291,6 +367,69 @@ PfcWdLossyHandler::~PfcWdLossyHandler(void) } } +bool PfcWdLossyHandler::getHwCounters(PfcWdHwStats& counters) +{ + SWSS_LOG_ENTER(); + + static const vector queueStatIds = + { + SAI_QUEUE_STAT_PACKETS, + SAI_QUEUE_STAT_DROPPED_PACKETS, + }; + + static const vector pgStatIds = + { + SAI_INGRESS_PRIORITY_GROUP_STAT_PACKETS, + SAI_INGRESS_PRIORITY_GROUP_STAT_DROPPED_PACKETS, + }; + + vector queueStats; + queueStats.resize(queueStatIds.size()); + + sai_status_t status = sai_queue_api->get_queue_stats( + getQueue(), + static_cast(queueStatIds.size()), + queueStatIds.data(), + queueStats.data()); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to fetch queue 0x%lx stats: %d", getQueue(), status); + return false; + } + + // PG counters not yet supported in Mellanox platform + Port portInstance; + if (!gPortsOrch->getPort(getPort(), portInstance)) + { + SWSS_LOG_ERROR("Cannot get port by ID 0x%lx", getPort()); + return false; + } + + sai_object_id_t pg = portInstance.m_priority_group_ids[getQueueId()]; + vector pgStats; + pgStats.resize(pgStatIds.size()); + + status = sai_buffer_api->get_ingress_priority_group_stats( + pg, + static_cast(pgStatIds.size()), + pgStatIds.data(), + pgStats.data()); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to fetch pg 0x%lx stats: %d", pg, status); + return false; + } + + counters.txPkt = queueStats[0]; + counters.txDropPkt = queueStats[1]; + counters.rxPkt = pgStats[0]; + counters.rxDropPkt = pgStats[1]; + + return true; +} + PfcWdZeroBufferHandler::PfcWdZeroBufferHandler(sai_object_id_t port, sai_object_id_t queue, uint8_t queueId, shared_ptr
countersTable): PfcWdLossyHandler(port, queue, queueId, countersTable) @@ -480,7 +619,7 @@ void PfcWdZeroBufferHandler::ZeroBufferProfile::createZeroBufferProfile(bool ing attribs.push_back(attr); attr.id = SAI_BUFFER_PROFILE_ATTR_SHARED_DYNAMIC_TH; - attr.value.u32 = 1; + attr.value.s8 = -8; // ALPHA_0 attribs.push_back(attr); status = sai_buffer_api->create_buffer_profile( diff --git a/orchagent/pfcactionhandler.h b/orchagent/pfcactionhandler.h index 34cf73c981..07a27babec 100644 --- a/orchagent/pfcactionhandler.h +++ b/orchagent/pfcactionhandler.h @@ -13,6 +13,14 @@ extern "C" { using namespace std; using namespace swss; +struct PfcWdHwStats +{ + uint64_t txPkt; + uint64_t txDropPkt; + uint64_t rxPkt; + uint64_t rxDropPkt; +}; + // PFC queue interface class // It resembles RAII behavior - pause storm is mitigated (queue is locked) on creation, // and is restored (queue released) on removal @@ -39,15 +47,30 @@ class PfcWdActionHandler } static void initWdCounters(shared_ptr
countersTable, const string &queueIdStr); + void initCounters(void); + void commitCounters(bool periodic = false); + + virtual bool getHwCounters(PfcWdHwStats& counters) + { + memset(&counters, 0, sizeof(PfcWdHwStats)); + + return true; + }; private: struct PfcWdQueueStats { - uint64_t detectCount = 0; - uint64_t restoreCount = 0; - uint64_t txPkt = 0; - uint64_t txDropPkt = 0; - bool operational = true; + uint64_t detectCount; + uint64_t restoreCount; + uint64_t txPkt; + uint64_t txDropPkt; + uint64_t rxPkt; + uint64_t rxDropPkt; + uint64_t txPktLast; + uint64_t txDropPktLast; + uint64_t rxPktLast; + uint64_t rxDropPktLast; + bool operational; }; static PfcWdQueueStats getQueueStats(shared_ptr
countersTable, const string &queueIdStr); @@ -56,11 +79,22 @@ class PfcWdActionHandler sai_object_id_t m_port = SAI_NULL_OBJECT_ID; sai_object_id_t m_queue = SAI_NULL_OBJECT_ID; uint8_t m_queueId = 0; + string m_portAlias; shared_ptr
m_countersTable = nullptr; - PfcWdQueueStats m_stats; + PfcWdHwStats m_hwStats; }; -class PfcWdAclHandler: public PfcWdActionHandler +// Pfc queue that implements forward action by disabling PFC on queue +class PfcWdLossyHandler: public PfcWdActionHandler +{ + public: + PfcWdLossyHandler(sai_object_id_t port, sai_object_id_t queue, + uint8_t queueId, shared_ptr
countersTable); + virtual ~PfcWdLossyHandler(void); + virtual bool getHwCounters(PfcWdHwStats& counters); +}; + +class PfcWdAclHandler: public PfcWdLossyHandler { public: PfcWdAclHandler(sai_object_id_t port, sai_object_id_t queue, @@ -77,16 +111,7 @@ class PfcWdAclHandler: public PfcWdActionHandler string m_strEgressTable; string m_strRule; void createPfcAclTable(sai_object_id_t port, string strTable, bool ingress); - void createPfcAclRule(shared_ptr rule, uint8_t queueId, string strTable); -}; - -// Pfc queue that implements forward action by disabling PFC on queue -class PfcWdLossyHandler: public PfcWdActionHandler -{ - public: - PfcWdLossyHandler(sai_object_id_t port, sai_object_id_t queue, - uint8_t queueId, shared_ptr
countersTable); - virtual ~PfcWdLossyHandler(void); + void createPfcAclRule(shared_ptr rule, uint8_t queueId, string strTable); }; // PFC queue that implements drop action by draining queue with buffer of zero size diff --git a/orchagent/pfcwdorch.cpp b/orchagent/pfcwdorch.cpp index 3e31061366..065bfc4d03 100644 --- a/orchagent/pfcwdorch.cpp +++ b/orchagent/pfcwdorch.cpp @@ -1,22 +1,29 @@ #include +#include #include "pfcwdorch.h" -#include "saiserialize.h" +#include "sai_serialize.h" #include "portsorch.h" #include "converter.h" #include "redisapi.h" #include "select.h" #include "notifier.h" +#include "redisclient.h" +#define PFC_WD_FLEX_COUNTER_GROUP "PFC_WD" +#define PFC_WD_GLOBAL "GLOBAL" #define PFC_WD_ACTION "action" #define PFC_WD_DETECTION_TIME "detection_time" #define PFC_WD_RESTORATION_TIME "restoration_time" +#define BIG_RED_SWITCH_FIELD "BIG_RED_SWITCH" #define PFC_WD_DETECTION_TIME_MAX (5 * 1000) #define PFC_WD_DETECTION_TIME_MIN 100 #define PFC_WD_RESTORATION_TIME_MAX (60 * 1000) #define PFC_WD_RESTORATION_TIME_MIN 100 -#define PFC_WD_TC_MAX 8 #define PFC_WD_POLL_TIMEOUT 5000 +#define SAI_PORT_STAT_PFC_PREFIX "SAI_PORT_STAT_PFC_" +#define PFC_WD_TC_MAX 8 +#define COUNTER_CHECK_POLL_TIMEOUT_SEC 1 extern sai_port_api_t *sai_port_api; extern sai_queue_api_t *sai_queue_api; @@ -32,6 +39,7 @@ PfcWdOrch::PfcWdOrch(DBConnector *db, vector PfcWdOrch::~PfcWdOrch(void) { @@ -251,6 +259,198 @@ void PfcWdOrch::deleteEntry(const string& name) SWSS_LOG_NOTICE("Stopped PFC Watchdog on port %s", name.c_str()); } +template +void PfcWdSwOrch::createEntry(const string& key, + const vector& data) +{ + SWSS_LOG_ENTER(); + + if (key == PFC_WD_GLOBAL) + { + for (auto valuePair: data) + { + const auto &field = fvField(valuePair); + const auto &value = fvValue(valuePair); + + if (field == POLL_INTERVAL_FIELD) + { + vector fieldValues; + fieldValues.emplace_back(POLL_INTERVAL_FIELD, value); + m_flexCounterGroupTable->set(PFC_WD_FLEX_COUNTER_GROUP, fieldValues); + } + else if (field == BIG_RED_SWITCH_FIELD) + { + SWSS_LOG_NOTICE("Recieve brs mode set, %s", value.c_str()); + setBigRedSwitchMode(value); + } + } + } + else + { + PfcWdOrch::createEntry(key, data); + } +} + +template +void PfcWdSwOrch::setBigRedSwitchMode(const string value) +{ + SWSS_LOG_ENTER(); + + if (value == "enable") + { + // When BIG_RED_SWITCH mode is enabled, pfcwd is automatically disabled + enableBigRedSwitchMode(); + } + else if (value == "disable") + { + disableBigRedSwitchMode(); + } + else + { + SWSS_LOG_NOTICE("Unsupported BIG_RED_SWITCH mode set input, please use enable or disable"); + } + +} + +template +void PfcWdSwOrch::disableBigRedSwitchMode() +{ + SWSS_LOG_ENTER(); + + m_bigRedSwitchFlag = false; + // Disable pfcwdaction hanlder on each queue if exists. + for (auto &entry : m_brsEntryMap) + { + + if (entry.second.handler != nullptr) + { + SWSS_LOG_NOTICE( + "PFC Watchdog BIG_RED_SWITCH mode disabled on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + entry.second.portAlias.c_str(), + entry.second.index, + entry.first, + entry.second.portId); + + entry.second.handler->commitCounters(); + entry.second.handler = nullptr; + } + + auto queueId = entry.first; + RedisClient redisClient(PfcWdOrch::getCountersDb().get()); + string countersKey = COUNTERS_TABLE ":" + sai_serialize_object_id(queueId); + redisClient.hdel(countersKey, "BIG_RED_SWITCH_MODE"); + } + + m_brsEntryMap.clear(); +} + +template +void PfcWdSwOrch::enableBigRedSwitchMode() +{ + SWSS_LOG_ENTER(); + + m_bigRedSwitchFlag = true; + // Write to database that each queue enables BIG_RED_SWITCH + auto allPorts = gPortsOrch->getAllPorts(); + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_PRIORITY_FLOW_CONTROL; + + for (auto &it: allPorts) + { + Port port = it.second; + + if (port.m_type != Port::PHY) + { + SWSS_LOG_INFO("Skip non-phy port %s", port.m_alias.c_str()); + continue; + } + + // use portorch api to get lossless tc in future. + sai_status_t status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get PFC mask on port %s: %d", port.m_alias.c_str(), status); + return; + } + + uint8_t pfcMask = attr.value.u8; + for (uint8_t i = 0; i < PFC_WD_TC_MAX; i++) + { + sai_object_id_t queueId = port.m_queue_ids[i]; + if ((pfcMask & (1 << i)) == 0 && m_entryMap.find(queueId) == m_entryMap.end()) + { + continue; + } + + string queueIdStr = sai_serialize_object_id(queueId); + + vector countersFieldValues; + countersFieldValues.emplace_back("BIG_RED_SWITCH_MODE", "enable"); + PfcWdOrch::getCountersTable()->set(queueIdStr, countersFieldValues); + } + } + + // Disable pfcwdaction handler on each queue if exists. + for (auto & entry: m_entryMap) + { + if (entry.second.handler != nullptr) + { + entry.second.handler->commitCounters(); + entry.second.handler = nullptr; + } + } + + // Create pfcwdaction hanlder on all the ports. + for (auto & it: allPorts) + { + Port port = it.second; + if (port.m_type != Port::PHY) + { + SWSS_LOG_INFO("Skip non-phy port %s", port.m_alias.c_str()); + continue; + } + + // use portorch api to get lossless tc in future after asym PFC is available. + sai_status_t status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to get PFC mask on port %s: %d", port.m_alias.c_str(), status); + return; + } + + uint8_t pfcMask = attr.value.u8; + for (uint8_t i = 0; i < PFC_WD_TC_MAX; i++) + { + if ((pfcMask & (1 << i)) == 0) + { + continue; + } + + sai_object_id_t queueId = port.m_queue_ids[i]; + string queueIdStr = sai_serialize_object_id(queueId); + + auto entry = m_brsEntryMap.emplace(queueId, PfcWdQueueEntry(PfcWdAction::PFC_WD_ACTION_DROP, port.m_port_id, i, port.m_alias)).first; + + if (entry->second.handler== nullptr) + { + SWSS_LOG_NOTICE( + "PFC Watchdog BIG_RED_SWITCH mode enabled on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + entry->second.portAlias.c_str(), + entry->second.index, + entry->first, + entry->second.portId); + + entry->second.handler = make_shared( + entry->second.portId, + entry->first, + entry->second.index, + PfcWdOrch::getCountersTable()); + entry->second.handler->initCounters(); + } + } + } +} + template void PfcWdSwOrch::registerInWdDb(const Port& port, uint32_t detectionTime, uint32_t restorationTime, PfcWdAction action) @@ -267,16 +467,7 @@ void PfcWdSwOrch::registerInWdDb(const Port& port, return; } - if (!c_portStatIds.empty()) - { - string key = sai_serialize_object_id(port.m_port_id) + ":" + std::to_string(m_pollInterval); - vector fieldValues; - string str = counterIdsToStr(c_portStatIds, &sai_serialize_port_stat); - fieldValues.emplace_back(PFC_WD_PORT_COUNTER_ID_LIST, str); - - m_pfcWdTable->set(key, fieldValues); - } - + set losslessTc; uint8_t pfcMask = attr.value.u8; for (uint8_t i = 0; i < PFC_WD_TC_MAX; i++) { @@ -285,6 +476,23 @@ void PfcWdSwOrch::registerInWdDb(const Port& port, continue; } + losslessTc.insert(i); + } + + if (!c_portStatIds.empty()) + { + string key = getFlexCounterTableKey(sai_serialize_object_id(port.m_port_id)); + vector fieldValues; + // Only register lossless tc counters in database. + string str = counterIdsToStr(c_portStatIds, &sai_serialize_port_stat); + string filteredStr = filterPfcCounters(str, losslessTc); + fieldValues.emplace_back(PORT_COUNTER_ID_LIST, filteredStr); + + m_flexCounterTable->set(key, fieldValues); + } + + for (auto i : losslessTc) + { sai_object_id_t queueId = port.m_queue_ids[i]; string queueIdStr = sai_serialize_object_id(queueId); @@ -306,21 +514,20 @@ void PfcWdSwOrch::registerInWdDb(const Port& port, if (!c_queueStatIds.empty()) { string str = counterIdsToStr(c_queueStatIds, sai_serialize_queue_stat); - queueFieldValues.emplace_back(PFC_WD_QUEUE_COUNTER_ID_LIST, str); + queueFieldValues.emplace_back(QUEUE_COUNTER_ID_LIST, str); } if (!c_queueAttrIds.empty()) { string str = counterIdsToStr(c_queueAttrIds, sai_serialize_queue_attr); - queueFieldValues.emplace_back(PFC_WD_QUEUE_ATTR_ID_LIST, str); + queueFieldValues.emplace_back(QUEUE_ATTR_ID_LIST, str); } // Create internal entry - m_entryMap.emplace(queueId, PfcWdQueueEntry(action, port.m_port_id, i)); - - string key = queueIdStr + ":" + std::to_string(m_pollInterval); + m_entryMap.emplace(queueId, PfcWdQueueEntry(action, port.m_port_id, i, port.m_alias)); - m_pfcWdTable->set(key, queueFieldValues); + string key = getFlexCounterTableKey(queueIdStr); + m_flexCounterTable->set(key, queueFieldValues); // Initialize PFC WD related counters PfcWdActionHandler::initWdCounters( @@ -329,20 +536,82 @@ void PfcWdSwOrch::registerInWdDb(const Port& port, } } +template +string PfcWdSwOrch::filterPfcCounters(string counters, set& losslessTc) +{ + SWSS_LOG_ENTER(); + + istringstream is(counters); + string counter; + string filterCounters; + + while (getline(is, counter, ',')) + { + size_t index = 0; + index = counter.find(SAI_PORT_STAT_PFC_PREFIX); + if (index != 0) + { + filterCounters = filterCounters + counter + ","; + } + else + { + uint8_t tc = (uint8_t)atoi(counter.substr(index + sizeof(SAI_PORT_STAT_PFC_PREFIX) - 1, 1).c_str()); + if (losslessTc.count(tc)) + { + filterCounters = filterCounters + counter + ","; + } + } + } + + if (!filterCounters.empty()) + { + filterCounters.pop_back(); + } + + return filterCounters; +} + +template +string PfcWdSwOrch::getFlexCounterTableKey(string key) +{ + SWSS_LOG_ENTER(); + + return string(PFC_WD_FLEX_COUNTER_GROUP) + ":" + key; +} + template void PfcWdSwOrch::unregisterFromWdDb(const Port& port) { SWSS_LOG_ENTER(); + string key = getFlexCounterTableKey(sai_serialize_object_id(port.m_port_id)); + m_flexCounterTable->del(key); + for (uint8_t i = 0; i < PFC_WD_TC_MAX; i++) { sai_object_id_t queueId = port.m_queue_ids[i]; - string key = sai_serialize_object_id(queueId) + ":" + std::to_string(m_pollInterval); + string key = getFlexCounterTableKey(sai_serialize_object_id(queueId)); // Unregister in syncd - m_pfcWdTable->del(key); + m_flexCounterTable->del(key); + + auto entry = m_entryMap.find(queueId); + if (entry != m_entryMap.end() && entry->second.handler != nullptr) + { + entry->second.handler->commitCounters(); + } + m_entryMap.erase(queueId); + + // Clean up + RedisClient redisClient(PfcWdOrch::getCountersDb().get()); + string countersKey = COUNTERS_TABLE ":" + sai_serialize_object_id(queueId); + redisClient.hdel(countersKey, "PFC_WD_DETECTION_TIME"); + redisClient.hdel(countersKey, "PFC_WD_RESTORATION_TIME"); + redisClient.hdel(countersKey, "PFC_WD_ACTION"); + redisClient.hdel(countersKey, "PFC_WD_STATUS"); } + } template @@ -354,8 +623,9 @@ PfcWdSwOrch::PfcWdSwOrch( const vector &queueAttrIds, int pollInterval): PfcWdOrch(db, tableNames), - m_pfcWdDb(new DBConnector(PFC_WD_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), - m_pfcWdTable(new ProducerStateTable(m_pfcWdDb.get(), PFC_WD_STATE_TABLE)), + m_flexCounterDb(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), + m_flexCounterTable(new ProducerTable(m_flexCounterDb.get(), FLEX_COUNTER_TABLE)), + m_flexCounterGroupTable(new ProducerTable(m_flexCounterDb.get(), FLEX_COUNTER_GROUP_TABLE)), c_portStatIds(portStatIds), c_queueStatIds(queueStatIds), c_queueAttrIds(queueAttrIds), @@ -387,38 +657,42 @@ PfcWdSwOrch::PfcWdSwOrch( restoreLuaScript); vector fieldValues; - fieldValues.emplace_back(SAI_OBJECT_TYPE, sai_serialize_object_type(SAI_OBJECT_TYPE_QUEUE)); - - auto pluginTable = ProducerStateTable(m_pfcWdDb.get(), PLUGIN_TABLE); - string detectShaKey = detectSha + ":" + std::to_string(m_pollInterval); - string restoreShaKey = restoreSha + ":" + std::to_string(m_pollInterval); - pluginTable.set(detectShaKey, fieldValues); - pluginTable.set(restoreShaKey, fieldValues); + fieldValues.emplace_back(QUEUE_PLUGIN_FIELD, detectSha + "," + restoreSha); + fieldValues.emplace_back(POLL_INTERVAL_FIELD, to_string(m_pollInterval)); + m_flexCounterGroupTable->set(PFC_WD_FLEX_COUNTER_GROUP, fieldValues); } catch (...) { - SWSS_LOG_WARN("Lua scripts for PFC watchdog were not loaded"); + SWSS_LOG_WARN("Lua scripts and polling interval for PFC watchdog were not set successfully"); } auto consumer = new swss::NotificationConsumer( PfcWdSwOrch::getCountersDb().get(), "PFC_WD"); auto wdNotification = new Notifier(consumer, this); - Orch::addExecutor("", wdNotification); + Orch::addExecutor("PFC_WD", wdNotification); + + auto interv = timespec { .tv_sec = COUNTER_CHECK_POLL_TIMEOUT_SEC, .tv_nsec = 0 }; + auto timer = new SelectableTimer(interv); + auto executor = new ExecutableTimer(timer, this); + Orch::addExecutor("PFC_WD_COUNTERS_POLL", executor); + timer->start(); } template PfcWdSwOrch::~PfcWdSwOrch(void) { SWSS_LOG_ENTER(); + m_flexCounterGroupTable->del(PFC_WD_FLEX_COUNTER_GROUP); } template PfcWdSwOrch::PfcWdQueueEntry::PfcWdQueueEntry( - PfcWdAction action, sai_object_id_t port, uint8_t idx): + PfcWdAction action, sai_object_id_t port, uint8_t idx, string alias): action(action), portId(port), - index(idx) + index(idx), + portAlias(alias) { SWSS_LOG_ENTER(); } @@ -466,49 +740,89 @@ void PfcWdSwOrch::doTask(swss::NotificationConsumer } SWSS_LOG_NOTICE("Receive notification, %s", event.c_str()); - if (event == "storm") + + if (m_bigRedSwitchFlag) + { + SWSS_LOG_NOTICE("Big_RED_SWITCH mode is on, ingore syncd pfc watchdog notification"); + } + else if (event == "storm") { if (entry->second.action == PfcWdAction::PFC_WD_ACTION_ALERT) { if (entry->second.handler == nullptr) { + SWSS_LOG_NOTICE( + "PFC Watchdog detected PFC storm on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + entry->second.portAlias.c_str(), + entry->second.index, + entry->first, + entry->second.portId); + entry->second.handler = make_shared( entry->second.portId, entry->first, entry->second.index, PfcWdOrch::getCountersTable()); + entry->second.handler->initCounters(); } } else if (entry->second.action == PfcWdAction::PFC_WD_ACTION_DROP) { if (entry->second.handler == nullptr) { + SWSS_LOG_NOTICE( + "PFC Watchdog detected PFC storm on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + entry->second.portAlias.c_str(), + entry->second.index, + entry->first, + entry->second.portId); + entry->second.handler = make_shared( entry->second.portId, entry->first, entry->second.index, PfcWdOrch::getCountersTable()); + entry->second.handler->initCounters(); } } else if (entry->second.action == PfcWdAction::PFC_WD_ACTION_FORWARD) { if (entry->second.handler == nullptr) { + SWSS_LOG_NOTICE( + "PFC Watchdog detected PFC storm on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + entry->second.portAlias.c_str(), + entry->second.index, + entry->first, + entry->second.portId); + entry->second.handler = make_shared( entry->second.portId, entry->first, entry->second.index, PfcWdOrch::getCountersTable()); + entry->second.handler->initCounters(); } } else { - throw runtime_error("Unknown PFC WD action"); + SWSS_LOG_ERROR("Unknown PFC WD action"); } } else if (event == "restore") { - entry->second.handler = nullptr; + if (entry->second.handler != nullptr) + { + SWSS_LOG_NOTICE( + "PFC Watchdog storm restored on port %s, queue index %d, queue id 0x%lx and port id 0x%lx.", + entry->second.portAlias.c_str(), + entry->second.index, + entry->first, + entry->second.portId); + + entry->second.handler->commitCounters(); + entry->second.handler = nullptr; + } } else { @@ -516,6 +830,21 @@ void PfcWdSwOrch::doTask(swss::NotificationConsumer } } +template +void PfcWdSwOrch::doTask(SelectableTimer &timer) +{ + SWSS_LOG_ENTER(); + + for (auto& handlerPair : m_entryMap) + { + if (handlerPair.second.handler != nullptr) + { + handlerPair.second.handler->commitCounters(true); + } + } + +} + // Trick to keep member functions in a separate file template class PfcWdSwOrch; template class PfcWdSwOrch; diff --git a/orchagent/pfcwdorch.h b/orchagent/pfcwdorch.h index 4be0718526..fada367689 100644 --- a/orchagent/pfcwdorch.h +++ b/orchagent/pfcwdorch.h @@ -4,8 +4,9 @@ #include "orch.h" #include "port.h" #include "pfcactionhandler.h" -#include "producerstatetable.h" +#include "producertable.h" #include "notificationconsumer.h" +#include "timer.h" extern "C" { #include "sai.h" @@ -43,9 +44,10 @@ class PfcWdOrch: public Orch static PfcWdAction deserializeAction(const string& key); static string serializeAction(const PfcWdAction &action); -private: - void createEntry(const string& key, const vector& data); + + virtual void createEntry(const string& key, const vector& data); void deleteEntry(const string& name); +private: shared_ptr m_countersDb = nullptr; shared_ptr
m_countersTable = nullptr; @@ -68,6 +70,8 @@ class PfcWdSwOrch: public PfcWdOrch uint32_t detectionTime, uint32_t restorationTime, PfcWdAction action); virtual bool stopWdOnPort(const Port& port); + void createEntry(const string& key, const vector& data); + virtual void doTask(SelectableTimer &timer); //XXX Add port/queue state change event handlers private: struct PfcWdQueueEntry @@ -75,11 +79,13 @@ class PfcWdSwOrch: public PfcWdOrch PfcWdQueueEntry( PfcWdAction action, sai_object_id_t port, - uint8_t idx); + uint8_t idx, + string alias); PfcWdAction action = PfcWdAction::PFC_WD_ACTION_UNKNOWN; sai_object_id_t portId = SAI_NULL_OBJECT_ID; uint8_t index = 0; + string portAlias; shared_ptr handler = { nullptr }; }; @@ -90,18 +96,25 @@ class PfcWdSwOrch: public PfcWdOrch void unregisterFromWdDb(const Port& port); void doTask(swss::NotificationConsumer &wdNotification); + string filterPfcCounters(string counters, set& losslessTc); + string getFlexCounterTableKey(string s); + + void disableBigRedSwitchMode(); + void enableBigRedSwitchMode(); + void setBigRedSwitchMode(string value); + map m_entryMap; + map m_brsEntryMap; const vector c_portStatIds; const vector c_queueStatIds; const vector c_queueAttrIds; - shared_ptr m_pfcWdDb = nullptr; - shared_ptr m_pfcWdTable = nullptr; - - atomic_bool m_runPfcWdSwOrchThread = { false }; - shared_ptr m_pfcWatchdogThread = nullptr; + shared_ptr m_flexCounterDb = nullptr; + shared_ptr m_flexCounterTable = nullptr; + shared_ptr m_flexCounterGroupTable = nullptr; + bool m_bigRedSwitchFlag = false; int m_pollInterval; }; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index cab6c4df04..90f539e7c9 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -1,4 +1,5 @@ #include "portsorch.h" +#include "neighorch.h" #include #include @@ -14,8 +15,10 @@ #include "logger.h" #include "schema.h" #include "converter.h" -#include "saiserialize.h" - +#include "sai_serialize.h" +#include "crmorch.h" +#include "countercheckorch.h" +#include "notifier.h" extern sai_switch_api_t *sai_switch_api; extern sai_bridge_api_t *sai_bridge_api; @@ -26,10 +29,15 @@ extern sai_hostif_api_t* sai_hostif_api; extern sai_acl_api_t* sai_acl_api; extern sai_queue_api_t *sai_queue_api; extern sai_object_id_t gSwitchId; +extern NeighOrch *gNeighOrch; +extern CrmOrch *gCrmOrch; #define VLAN_PREFIX "Vlan" #define DEFAULT_VLAN_ID 1 -#define FLEX_STAT_COUNTER_POLL_MSECS "1000" +#define PORT_FLEX_STAT_COUNTER_POLL_MSECS "1000" +#define QUEUE_FLEX_STAT_COUNTER_POLL_MSECS "10000" +#define PORT_STAT_COUNTER_FLEX_COUNTER_GROUP "PORT_STAT_COUNTER" +#define QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP "QUEUE_STAT_COUNTER" static map fec_mode_map = { @@ -38,12 +46,57 @@ static map fec_mode_map = { "fc", SAI_PORT_FEC_MODE_FC } }; +const vector portStatIds = +{ + SAI_PORT_STAT_IF_IN_OCTETS, + SAI_PORT_STAT_IF_IN_UCAST_PKTS, + SAI_PORT_STAT_IF_IN_NON_UCAST_PKTS, + SAI_PORT_STAT_IF_IN_DISCARDS, + SAI_PORT_STAT_IF_IN_ERRORS, + SAI_PORT_STAT_IF_IN_UNKNOWN_PROTOS, + SAI_PORT_STAT_IF_OUT_OCTETS, + SAI_PORT_STAT_IF_OUT_UCAST_PKTS, + SAI_PORT_STAT_IF_OUT_NON_UCAST_PKTS, + SAI_PORT_STAT_IF_OUT_DISCARDS, + SAI_PORT_STAT_IF_OUT_ERRORS, + SAI_PORT_STAT_IF_OUT_QLEN, + SAI_PORT_STAT_IF_IN_MULTICAST_PKTS, + SAI_PORT_STAT_IF_IN_BROADCAST_PKTS, + SAI_PORT_STAT_IF_OUT_MULTICAST_PKTS, + SAI_PORT_STAT_IF_OUT_BROADCAST_PKTS, + SAI_PORT_STAT_ETHER_RX_OVERSIZE_PKTS, + SAI_PORT_STAT_ETHER_TX_OVERSIZE_PKTS, + SAI_PORT_STAT_PFC_0_TX_PKTS, + SAI_PORT_STAT_PFC_1_TX_PKTS, + SAI_PORT_STAT_PFC_2_TX_PKTS, + SAI_PORT_STAT_PFC_3_TX_PKTS, + SAI_PORT_STAT_PFC_4_TX_PKTS, + SAI_PORT_STAT_PFC_5_TX_PKTS, + SAI_PORT_STAT_PFC_6_TX_PKTS, + SAI_PORT_STAT_PFC_7_TX_PKTS, + SAI_PORT_STAT_PFC_0_RX_PKTS, + SAI_PORT_STAT_PFC_1_RX_PKTS, + SAI_PORT_STAT_PFC_2_RX_PKTS, + SAI_PORT_STAT_PFC_3_RX_PKTS, + SAI_PORT_STAT_PFC_4_RX_PKTS, + SAI_PORT_STAT_PFC_5_RX_PKTS, + SAI_PORT_STAT_PFC_6_RX_PKTS, + SAI_PORT_STAT_PFC_7_RX_PKTS, + SAI_PORT_STAT_PAUSE_RX_PKTS, + SAI_PORT_STAT_PAUSE_TX_PKTS, + SAI_PORT_STAT_ETHER_STATS_TX_NO_ERRORS, + SAI_PORT_STAT_IP_IN_UCAST_PKTS, + SAI_PORT_STAT_ETHER_IN_PKTS_128_TO_255_OCTETS, +}; + static const vector queueStatIds = { SAI_QUEUE_STAT_PACKETS, SAI_QUEUE_STAT_BYTES, SAI_QUEUE_STAT_DROPPED_PACKETS, - SAI_QUEUE_STAT_DROPPED_BYTES + SAI_QUEUE_STAT_DROPPED_BYTES, + SAI_QUEUE_STAT_CURR_OCCUPANCY_BYTES, + SAI_QUEUE_STAT_WATERMARK_BYTES, }; static char* hostif_vlan_tag[] = { @@ -64,7 +117,7 @@ static char* hostif_vlan_tag[] = { * bridge. By design, SONiC switch starts with all bridge ports removed from * default VLAN and all ports removed from .1Q bridge. */ -PortsOrch::PortsOrch(DBConnector *db, vector tableNames) : +PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) : Orch(db, tableNames) { SWSS_LOG_ENTER(); @@ -82,8 +135,16 @@ PortsOrch::PortsOrch(DBConnector *db, vector tableNames) : m_queueIndexTable = unique_ptr
(new Table(m_counter_db.get(), COUNTERS_QUEUE_INDEX_MAP)); m_queueTypeTable = unique_ptr
(new Table(m_counter_db.get(), COUNTERS_QUEUE_TYPE_MAP)); - m_flex_db = shared_ptr(new DBConnector(PFC_WD_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)); - m_flexCounterTable = unique_ptr(new ProducerStateTable(m_flex_db.get(), PFC_WD_STATE_TABLE)); + m_flex_db = shared_ptr(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)); + m_flexCounterTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_TABLE)); + m_flexCounterGroupTable = unique_ptr(new ProducerTable(m_flex_db.get(), FLEX_COUNTER_GROUP_TABLE)); + + vector fields; + fields.emplace_back(POLL_INTERVAL_FIELD, PORT_FLEX_STAT_COUNTER_POLL_MSECS); + m_flexCounterGroupTable->set(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP, fields); + + fields.emplace_back(POLL_INTERVAL_FIELD, QUEUE_FLEX_STAT_COUNTER_POLL_MSECS); + m_flexCounterGroupTable->set(QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP, fields); uint32_t i, j; sai_status_t status; @@ -180,6 +241,12 @@ PortsOrch::PortsOrch(DBConnector *db, vector tableNames) : removeDefaultVlanMembers(); removeDefaultBridgePorts(); + + /* Add port oper status notification support */ + DBConnector *notificationsDb = new DBConnector(ASIC_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + m_portStatusNotificationConsumer = new swss::NotificationConsumer(notificationsDb, "NOTIFICATIONS"); + auto portStatusNotificatier = new Notifier(m_portStatusNotificationConsumer, this); + Orch::addExecutor("PORT_STATUS_NOTIFICATIONS", portStatusNotificatier); } void PortsOrch::removeDefaultVlanMembers() @@ -308,6 +375,13 @@ bool PortsOrch::getPort(sai_object_id_t id, Port &port) return true; } break; + case Port::VLAN: + if (portIter.second.m_vlan_info.vlan_oid == id) + { + port = portIter.second; + return true; + } + break; default: continue; } @@ -434,7 +508,30 @@ bool PortsOrch::bindAclTable(sai_object_id_t id, sai_object_id_t table_oid, sai_ { bool ingress = acl_stage == ACL_STAGE_INGRESS ? true : false; // If port ACL table group does not exist, create one - sai_object_id_t bp_list[] = { SAI_ACL_BIND_POINT_TYPE_PORT }; + + Port p; + if (!getPort(id, p)) + { + return false; + } + + sai_acl_bind_point_type_t bind_type; + switch (p.m_type) { + case Port::PHY: + bind_type = SAI_ACL_BIND_POINT_TYPE_PORT; + break; + case Port::LAG: + bind_type = SAI_ACL_BIND_POINT_TYPE_LAG; + break; + case Port::VLAN: + bind_type = SAI_ACL_BIND_POINT_TYPE_VLAN; + break; + default: + SWSS_LOG_ERROR("Failed to bind ACL table to port %s with unknown type %d", p.m_alias.c_str(), p.m_type); + return false; + } + + sai_object_id_t bp_list[] = { bind_type }; vector group_attrs; sai_attribute_t group_attr; @@ -468,17 +565,60 @@ bool PortsOrch::bindAclTable(sai_object_id_t id, sai_object_id_t table_oid, sai_ port.m_egress_acl_table_group_id = groupOid; } - // Bind this ACL group to port OID - sai_attribute_t port_attr; - port_attr.id = ingress ? SAI_PORT_ATTR_INGRESS_ACL : SAI_PORT_ATTR_EGRESS_ACL; - port_attr.value.oid = groupOid; + gCrmOrch->incCrmAclUsedCounter(CrmResourceType::CRM_ACL_GROUP, (sai_acl_stage_t) group_attr.value.s32, SAI_ACL_BIND_POINT_TYPE_PORT); - status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); - if (status != SAI_STATUS_SUCCESS) + switch (port.m_type) { - SWSS_LOG_ERROR("Failed to bind port %lx(%s) to ACL table group %lx, rv:%d", - port.m_port_id, port.m_alias.c_str(), groupOid, status); - return false; + case Port::PHY: + { + // Bind this ACL group to physical port + sai_attribute_t port_attr; + port_attr.id = ingress ? SAI_PORT_ATTR_INGRESS_ACL : SAI_PORT_ATTR_EGRESS_ACL; + port_attr.value.oid = groupOid; + + status = sai_port_api->set_port_attribute(port.m_port_id, &port_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to bind port %s to ACL table group %lx, rv:%d", + port.m_alias.c_str(), groupOid, status); + return status; + } + break; + } + case Port::LAG: + { + // Bind this ACL group to LAG + sai_attribute_t lag_attr; + lag_attr.id = ingress ? SAI_LAG_ATTR_INGRESS_ACL : SAI_LAG_ATTR_EGRESS_ACL; + lag_attr.value.oid = groupOid; + + status = sai_lag_api->set_lag_attribute(port.m_lag_id, &lag_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to bind LAG %s to ACL table group %lx, rv:%d", + port.m_alias.c_str(), groupOid, status); + return status; + } + break; + } + case Port::VLAN: + // Bind this ACL group to VLAN + sai_attribute_t vlan_attr; + vlan_attr.id = ingress ? SAI_VLAN_ATTR_INGRESS_ACL : SAI_VLAN_ATTR_EGRESS_ACL; + vlan_attr.value.oid = groupOid; + + status = sai_vlan_api->set_vlan_attribute(port.m_vlan_info.vlan_oid, &vlan_attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to bind VLAN %s to ACL table group %lx, rv:%d", + port.m_alias.c_str(), groupOid, status); + return status; + } + + break; + default: + SWSS_LOG_ERROR("Failed to bind %s port with type %d", port.m_alias.c_str(), port.m_type); + return SAI_STATUS_FAILURE; } SWSS_LOG_NOTICE("Create ACL table group and bind port %s to it", port.m_alias.c_str()); @@ -523,32 +663,38 @@ bool PortsOrch::setPortPvid(Port &port, sai_uint32_t pvid) vector portv; if (port.m_type == Port::PHY) { - portv.push_back(port); + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_PORT_VLAN_ID; + attr.value.u32 = pvid; + + 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 set pvid %u to port: %s", attr.value.u32, port.m_alias.c_str()); + return false; + } + SWSS_LOG_NOTICE("Set pvid %u to port: %s", attr.value.u32, port.m_alias.c_str()); } else if (port.m_type == Port::LAG) - { - getLagMember(port, portv); - } - else - { - SWSS_LOG_ERROR("PortsOrch::setPortPvid port type %d not supported", port.m_type); - return false; - } - - for (const auto p: portv) { sai_attribute_t attr; - attr.id = SAI_PORT_ATTR_PORT_VLAN_ID; + attr.id = SAI_LAG_ATTR_PORT_VLAN_ID; attr.value.u32 = pvid; - sai_status_t status = sai_port_api->set_port_attribute(p.m_port_id, &attr); + sai_status_t status = sai_lag_api->set_lag_attribute(port.m_lag_id, &attr); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to set pvid %u to port: %s", attr.value.u32, p.m_alias.c_str()); + SWSS_LOG_ERROR("Failed to set pvid %u to lag: %s", attr.value.u32, port.m_alias.c_str()); return false; } - SWSS_LOG_NOTICE("Set pvid %u to port: %s", attr.value.u32, p.m_alias.c_str()); + SWSS_LOG_NOTICE("Set pvid %u to lag: %s", attr.value.u32, port.m_alias.c_str()); + } + else + { + SWSS_LOG_ERROR("PortsOrch::setPortPvid port type %d not supported", port.m_type); + return false; } + port.m_port_vlan_id = (sai_vlan_id_t)pvid; return true; } @@ -642,7 +788,9 @@ bool PortsOrch::validatePortSpeed(sai_object_id_t port_id, sai_uint32_t speed) } // TODO: change to macro SAI_STATUS_IS_ATTR_NOT_SUPPORTED once it is fixed in SAI // https://github.com/opencomputeproject/SAI/pull/710 - else if (((status) & (~0xFFFF)) == SAI_STATUS_ATTR_NOT_SUPPORTED_0) + else if ((((status) & (~0xFFFF)) == SAI_STATUS_ATTR_NOT_SUPPORTED_0) || + (((status) & (~0xFFFF)) == SAI_STATUS_ATTR_NOT_IMPLEMENTED_0) || + (status == SAI_STATUS_NOT_IMPLEMENTED)) { // unable to validate speed if attribute is not supported on platform // assuming input value is correct @@ -758,23 +906,30 @@ bool PortsOrch::setHostIntfsOperStatus(sai_object_id_t port_id, bool up) for (auto it = m_portList.begin(); it != m_portList.end(); it++) { - if (it->second.m_port_id == port_id) + if (it->second.m_port_id != port_id) { - sai_attribute_t attr; - attr.id = SAI_HOSTIF_ATTR_OPER_STATUS; - attr.value.booldata = up; + continue; + } - sai_status_t status = sai_hostif_api->set_hostif_attribute(it->second.m_hif_id, &attr); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_WARN("Failed to set operation status %s to host interface %s", - up ? "UP" : "DOWN", it->second.m_alias.c_str()); - return false; - } - SWSS_LOG_NOTICE("Set operation status %s to host interface %s", - up ? "UP" : "DOWN", it->second.m_alias.c_str()); - return true; + sai_attribute_t attr; + attr.id = SAI_HOSTIF_ATTR_OPER_STATUS; + attr.value.booldata = up; + + sai_status_t status = sai_hostif_api->set_hostif_attribute(it->second.m_hif_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_WARN("Failed to set operation status %s to host interface %s", + up ? "UP" : "DOWN", it->second.m_alias.c_str()); + return false; + } + SWSS_LOG_NOTICE("Set operation status %s to host interface %s", + up ? "UP" : "DOWN", it->second.m_alias.c_str()); + if (gNeighOrch->ifChangeInformNextHop(it->second.m_alias, up) == false) + { + SWSS_LOG_WARN("Inform nexthop operation failed for interface %s", + it->second.m_alias.c_str()); } + return true; } return false; } @@ -858,6 +1013,16 @@ bool PortsOrch::removePort(sai_object_id_t port_id) return true; } +string PortsOrch::getPortFlexCounterTableKey(string key) +{ + return string(PORT_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; +} + +string PortsOrch::getQueueFlexCounterTableKey(string key) +{ + return string(QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP) + ":" + key; +} + bool PortsOrch::initPort(const string &alias, const set &lane_set) { SWSS_LOG_ENTER(); @@ -891,18 +1056,17 @@ bool PortsOrch::initPort(const string &alias, const set &lane_set) m_counterTable->set("", fields); /* Add port to flex_counter for updating stat counters */ - string key = sai_serialize_object_id(p.m_port_id) + ":" + FLEX_STAT_COUNTER_POLL_MSECS; - + string key = getPortFlexCounterTableKey(sai_serialize_object_id(p.m_port_id)); std::string delimiter = ""; std::ostringstream counters_stream; - for (int cntr = SAI_PORT_STAT_IF_IN_OCTETS; cntr <= SAI_PORT_STAT_PFC_7_ON2OFF_RX_PKTS; ++cntr) + for (const auto &id: portStatIds) { - counters_stream << delimiter << sai_serialize_port_stat(static_cast(cntr)); + counters_stream << delimiter << sai_serialize_port_stat(id); delimiter = ","; } fields.clear(); - fields.emplace_back(PFC_WD_PORT_COUNTER_ID_LIST, counters_stream.str()); + fields.emplace_back(PORT_COUNTER_ID_LIST, counters_stream.str()); m_flexCounterTable->set(key, fields); @@ -1172,6 +1336,7 @@ void PortsOrch::doPortTask(Consumer &consumer) if (setPortMtu(p.m_port_id, mtu)) { p.m_mtu = mtu; + m_portList[alias] = p; SWSS_LOG_NOTICE("Set port %s MTU to %u", alias.c_str(), mtu); } else @@ -1676,7 +1841,7 @@ void PortsOrch::initializeQueues(Port &port) queueTypeVector.push_back(queueTypeTuple); } - string key = sai_serialize_object_id(port.m_queue_ids[queueIndex]) + ":" + FLEX_STAT_COUNTER_POLL_MSECS; + string key = getQueueFlexCounterTableKey(sai_serialize_object_id(port.m_queue_ids[queueIndex])); std::string delimiter = ""; std::ostringstream counters_stream; @@ -1687,7 +1852,7 @@ void PortsOrch::initializeQueues(Port &port) } vector fieldValues; - fieldValues.emplace_back(PFC_WD_QUEUE_COUNTER_ID_LIST, counters_stream.str()); + fieldValues.emplace_back(QUEUE_COUNTER_ID_LIST, counters_stream.str()); m_flexCounterTable->set(key, fieldValues); } @@ -1767,6 +1932,8 @@ bool PortsOrch::initializePort(Port &p) vector.push_back(tuple); m_portTable->set(p.m_alias, vector); + CounterCheckOrch::getInstance().addPort(p); + return true; } @@ -1786,7 +1953,7 @@ bool PortsOrch::addHostIntfs(Port &port, string alias, sai_object_id_t &host_int attrs.push_back(attr); attr.id = SAI_HOSTIF_ATTR_NAME; - strncpy((char *)&attr.value.chardata, alias.c_str(), HOSTIF_NAME_SIZE); + strncpy((char *)&attr.value.chardata, alias.c_str(), SAI_HOSTIF_NAME_SIZE); attrs.push_back(attr); sai_status_t status = sai_hostif_api->create_hostif(&host_intfs_id, gSwitchId, (uint32_t)attrs.size(), attrs.data()); @@ -1963,6 +2130,22 @@ bool PortsOrch::removeVlan(Port vlan) return true; } +bool PortsOrch::getVlanByVlanId(sai_vlan_id_t vlan_id, Port &vlan) +{ + SWSS_LOG_ENTER(); + + for (auto &it: m_portList) + { + if (it.second.m_type == Port::VLAN && it.second.m_vlan_info.vlan_id == vlan_id) + { + vlan = it.second; + return true; + } + } + + return false; +} + bool PortsOrch::addVlanMember(Port &vlan, Port &port, string &tagging_mode) { SWSS_LOG_ENTER(); @@ -2138,6 +2321,12 @@ bool PortsOrch::addLagMember(Port &lag, Port &port) { SWSS_LOG_ENTER(); + sai_uint32_t pvid; + if (getPortPvid(lag, pvid)) + { + setPortPvid (port, pvid); + } + sai_attribute_t attr; vector attrs; @@ -2178,11 +2367,6 @@ bool PortsOrch::addLagMember(Port &lag, Port &port) return false; } } - sai_uint32_t pvid; - if (getPortPvid(lag, pvid)) - { - setPortPvid (port, pvid); - } LagMemberUpdate update = { lag, port, true }; notify(SUBJECT_TYPE_LAG_MEMBER_CHANGE, static_cast(&update)); @@ -2224,3 +2408,46 @@ bool PortsOrch::removeLagMember(Port &lag, Port &port) return true; } + +void PortsOrch::doTask(NotificationConsumer &consumer) +{ + SWSS_LOG_ENTER(); + + /* Wait for all ports to be initialized */ + if (!isInitDone()) + { + return; + } + + std::string op; + std::string data; + std::vector values; + + consumer.pop(op, data, values); + + if (&consumer != m_portStatusNotificationConsumer) + { + return; + } + + if (op == "port_state_change") + { + uint32_t count; + sai_port_oper_status_notification_t *portoperstatus = nullptr; + + sai_deserialize_port_oper_status_ntf(data, count, &portoperstatus); + + for (uint32_t i = 0; i < count; i++) + { + sai_object_id_t id = portoperstatus[i].port_id; + sai_port_oper_status_t status = portoperstatus[i].port_state; + + SWSS_LOG_NOTICE("Get port state change notification id:%lx status:%d", id, status); + + this->updateDbPortOperStatus(id, status); + this->setHostIntfsOperStatus(id, status == SAI_PORT_OPER_STATUS_UP); + } + + sai_deserialize_free_port_oper_status_ntf(count, portoperstatus); + } +} diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index e5ed688eac..ae2cb06f12 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -8,7 +8,7 @@ #include "port.h" #include "observer.h" #include "macaddress.h" -#include "producerstatetable.h" +#include "producertable.h" #define FCS_LEN 4 #define VLAN_TAG_LEN 4 @@ -42,7 +42,7 @@ struct VlanMemberUpdate class PortsOrch : public Orch, public Subject { public: - PortsOrch(DBConnector *db, vector tableNames); + PortsOrch(DBConnector *db, vector &tableNames); bool isInitDone(); @@ -53,6 +53,7 @@ class PortsOrch : public Orch, public Subject bool getPortByBridgePortId(sai_object_id_t bridge_port_id, Port &port); void setPort(string alias, Port port); void getCpuPort(Port &port); + bool getVlanByVlanId(sai_vlan_id_t vlan_id, Port &vlan); bool setHostIntfsOperStatus(sai_object_id_t id, bool up); void updateDbPortOperStatus(sai_object_id_t id, sai_port_oper_status_t status); @@ -64,7 +65,11 @@ class PortsOrch : public Orch, public Subject unique_ptr
m_queuePortTable; unique_ptr
m_queueIndexTable; unique_ptr
m_queueTypeTable; - unique_ptr m_flexCounterTable; + unique_ptr m_flexCounterTable; + unique_ptr m_flexCounterGroupTable; + + std::string getQueueFlexCounterTableKey(std::string s); + std::string getPortFlexCounterTableKey(std::string s); shared_ptr m_counter_db; shared_ptr m_flex_db; @@ -83,6 +88,8 @@ class PortsOrch : public Orch, public Subject map, tuple> m_lanesAliasSpeedMap; map m_portList; + NotificationConsumer* m_portStatusNotificationConsumer; + void doTask(Consumer &consumer); void doPortTask(Consumer &consumer); void doVlanTask(Consumer &consumer); @@ -90,6 +97,8 @@ class PortsOrch : public Orch, public Subject void doLagTask(Consumer &consumer); void doLagMemberTask(Consumer &consumer); + void doTask(NotificationConsumer &consumer); + void removeDefaultVlanMembers(); void removeDefaultBridgePorts(); diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp index a71284e327..23f0e9f0c9 100644 --- a/orchagent/qosorch.cpp +++ b/orchagent/qosorch.cpp @@ -1,6 +1,7 @@ #include "tokenize.h" #include "qosorch.h" #include "logger.h" +#include "crmorch.h" #include #include @@ -20,6 +21,7 @@ extern sai_acl_api_t* sai_acl_api; extern PortsOrch *gPortsOrch; extern sai_object_id_t gSwitchId; +extern CrmOrch *gCrmOrch; map ecn_map = { {"ecn_none", SAI_ECN_MARK_MODE_NONE}, @@ -677,6 +679,8 @@ sai_object_id_t QosOrch::initSystemAclTable() } SWSS_LOG_NOTICE("Create a system ACL table for ECN coloring"); + gCrmOrch->incCrmAclUsedCounter(CrmResourceType::CRM_ACL_TABLE, (sai_acl_stage_t) attr.value.s32, SAI_ACL_BIND_POINT_TYPE_PORT); + for (auto& pair: gPortsOrch->getAllPorts()) { auto& port = pair.second; @@ -738,6 +742,8 @@ void QosOrch::initAclEntryForEcn(sai_object_id_t acl_table_id, sai_uint32_t prio throw runtime_error("Failed to create a system ACL entry for ECN coloring"); } SWSS_LOG_INFO("Create a system ACL entry for ECN coloring"); + + gCrmOrch->incCrmAclTableUsedCounter(CrmResourceType::CRM_ACL_ENTRY, acl_table_id); } void QosOrch::initTableHandlers() @@ -869,106 +875,131 @@ task_process_status QosOrch::handleSchedulerTable(Consumer& consumer) return task_process_status::task_success; } -bool QosOrch::applySchedulerToQueueSchedulerGroup(Port &port, size_t queue_ind, sai_object_id_t scheduler_profile_id) +sai_object_id_t QosOrch::getSchedulerGroup(const Port &port, const sai_object_id_t queue_id) { SWSS_LOG_ENTER(); - sai_attribute_t attr; - sai_status_t sai_status; - sai_object_id_t queue_id; - vector groups; - vector child_groups; - uint32_t groups_count = 0; - - if (port.m_queue_ids.size() <= queue_ind) - { - SWSS_LOG_ERROR("Invalid queue index specified:%zd", queue_ind); - return false; - } - queue_id = port.m_queue_ids[queue_ind]; - - /* Get max child groups count */ - attr.id = SAI_SWITCH_ATTR_QOS_MAX_NUMBER_OF_CHILDS_PER_SCHEDULER_GROUP; - sai_status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); - if (SAI_STATUS_SUCCESS != sai_status) - { - SWSS_LOG_ERROR("Failed to get number of childs per scheduler group for port:%s", port.m_alias.c_str()); - return false; - } - child_groups.resize(attr.value.u32); - - /* Get max sched groups count */ - attr.id = SAI_PORT_ATTR_QOS_NUMBER_OF_SCHEDULER_GROUPS; - sai_status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); - if (SAI_STATUS_SUCCESS != sai_status) - { - SWSS_LOG_ERROR("Failed to get number of scheduler groups for port:%s", port.m_alias.c_str()); - return false; - } - /* Get total groups list on the port */ - groups_count = attr.value.u32; - groups.resize(groups_count); - - attr.id = SAI_PORT_ATTR_QOS_SCHEDULER_GROUP_LIST; - attr.value.objlist.list = groups.data(); - attr.value.objlist.count = groups_count; - sai_status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); - if (SAI_STATUS_SUCCESS != sai_status) - { - SWSS_LOG_ERROR("Failed to get scheduler group list for port:%s", port.m_alias.c_str()); - return false; - } + sai_attribute_t attr; + sai_status_t sai_status; - /* Lookup groups to which queue belongs */ - for (uint32_t ii = 0; ii < groups_count ; ii++) + const auto it = m_scheduler_group_port_info.find(port.m_port_id); + if (it == m_scheduler_group_port_info.end()) { - uint32_t child_count = 0; - - attr.id = SAI_SCHEDULER_GROUP_ATTR_CHILD_COUNT;//Number of queues/groups childs added to scheduler group - sai_status = sai_scheduler_group_api->get_scheduler_group_attribute(groups[ii], 1, &attr); - child_count = attr.value.u32; + /* Get max sched groups count */ + attr.id = SAI_PORT_ATTR_QOS_NUMBER_OF_SCHEDULER_GROUPS; + sai_status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); if (SAI_STATUS_SUCCESS != sai_status) { - SWSS_LOG_ERROR("Failed to get child count for scheduler group:0x%lx of port:%s", groups[ii], port.m_alias.c_str()); - return false; + SWSS_LOG_ERROR("Failed to get number of scheduler groups for port:%s", port.m_alias.c_str()); + return SAI_NULL_OBJECT_ID; } - // skip this iteration if there're no children in this group - if (child_count == 0) - { - continue; - } + /* Get total groups list on the port */ + uint32_t groups_count = attr.value.u32; + std::vector groups(groups_count); - attr.id = SAI_SCHEDULER_GROUP_ATTR_CHILD_LIST; - attr.value.objlist.list = child_groups.data(); - attr.value.objlist.count = child_count; - sai_status = sai_scheduler_group_api->get_scheduler_group_attribute(groups[ii], 1, &attr); + attr.id = SAI_PORT_ATTR_QOS_SCHEDULER_GROUP_LIST; + attr.value.objlist.list = groups.data(); + attr.value.objlist.count = groups_count; + sai_status = sai_port_api->get_port_attribute(port.m_port_id, 1, &attr); if (SAI_STATUS_SUCCESS != sai_status) { - SWSS_LOG_ERROR("Failed to get child list for scheduler group:0x%lx of port:%s", groups[ii], port.m_alias.c_str()); - return false; + SWSS_LOG_ERROR("Failed to get scheduler group list for port:%s", port.m_alias.c_str()); + return SAI_NULL_OBJECT_ID; } - for (uint32_t jj = 0; jj < child_count; jj++) + m_scheduler_group_port_info[port.m_port_id] = { + .groups = std::move(groups), + .child_groups = std::vector>(groups_count) + }; + } + + /* Lookup groups to which queue belongs */ + const auto& groups = m_scheduler_group_port_info[port.m_port_id].groups; + for (uint32_t ii = 0; ii < groups.size() ; ii++) + { + const auto& group_id = groups[ii]; + const auto& child_groups_per_group = m_scheduler_group_port_info[port.m_port_id].child_groups[ii]; + if (child_groups_per_group.empty()) { - if (child_groups[jj] != queue_id) + attr.id = SAI_SCHEDULER_GROUP_ATTR_CHILD_COUNT;//Number of queues/groups childs added to scheduler group + sai_status = sai_scheduler_group_api->get_scheduler_group_attribute(group_id, 1, &attr); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed to get child count for scheduler group:0x%lx of port:%s", group_id, port.m_alias.c_str()); + return SAI_NULL_OBJECT_ID; + } + + uint32_t child_count = attr.value.u32; + vector child_groups(child_count); + + // skip this iteration if there're no children in this group + if (child_count == 0) { continue; } - attr.id = SAI_SCHEDULER_GROUP_ATTR_SCHEDULER_PROFILE_ID; - attr.value.oid = scheduler_profile_id; - sai_status = sai_scheduler_group_api->set_scheduler_group_attribute(groups[ii], &attr); + attr.id = SAI_SCHEDULER_GROUP_ATTR_CHILD_LIST; + attr.value.objlist.list = child_groups.data(); + attr.value.objlist.count = child_count; + sai_status = sai_scheduler_group_api->get_scheduler_group_attribute(group_id, 1, &attr); if (SAI_STATUS_SUCCESS != sai_status) { - SWSS_LOG_ERROR("Failed applying scheduler profile:0x%lx to scheduler group:0x%lx, port:%s", scheduler_profile_id, groups[ii], port.m_alias.c_str()); - return false; + SWSS_LOG_ERROR("Failed to get child list for scheduler group:0x%lx of port:%s", group_id, port.m_alias.c_str()); + return SAI_NULL_OBJECT_ID; + } + + m_scheduler_group_port_info[port.m_port_id].child_groups[ii] = std::move(child_groups); + } + + for (const auto& child_group_id: child_groups_per_group) + { + if (child_group_id == queue_id) + { + return group_id; } - SWSS_LOG_DEBUG("port:%s, scheduler_profile_id:0x%lx applied to scheduler group:0x%lx", port.m_alias.c_str(), scheduler_profile_id, groups[ii]); - return true; } } - return false; + + return SAI_NULL_OBJECT_ID; +} + +bool QosOrch::applySchedulerToQueueSchedulerGroup(Port &port, size_t queue_ind, sai_object_id_t scheduler_profile_id) +{ + SWSS_LOG_ENTER(); + + if (port.m_queue_ids.size() <= queue_ind) + { + SWSS_LOG_ERROR("Invalid queue index specified:%zd", queue_ind); + return false; + } + + const sai_object_id_t queue_id = port.m_queue_ids[queue_ind]; + + const sai_object_id_t group_id = getSchedulerGroup(port, queue_id); + if(group_id == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_ERROR("Failed to find a scheduler group for port: %s queue: %lu", port.m_alias.c_str(), queue_ind); + return false; + } + + /* Apply scheduler profile to all port groups */ + sai_attribute_t attr; + sai_status_t sai_status; + + attr.id = SAI_SCHEDULER_GROUP_ATTR_SCHEDULER_PROFILE_ID; + attr.value.oid = scheduler_profile_id; + + sai_status = sai_scheduler_group_api->set_scheduler_group_attribute(group_id, &attr); + if (SAI_STATUS_SUCCESS != sai_status) + { + SWSS_LOG_ERROR("Failed applying scheduler profile:0x%lx to scheduler group:0x%lx, port:%s", scheduler_profile_id, group_id, port.m_alias.c_str()); + return false; + } + + SWSS_LOG_DEBUG("port:%s, scheduler_profile_id:0x%lx applied to scheduler group:0x%lx", port.m_alias.c_str(), scheduler_profile_id, group_id); + + return true; } bool QosOrch::applyWredProfileToQueue(Port &port, size_t queue_ind, sai_object_id_t sai_wred_profile) diff --git a/orchagent/qosorch.h b/orchagent/qosorch.h index 3c39d37012..03b70a7dbc 100644 --- a/orchagent/qosorch.h +++ b/orchagent/qosorch.h @@ -2,6 +2,8 @@ #define SWSS_QOSORCH_H #include +#include +#include #include "orch.h" #include "portsorch.h" @@ -132,6 +134,8 @@ class QosOrch : public Orch task_process_status handleQueueTable(Consumer& consumer); task_process_status handleWredProfileTable(Consumer& consumer); + 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); @@ -140,5 +144,13 @@ class QosOrch : public Orch private: qos_table_handler_map m_qos_handler_map; + + struct SchedulerGroupPortInfo_t + { + std::vector groups; + std::vector> child_groups; + }; + + std::unordered_map m_scheduler_group_port_info; }; #endif /* SWSS_QOSORCH_H */ diff --git a/orchagent/request_parser.cpp b/orchagent/request_parser.cpp new file mode 100644 index 0000000000..caa1779f4a --- /dev/null +++ b/orchagent/request_parser.cpp @@ -0,0 +1,201 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sai.h" +#include "macaddress.h" +#include "orch.h" +#include "request_parser.h" + + +void Request::parse(const KeyOpFieldsValuesTuple& request) +{ + if (is_parsed_) + { + throw std::logic_error("The parser already has a parsed request"); + } + + parseOperation(request); + parseKey(request); + parseAttrs(request); + + is_parsed_ = true; +} + +void Request::clear() +{ + operation_.clear(); + full_key_.clear(); + attr_names_.clear(); + key_item_strings_.clear(); + key_item_mac_addresses_.clear(); + attr_item_strings_.clear(); + attr_item_bools_.clear(); + attr_item_mac_addresses_.clear(); + attr_item_packet_actions_.clear(); + + is_parsed_ = false; +} + +void Request::parseOperation(const KeyOpFieldsValuesTuple& request) +{ + operation_ = kfvOp(request); + if (operation_ != SET_COMMAND && operation_ != DEL_COMMAND) + { + throw std::invalid_argument(std::string("Wrong operation: ") + operation_); + } +} + +void Request::parseKey(const KeyOpFieldsValuesTuple& request) +{ + full_key_ = kfvKey(request); + + // split the key by separator + std::vector key_items; + size_t f_position = 0; + size_t e_position = full_key_.find(key_separator_); + while (e_position != std::string::npos) + { + key_items.push_back(full_key_.substr(f_position, e_position - f_position)); + f_position = e_position + 1; + e_position = full_key_.find(key_separator_, f_position); + } + key_items.push_back(full_key_.substr(f_position, full_key_.length())); + + if (key_items.size() != number_of_key_items_) + { + throw std::invalid_argument(std::string("Wrong number of key items. Expected ") + + std::to_string(number_of_key_items_) + + std::string(" item(s). Key: '") + + full_key_ + + std::string("'")); + } + + // check types of the key items + for (int i = 0; i < static_cast(number_of_key_items_); i++) + { + switch(request_description_.key_item_types[i]) + { + case REQ_T_STRING: + key_item_strings_[i] = key_items[i]; + break; + case REQ_T_MAC_ADDRESS: + key_item_mac_addresses_[i] = parseMacAddress(key_items[i]); + break; + default: + throw std::logic_error(std::string("Not implemented key type parser. Key '") + + full_key_ + + std::string("'. Key item:") + + key_items[i]); + } + } +} + +void Request::parseAttrs(const KeyOpFieldsValuesTuple& request) +{ + const auto not_found = std::end(request_description_.attr_item_types); + + for (auto i = kfvFieldsValues(request).begin(); + i != kfvFieldsValues(request).end(); i++) + { + if (fvField(*i) == "empty") + { + // if name of the attribute is 'empty', just skip it. + // it's used when we don't have any attributes, but we have to provide one for redis + continue; + } + const auto item = request_description_.attr_item_types.find(fvField(*i)); + if (item == not_found) + { + throw std::invalid_argument(std::string("Unknown attribute name: ") + fvField(*i)); + } + attr_names_.insert(fvField(*i)); + switch(item->second) + { + case REQ_T_STRING: + attr_item_strings_[fvField(*i)] = fvValue(*i); + break; + case REQ_T_BOOL: + attr_item_bools_[fvField(*i)] = parseBool(fvValue(*i)); + break; + case REQ_T_MAC_ADDRESS: + attr_item_mac_addresses_[fvField(*i)] = parseMacAddress(fvValue(*i)); + break; + case REQ_T_PACKET_ACTION: + attr_item_packet_actions_[fvField(*i)] = parsePacketAction(fvValue(*i)); + break; + default: + throw std::logic_error(std::string("Not implemented attribute type parser for attribute:") + fvField(*i)); + } + } + + if (operation_ == DEL_COMMAND && attr_names_.size() > 0) + { + throw std::invalid_argument("Delete operation request contains attributes"); + } + + if (operation_ == SET_COMMAND) + { + for (const auto& attr: request_description_.mandatory_attr_items) + { + if (attr_names_.find(attr) == std::end(attr_names_)) + { + throw std::invalid_argument(std::string("Mandatory attribute '") + attr + std::string("' not found")); + } + } + } +} + +bool Request::parseBool(const std::string& str) +{ + if (str == "true") + { + return true; + } + + if (str == "false") + { + return false; + } + + throw std::invalid_argument(std::string("Can't parse boolean value '") + str + std::string("'")); +} + +MacAddress Request::parseMacAddress(const std::string& str) +{ + uint8_t mac[ETHER_ADDR_LEN]; + + if (!MacAddress::parseMacString(str, mac)) + { + throw std::invalid_argument(std::string("Invalid mac address: ") + str); + } + + return MacAddress(mac); +} + +sai_packet_action_t Request::parsePacketAction(const std::string& str) +{ + std::unordered_map m = { + {"drop", SAI_PACKET_ACTION_DROP}, + {"forward", SAI_PACKET_ACTION_FORWARD}, + {"copy", SAI_PACKET_ACTION_COPY}, + {"copy_cancel", SAI_PACKET_ACTION_COPY_CANCEL}, + {"trap", SAI_PACKET_ACTION_TRAP}, + {"log", SAI_PACKET_ACTION_LOG}, + {"deny", SAI_PACKET_ACTION_DENY}, + {"transit", SAI_PACKET_ACTION_TRANSIT}, + }; + + const auto found = m.find(str); + if (found == std::end(m)) + { + throw std::invalid_argument(std::string("Wrong packet action attribute value '") + str + std::string("'")); + } + + return found->second; +} diff --git a/orchagent/request_parser.h b/orchagent/request_parser.h new file mode 100644 index 0000000000..552c36e667 --- /dev/null +++ b/orchagent/request_parser.h @@ -0,0 +1,114 @@ +#ifndef __REQUEST_PARSER_H +#define __REQUEST_PARSER_H + +typedef enum _request_types_t +{ + REQ_T_NOT_USED, + REQ_T_BOOL, + REQ_T_STRING, + REQ_T_MAC_ADDRESS, + REQ_T_PACKET_ACTION, +} request_types_t; + +typedef struct _request_description +{ + std::vector key_item_types; + std::unordered_map attr_item_types; + std::vector mandatory_attr_items; +} request_description_t; + +class Request +{ +public: + void parse(const KeyOpFieldsValuesTuple& request); + void clear(); + + const std::string& getOperation() const + { + assert(is_parsed_); + return operation_; + } + + const std::string& getFullKey() const + { + assert(is_parsed_); + return full_key_; + } + + const std::string& getKeyString(int position) const + { + assert(is_parsed_); + return key_item_strings_.at(position); + } + + const MacAddress& getKeyMacAddress(int position) const + { + assert(is_parsed_); + return key_item_mac_addresses_.at(position); + } + + const std::unordered_set& getAttrFieldNames() const + { + assert(is_parsed_); + return attr_names_; + } + + const std::string& getAttrString(const std::string& attr_name) const + { + assert(is_parsed_); + return attr_item_strings_.at(attr_name); + } + + bool getAttrBool(const std::string& attr_name) const + { + assert(is_parsed_); + return attr_item_bools_.at(attr_name); + } + + const MacAddress& getAttrMacAddress(const std::string& attr_name) const + { + assert(is_parsed_); + return attr_item_mac_addresses_.at(attr_name); + } + + sai_packet_action_t getAttrPacketAction(const std::string& attr_name) const + { + assert(is_parsed_); + return attr_item_packet_actions_.at(attr_name); + } + +protected: + Request(const request_description_t& request_description, const char key_separator) + : request_description_(request_description), + key_separator_(key_separator), + is_parsed_(false), + number_of_key_items_(request_description.key_item_types.size()) + { + } + + +private: + void parseOperation(const KeyOpFieldsValuesTuple& request); + void parseKey(const KeyOpFieldsValuesTuple& request); + void parseAttrs(const KeyOpFieldsValuesTuple& request); + bool parseBool(const std::string& str); + MacAddress parseMacAddress(const std::string& str); + sai_packet_action_t parsePacketAction(const std::string& str); + + const request_description_t& request_description_; + char key_separator_; + bool is_parsed_; + size_t number_of_key_items_; + + std::string operation_; + std::string full_key_; + std::unordered_map key_item_strings_; + std::unordered_map key_item_mac_addresses_; + std::unordered_set attr_names_; + std::unordered_map attr_item_strings_; + std::unordered_map attr_item_bools_; + std::unordered_map attr_item_mac_addresses_; + std::unordered_map attr_item_packet_actions_; +}; + +#endif // __REQUEST_PARSER_H diff --git a/orchagent/routeorch.cpp b/orchagent/routeorch.cpp index 0df8fd1b48..18e8d61769 100644 --- a/orchagent/routeorch.cpp +++ b/orchagent/routeorch.cpp @@ -2,6 +2,7 @@ #include "routeorch.h" #include "logger.h" #include "swssnet.h" +#include "crmorch.h" extern sai_object_id_t gVirtualRouterId; extern sai_object_id_t gSwitchId; @@ -11,13 +12,16 @@ extern sai_route_api_t* sai_route_api; extern sai_switch_api_t* sai_switch_api; extern PortsOrch *gPortsOrch; +extern CrmOrch *gCrmOrch; /* Default maximum number of next hop groups */ #define DEFAULT_NUMBER_OF_ECMP_GROUPS 128 #define DEFAULT_MAX_ECMP_GROUP_SIZE 32 +const int routeorch_pri = 5; + RouteOrch::RouteOrch(DBConnector *db, string tableName, NeighOrch *neighOrch) : - Orch(db, tableName), + Orch(db, tableName, routeorch_pri), m_neighOrch(neighOrch), m_nextHopGroupCount(0), m_resync(false) @@ -73,6 +77,8 @@ RouteOrch::RouteOrch(DBConnector *db, string tableName, NeighOrch *neighOrch) : throw runtime_error("Failed to create IPv4 default route with packet action drop"); } + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); + /* Add default IPv4 route into the m_syncdRoutes */ m_syncdRoutes[default_ip_prefix] = IpAddresses(); @@ -90,6 +96,8 @@ RouteOrch::RouteOrch(DBConnector *db, string tableName, NeighOrch *neighOrch) : throw runtime_error("Failed to create IPv6 default route with packet action drop"); } + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + /* Add default IPv6 route into the m_syncdRoutes */ m_syncdRoutes[v6_default_ip_prefix] = IpAddresses(); @@ -160,6 +168,83 @@ void RouteOrch::detach(Observer *observer, const IpAddress& dstAddr) } } +bool RouteOrch::validnexthopinNextHopGroup(const IpAddress &ipaddr) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t nexthop_id; + sai_status_t status; + + for (auto nhopgroup = m_syncdNextHopGroups.begin(); + nhopgroup != m_syncdNextHopGroups.end(); ++nhopgroup) + { + + if (!(nhopgroup->first.contains(ipaddr))) + { + continue; + } + + vector nhgm_attrs; + sai_attribute_t nhgm_attr; + + nhgm_attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID; + nhgm_attr.value.oid = nhopgroup->second.next_hop_group_id; + nhgm_attrs.push_back(nhgm_attr); + + nhgm_attr.id = SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_ID; + nhgm_attr.value.oid = m_neighOrch->getNextHopId(ipaddr); + nhgm_attrs.push_back(nhgm_attr); + + status = sai_next_hop_group_api->create_next_hop_group_member(&nexthop_id, gSwitchId, + (uint32_t)nhgm_attrs.size(), + nhgm_attrs.data()); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to add next hop member to group %lx: %d\n", + nhopgroup->second.next_hop_group_id, status); + return false; + } + + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER); + nhopgroup->second.nhopgroup_members[ipaddr] = nexthop_id; + } + + return true; +} + +bool RouteOrch::invalidnexthopinNextHopGroup(const IpAddress &ipaddr) +{ + SWSS_LOG_ENTER(); + + sai_object_id_t nexthop_id; + sai_status_t status; + + for (auto nhopgroup = m_syncdNextHopGroups.begin(); + nhopgroup != m_syncdNextHopGroups.end(); ++nhopgroup) + { + + if (!(nhopgroup->first.contains(ipaddr))) + { + continue; + } + + nexthop_id = nhopgroup->second.nhopgroup_members[ipaddr]; + status = sai_next_hop_group_api->remove_next_hop_group_member(nexthop_id); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove next hop member %lx from group %lx: %d\n", + nexthop_id, nhopgroup->second.next_hop_group_id, status); + return false; + } + + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER); + } + + return true; +} + void RouteOrch::doTask(Consumer& consumer) { SWSS_LOG_ENTER(); @@ -430,6 +515,8 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) vector next_hop_ids; set next_hop_set = ipAddresses.getIpAddresses(); + sai_object_id_t next_hop_id; + std::map nhopgroup_members_set; /* Assert each IP address exists in m_syncdNextHops table, * and add the corresponding next_hop_id to next_hop_ids. */ @@ -444,6 +531,7 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) sai_object_id_t next_hop_id = m_neighOrch->getNextHopId(it); next_hop_ids.push_back(next_hop_id); + nhopgroup_members_set[next_hop_id] = it; } sai_attribute_t nhg_attr; @@ -454,8 +542,10 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) nhg_attrs.push_back(nhg_attr); sai_object_id_t next_hop_group_id; - sai_status_t status = sai_next_hop_group_api-> - create_next_hop_group(&next_hop_group_id, gSwitchId, (uint32_t)nhg_attrs.size(), nhg_attrs.data()); + sai_status_t status = sai_next_hop_group_api->create_next_hop_group(&next_hop_group_id, + gSwitchId, + (uint32_t)nhg_attrs.size(), + nhg_attrs.data()); if (status != SAI_STATUS_SUCCESS) { @@ -467,6 +557,8 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) m_nextHopGroupCount ++; SWSS_LOG_NOTICE("Create next hop group %s", ipAddresses.to_string().c_str()); + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP); + NextHopGroupEntry next_hop_group_entry; next_hop_group_entry.next_hop_group_id = next_hop_group_id; @@ -485,8 +577,10 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) nhgm_attrs.push_back(nhgm_attr); sai_object_id_t next_hop_group_member_id; - status = sai_next_hop_group_api-> - create_next_hop_group_member(&next_hop_group_member_id, gSwitchId, (uint32_t)nhgm_attrs.size(), nhgm_attrs.data()); + status = sai_next_hop_group_api->create_next_hop_group_member(&next_hop_group_member_id, + gSwitchId, + (uint32_t)nhgm_attrs.size(), + nhgm_attrs.data()); if (status != SAI_STATUS_SUCCESS) { @@ -496,11 +590,14 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) return false; } + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER); + // Save the membership into next hop structure - next_hop_group_entry.next_hop_group_members.insert(next_hop_group_member_id); + next_hop_group_entry.nhopgroup_members[nhopgroup_members_set.find(nhid)->second] = + next_hop_group_member_id; } - /* Increate the ref_count for the next hops used by the next hop group. */ + /* Increment the ref_count for the next hops used by the next hop group. */ for (auto it : next_hop_set) m_neighOrch->increaseNextHopRefCount(it); @@ -511,6 +608,22 @@ bool RouteOrch::addNextHopGroup(IpAddresses ipAddresses) next_hop_group_entry.ref_count = 0; m_syncdNextHopGroups[ipAddresses] = next_hop_group_entry; + for (auto nhop : next_hop_set) { + if (!m_neighOrch->isNextHopFlagSet(nhop, NHFLAGS_IFDOWN)) { + continue; + } + + next_hop_id = next_hop_group_entry.nhopgroup_members[nhop]; + status = sai_next_hop_group_api->remove_next_hop_group_member(next_hop_id); + + if (status != SAI_STATUS_SUCCESS) { + SWSS_LOG_ERROR("Failed to remove next hop group member %lx: %d\n", + next_hop_id, status); + } + + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER); + } + return true; } @@ -518,39 +631,57 @@ bool RouteOrch::removeNextHopGroup(IpAddresses ipAddresses) { SWSS_LOG_ENTER(); + sai_object_id_t next_hop_group_id; + auto next_hop_group_entry = m_syncdNextHopGroups.find(ipAddresses); sai_status_t status; - assert(hasNextHopGroup(ipAddresses)); - if (m_syncdNextHopGroups[ipAddresses].ref_count == 0) + assert(next_hop_group_entry != m_syncdNextHopGroups.end()); + + if (next_hop_group_entry->second.ref_count != 0) + { + return true; + } + + next_hop_group_id = next_hop_group_entry->second.next_hop_group_id; + SWSS_LOG_NOTICE("Delete next hop group %s", ipAddresses.to_string().c_str()); + + for (auto nhop = next_hop_group_entry->second.nhopgroup_members.begin(); + nhop != next_hop_group_entry->second.nhopgroup_members.end();) { - auto next_hop_group_entry = m_syncdNextHopGroups[ipAddresses]; - sai_object_id_t next_hop_group_id = next_hop_group_entry.next_hop_group_id; - for (auto next_hop_group_member_id: next_hop_group_entry.next_hop_group_members) + if (m_neighOrch->isNextHopFlagSet(nhop->first, NHFLAGS_IFDOWN)) { - status = sai_next_hop_group_api->remove_next_hop_group_member(next_hop_group_member_id); - if (status != SAI_STATUS_SUCCESS) - { - SWSS_LOG_ERROR("Failed to remove next hop group member %lx, rv:%d", next_hop_group_member_id, status); - return false; - } + nhop = next_hop_group_entry->second.nhopgroup_members.erase(nhop); + continue; } - - sai_status_t status = sai_next_hop_group_api->remove_next_hop_group(next_hop_group_id); + status = sai_next_hop_group_api->remove_next_hop_group_member(nhop->second); if (status != SAI_STATUS_SUCCESS) { - SWSS_LOG_ERROR("Failed to remove next hop group %lx, rv:%d", next_hop_group_id, status); + SWSS_LOG_ERROR("Failed to remove next hop group member %lx, rv:%d", + nhop->second, status); return false; } - m_nextHopGroupCount --; + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP_MEMBER); + nhop = next_hop_group_entry->second.nhopgroup_members.erase(nhop); + } - set ip_address_set = ipAddresses.getIpAddresses(); - for (auto it : ip_address_set) - m_neighOrch->decreaseNextHopRefCount(it); + status = sai_next_hop_group_api->remove_next_hop_group(next_hop_group_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove next hop group %lx, rv:%d", next_hop_group_id, status); + return false; + } + + m_nextHopGroupCount --; + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_NEXTHOP_GROUP); - m_syncdNextHopGroups.erase(ipAddresses); + set ip_address_set = ipAddresses.getIpAddresses(); + for (auto it : ip_address_set) + { + m_neighOrch->decreaseNextHopRefCount(it); } + m_syncdNextHopGroups.erase(ipAddresses); return true; } @@ -677,6 +808,15 @@ bool RouteOrch::addRoute(IpPrefix ipPrefix, IpAddresses nextHops) return false; } + if (route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); + } + else + { + gCrmOrch->incCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + } + /* Increase the ref_count for the next hop (group) entry */ increaseNextHopRefCount(nextHops); SWSS_LOG_INFO("Create route %s with next hop(s) %s", @@ -779,8 +919,17 @@ bool RouteOrch::removeRoute(IpPrefix ipPrefix) SWSS_LOG_ERROR("Failed to remove route prefix:%s\n", ipPrefix.to_string().c_str()); return false; } - } + if (route_entry.destination.addr_family == SAI_IP_ADDR_FAMILY_IPV4) + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV4_ROUTE); + } + else + { + gCrmOrch->decCrmResUsedCounter(CrmResourceType::CRM_IPV6_ROUTE); + } + + } /* Remove next hop group entry if ref_count is zero */ auto it_route = m_syncdRoutes.find(ipPrefix); if (it_route != m_syncdRoutes.end()) diff --git a/orchagent/routeorch.h b/orchagent/routeorch.h index b34ac6aa9f..af17b627a4 100644 --- a/orchagent/routeorch.h +++ b/orchagent/routeorch.h @@ -15,11 +15,13 @@ /* Maximum next hop group number */ #define NHGRP_MAX_SIZE 128 +typedef std::map NextHopGroupMembers; + struct NextHopGroupEntry { sai_object_id_t next_hop_group_id; // next hop group id - std::set next_hop_group_members; // next hop group member ids int ref_count; // reference count + NextHopGroupMembers nhopgroup_members; // ids of members indexed by ip address }; struct NextHopUpdate @@ -61,6 +63,9 @@ class RouteOrch : public Orch, public Subject bool addNextHopGroup(IpAddresses); bool removeNextHopGroup(IpAddresses); + bool validnexthopinNextHopGroup(const IpAddress &); + bool invalidnexthopinNextHopGroup(const IpAddress &); + private: NeighOrch *m_neighOrch; diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index d374d13121..58e9eb48c4 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -90,7 +90,7 @@ int test_profile_get_next_value ( return -1; } -const service_method_table_t test_services = { +const sai_service_method_table_t test_services = { test_profile_get_value, test_profile_get_next_value }; @@ -99,7 +99,7 @@ void initSaiApi() { SWSS_LOG_ENTER(); - sai_api_initialize(0, (const service_method_table_t *)&test_services); + sai_api_initialize(0, (const sai_service_method_table_t *)&test_services); sai_api_query(SAI_API_SWITCH, (void **)&sai_switch_api); sai_api_query(SAI_API_BRIDGE, (void **)&sai_bridge_api); diff --git a/orchagent/vrforch.cpp b/orchagent/vrforch.cpp new file mode 100644 index 0000000000..a67e6734bd --- /dev/null +++ b/orchagent/vrforch.cpp @@ -0,0 +1,130 @@ +#include +#include +#include +#include +#include +#include + +#include "sai.h" +#include "macaddress.h" +#include "orch.h" +#include "request_parser.h" +#include "vrforch.h" + +extern sai_virtual_router_api_t* sai_virtual_router_api; +extern sai_object_id_t gSwitchId; + +bool VRFOrch::addOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + vector attrs; + + for (const auto& name: request.getAttrFieldNames()) + { + if (name == "v4") + { + attr.id = SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE; + attr.value.booldata = request.getAttrBool("v4"); + } + else if (name == "v6") + { + attr.id = SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE; + attr.value.booldata = request.getAttrBool("v6"); + } + else if (name == "src_mac") + { + const auto& mac = request.getAttrMacAddress("src_mac"); + attr.id = SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS; + memcpy(attr.value.mac, mac.getMac(), sizeof(sai_mac_t)); + } + else if (name == "ttl_action") + { + attr.id = SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_TTL1_PACKET_ACTION; + attr.value.s32 = request.getAttrPacketAction("ttl_action"); + } + else if (name == "ip_opt_action") + { + attr.id = SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_IP_OPTIONS_PACKET_ACTION; + attr.value.s32 = request.getAttrPacketAction("ip_opt_action"); + } + else if (name == "l3_mc_action") + { + attr.id = SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION; + attr.value.s32 = request.getAttrPacketAction("l3_mc_action"); + } + else + { + SWSS_LOG_ERROR("Logic error: Unknown attribute: %s", name.c_str()); + continue; + } + attrs.push_back(attr); + } + + const std::string& vrf_name = request.getKeyString(0); + auto it = vrf_table_.find(vrf_name); + if (it == std::end(vrf_table_)) + { + // Create a new vrf + sai_object_id_t router_id; + sai_status_t status = sai_virtual_router_api->create_virtual_router(&router_id, + gSwitchId, + static_cast(attrs.size()), + attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create virtual router name: %s, rv: %d", vrf_name.c_str(), status); + return false; + } + + vrf_table_[vrf_name] = router_id; + SWSS_LOG_NOTICE("VRF '%s' was added", vrf_name.c_str()); + } + else + { + // Update an existing vrf + + sai_object_id_t router_id = it->second; + + for (const auto& attr: attrs) + { + sai_status_t status = sai_virtual_router_api->set_virtual_router_attribute(router_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to update virtual router attribute. vrf name: %s, rv: %d", vrf_name.c_str(), status); + return false; + } + } + + SWSS_LOG_NOTICE("VRF '%s' was updated", vrf_name.c_str()); + } + + return true; +} + +bool VRFOrch::delOperation(const Request& request) +{ + SWSS_LOG_ENTER(); + + const std::string& vrf_name = request.getKeyString(0); + if (vrf_table_.find(vrf_name) == std::end(vrf_table_)) + { + SWSS_LOG_ERROR("VRF '%s' doesn't exist", vrf_name.c_str()); + return true; + } + + sai_object_id_t router_id = vrf_table_[vrf_name]; + sai_status_t status = sai_virtual_router_api->remove_virtual_router(router_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove virtual router name: %s, rv:%d", vrf_name.c_str(), status); + return false; + } + + vrf_table_.erase(vrf_name); + + SWSS_LOG_NOTICE("VRF '%s' was removed", vrf_name.c_str()); + + return true; +} diff --git a/orchagent/vrforch.h b/orchagent/vrforch.h new file mode 100644 index 0000000000..49df85492e --- /dev/null +++ b/orchagent/vrforch.h @@ -0,0 +1,52 @@ +#ifndef __VRFORCH_H +#define __VRFORCH_H + +#include "request_parser.h" + +typedef std::unordered_map VRFTable; + +const request_description_t request_description = { + { REQ_T_STRING }, + { + { "v4", REQ_T_BOOL }, + { "v6", REQ_T_BOOL }, + { "src_mac", REQ_T_MAC_ADDRESS }, + { "ttl_action", REQ_T_PACKET_ACTION }, + { "ip_opt_action", REQ_T_PACKET_ACTION }, + { "l3_mc_action", REQ_T_PACKET_ACTION }, + }, + { } // no mandatory attributes +}; + +class VRFRequest : public Request +{ +public: + VRFRequest() : Request(request_description, '|') { } +}; + + +class VRFOrch : public Orch2 +{ +public: + VRFOrch(DBConnector *db, const std::string& tableName) : Orch2(db, tableName, request_) + { + } + + bool isVRFexists(const std::string& name) const + { + return vrf_table_.find(name) != std::end(vrf_table_); + } + + sai_object_id_t getVRFid(const std::string& name) const + { + return vrf_table_.at(name); + } +private: + virtual bool addOperation(const Request& request); + virtual bool delOperation(const Request& request); + + VRFTable vrf_table_; + VRFRequest request_; +}; + +#endif // __VRFORCH_H diff --git a/portsyncd/linksync.cpp b/portsyncd/linksync.cpp index 628285abce..fa64b68711 100644 --- a/portsyncd/linksync.cpp +++ b/portsyncd/linksync.cpp @@ -9,6 +9,7 @@ #include "dbconnector.h" #include "producerstatetable.h" #include "tokenize.h" +#include "exec.h" #include "linkcache.h" #include "portsyncd/linksync.h" @@ -28,10 +29,17 @@ 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, CONFIGDB_TABLE_NAME_SEPARATOR) + m_statePortTable(state_db, STATE_PORT_TABLE_NAME) { /* See the comments for g_portSet in portsyncd.cpp */ for (string port : g_portSet) @@ -49,6 +57,38 @@ LinkSync::LinkSync(DBConnector *appl_db, DBConnector *state_db) : } } } + + struct if_nameindex *if_ni, *idx_p; + if_ni = if_nameindex(); + if (if_ni == NULL) + { + return; + } + + for (idx_p = if_ni; ! (idx_p->if_index == 0 && idx_p->if_name == NULL); idx_p++) + { + string key = idx_p->if_name; + if (key.compare(0, INTFS_PREFIX.length(), INTFS_PREFIX)) + { + continue; + } + + m_ifindexOldNameMap[idx_p->if_index] = key; + + /* Bring down the existing kernel interfaces */ + string cmd, res; + SWSS_LOG_INFO("Bring down old interface %s(%d)", key.c_str(), idx_p->if_index); + cmd = "ip link set " + key + " down"; + try + { + swss::exec(cmd, res); + } + catch (...) + { + /* Ignore error in this flow ; */ + SWSS_LOG_WARN("Failed to bring down old interface %s(%d)", key.c_str(), idx_p->if_index); + } + } } void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj) @@ -99,29 +139,15 @@ void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj) /* In the event of swss restart, it is possible to get netlink messages during bridge * delete, interface delete etc which are part of cleanup. These netlink messages for * the front-panel interface must not be published or it will update the statedb with - * old interface info and result in subsequent failures. A new interface creation shall - * not have master or admin status iff_up. So if the first netlink message comes with these - * values set, it is considered to be happening during a cleanup process. - * Fix to ignore this and any further messages for this ifindex + * old interface info and result in subsequent failures. Ingore all netlink messages + * coming from old interfaces. */ - static std::map m_ifindexOldNameMap; - if (m_ifindexNameMap.find(ifindex) == m_ifindexNameMap.end()) + if (m_ifindexOldNameMap.find(ifindex) != m_ifindexOldNameMap.end()) { - if (master) - { - m_ifindexOldNameMap[ifindex] = key; - SWSS_LOG_INFO("nlmsg type:%d Ignoring for %d, master %d", nlmsg_type, ifindex, master); - return; - } - else if (m_ifindexOldNameMap.find(ifindex) != m_ifindexOldNameMap.end()) - { - if (m_ifindexOldNameMap[ifindex] == key) - { - SWSS_LOG_INFO("nlmsg type:%d Ignoring message for old interface %d", nlmsg_type, ifindex); - return; - } - } + SWSS_LOG_INFO("nlmsg type:%d Ignoring message for old interface %s(%d)", + nlmsg_type, key.c_str(), ifindex); + return; } /* Insert or update the ifindex to key map */ @@ -153,6 +179,7 @@ void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj) vector vector; vector.push_back(tuple); m_statePortTable.set(key, vector); + SWSS_LOG_INFO("Publish %s(ok) to state db", key.c_str()); } m_portTableProducer.set(key, fvVector); diff --git a/portsyncd/linksync.h b/portsyncd/linksync.h index 620554de83..5b31ed9b3c 100644 --- a/portsyncd/linksync.h +++ b/portsyncd/linksync.h @@ -23,6 +23,7 @@ class LinkSync : public NetMsg Table m_portTable, m_statePortTable; std::map m_ifindexNameMap; + std::map m_ifindexOldNameMap; }; } diff --git a/portsyncd/portsyncd.cpp b/portsyncd/portsyncd.cpp index 32386eff89..c49ba3da68 100644 --- a/portsyncd/portsyncd.cpp +++ b/portsyncd/portsyncd.cpp @@ -96,8 +96,8 @@ int main(int argc, char **argv) while (true) { Selectable *temps; - int tempfd, ret; - ret = s.select(&temps, &tempfd, 1); + int ret; + ret = s.select(&temps, 1); if (ret == Select::ERROR) { @@ -169,7 +169,7 @@ void handlePortConfigFromConfigDB(ProducerStateTable &p, DBConnector &cfgDb) { cout << "Get port configuration from ConfigDB..." << endl; - Table table(&cfgDb, CFG_PORT_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR); + Table table(&cfgDb, CFG_PORT_TABLE_NAME); std::vector ovalues; std::vector keys; table.getKeys(keys); @@ -205,17 +205,15 @@ void handlePortConfigFile(ProducerStateTable &p, string file) { if (line.at(0) == '#') { - /* Find out what info is specified in the configuration file */ - for (auto it = header.begin(); it != header.end();) - { - if (line.find(*it) == string::npos) - { - it = header.erase(it); - } - else - { - ++it; - } + // Take this line as column header line + istringstream iss_hdr(line.substr(1)); + string hdr; + + header.clear(); + while (! iss_hdr.eof()) { + iss_hdr >> hdr; + cout << "Adding column header '" << hdr << "'" << endl; + header.push_back(hdr); } continue; diff --git a/swssconfig/sample/00-copp.config.json b/swssconfig/sample/00-copp.config.json index e3400c65ca..c7b0473ffc 100644 --- a/swssconfig/sample/00-copp.config.json +++ b/swssconfig/sample/00-copp.config.json @@ -34,8 +34,8 @@ "OP": "SET" }, { - "COPP_TABLE:trap.group.lldp.dhcp": { - "trap_ids": "lldp,dhcp", + "COPP_TABLE:trap.group.lldp.dhcp.udld": { + "trap_ids": "lldp,dhcp,udld", "trap_action":"trap", "trap_priority":"4", "queue": "4" diff --git a/teamsyncd/teamsync.cpp b/teamsyncd/teamsync.cpp index d3fb222d5c..88ed67602d 100644 --- a/teamsyncd/teamsync.cpp +++ b/teamsyncd/teamsync.cpp @@ -20,7 +20,7 @@ TeamSync::TeamSync(DBConnector *db, DBConnector *stateDb, Select *select) : m_select(select), m_lagTable(db, APP_LAG_TABLE_NAME), m_lagMemberTable(db, APP_LAG_MEMBER_TABLE_NAME), - m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR) + m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME) { } @@ -160,11 +160,19 @@ int TeamSync::TeamPortSync::onChange() bool enabled; ifindex = team_get_port_ifindex(port); - team_ifindex2ifname(m_team, ifindex, ifname, MAX_IFNAME); + + /* Skip if interface is not found */ + if (!team_ifindex2ifname(m_team, ifindex, ifname, MAX_IFNAME)) + { + SWSS_LOG_INFO("Interface ifindex(%u) is not found", ifindex); + continue; + } /* Skip the member that is removed from the LAG */ if (team_is_port_removed(port)) + { continue; + } team_get_port_enabled(m_team, ifindex, &enabled); tmp_lag_members[string(ifname)] = enabled; @@ -203,22 +211,12 @@ int TeamSync::TeamPortSync::teamdHandler(struct team_handle *team, void *arg, return ((TeamSync::TeamPortSync *)arg)->onChange(); } -void TeamSync::TeamPortSync::addFd(fd_set *fd) -{ - FD_SET(team_get_event_fd(m_team), fd); -} - -bool TeamSync::TeamPortSync::isMe(fd_set *fd) -{ - return FD_ISSET(team_get_event_fd(m_team), fd); -} - -int TeamSync::TeamPortSync::readCache() +int TeamSync::TeamPortSync::getFd() { - return NODATA; + return team_get_event_fd(m_team); } -void TeamSync::TeamPortSync::readMe() +void TeamSync::TeamPortSync::readData() { team_handle_events(m_team); } diff --git a/teamsyncd/teamsync.h b/teamsyncd/teamsync.h index 8f8114a5b2..79ca1e1e57 100644 --- a/teamsyncd/teamsync.h +++ b/teamsyncd/teamsync.h @@ -32,10 +32,8 @@ class TeamSync : public NetMsg ProducerStateTable *lagMemberTable); ~TeamPortSync(); - virtual void addFd(fd_set *fd); - virtual bool isMe(fd_set *fd); - virtual int readCache(); - virtual void readMe(); + int getFd() override; + void readData() override; protected: int onChange(); diff --git a/teamsyncd/teamsyncd.cpp b/teamsyncd/teamsyncd.cpp index 3ed2fb4dce..2ac52ebf72 100644 --- a/teamsyncd/teamsyncd.cpp +++ b/teamsyncd/teamsyncd.cpp @@ -34,8 +34,7 @@ int main(int argc, char **argv) while (true) { Selectable *temps; - int tempfd; - s.select(&temps, &tempfd); + s.select(&temps); } } catch (const std::exception& e) diff --git a/tests/Makefile.am b/tests/Makefile.am index d96e3f5c3e..b5e570c6b4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -10,9 +10,9 @@ DBGFLAGS = -g -DNDEBUG endif CFLAGS_GTEST = -LDADD_GTEST = +LDADD_GTEST = -L/usr/src/gtest -tests_SOURCES = swssnet_ut.cpp +tests_SOURCES = swssnet_ut.cpp request_parser_ut.cpp tests_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) tests_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_GTEST) $(CFLAGS_SAI) diff --git a/tests/README.md b/tests/README.md index 6d05174863..7c5ef03a68 100644 --- a/tests/README.md +++ b/tests/README.md @@ -11,7 +11,7 @@ SWSS Integration tests runs on docker-sonic-vs which runs on top of SAI virtual sudo pip install --system docker==2.6.1 sudo pip install --system pytest==3.3.0 ``` -- Compile and install swss common library +- Compile and install swss common library. Follow instructions [here](https://github.com/Azure/sonic-swss-common/blob/master/README.md) to first install prerequisites to build swss common library. ``` cd sonic-swss-common ./autogen.sh diff --git a/tests/request_parser_ut.cpp b/tests/request_parser_ut.cpp new file mode 100644 index 0000000000..fd68bdb4f8 --- /dev/null +++ b/tests/request_parser_ut.cpp @@ -0,0 +1,878 @@ +#include +#include +#include +#include +#include + +#include "macaddress.h" +#include "orch.h" +#include "request_parser.h" +#include "request_parser.cpp" + +const request_description_t request_description1 = { + { REQ_T_STRING }, + { + { "v4", REQ_T_BOOL }, + { "v6", REQ_T_BOOL }, + { "src_mac", REQ_T_MAC_ADDRESS }, + { "ttl_action", REQ_T_PACKET_ACTION }, + { "ip_opt_action", REQ_T_PACKET_ACTION }, + { "l3_mc_action", REQ_T_PACKET_ACTION }, + }, + { } // no mandatory attributes +}; + +class TestRequest1 : public Request +{ +public: + TestRequest1() : Request(request_description1, '|') { } +}; + +const request_description_t request_description2 = { + { REQ_T_STRING, REQ_T_MAC_ADDRESS, REQ_T_STRING }, + { + { "v4", REQ_T_BOOL }, + { "v6", REQ_T_BOOL }, + { "src_mac", REQ_T_MAC_ADDRESS }, + { "ttl_action", REQ_T_PACKET_ACTION }, + { "ip_opt_action", REQ_T_PACKET_ACTION }, + { "l3_mc_action", REQ_T_PACKET_ACTION }, + { "just_string", REQ_T_STRING }, + }, + {"just_string"} +}; + +class TestRequest2 : public Request +{ +public: + TestRequest2() : Request(request_description2, '|') { } +}; + +const request_description_t request_description3 = { + { REQ_T_STRING, REQ_T_STRING }, + { + { "v4", REQ_T_BOOL }, + { "v6", REQ_T_BOOL }, + }, + { } // no mandatory attributes +}; + +class TestRequest3 : public Request +{ +public: + TestRequest3() : Request(request_description3, ':') { } +}; + +TEST(request_parser, simpleKey) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "v4", "true" }, + { "v6", "true" }, + { "src_mac", "02:03:04:05:06:07" }, + { "ttl_action", "copy" }, + { "ip_opt_action", "drop" }, + { "l3_mc_action", "log" } + } + }; + + try + { + TestRequest1 request; + + EXPECT_NO_THROW(request.parse(t)); + + EXPECT_STREQ(request.getOperation().c_str(), "SET"); + EXPECT_STREQ(request.getFullKey().c_str(), "key1"); + EXPECT_STREQ(request.getKeyString(0).c_str(), "key1"); + EXPECT_TRUE(request.getAttrFieldNames() == (std::unordered_set{"v4", "v6", "src_mac", "ttl_action", "ip_opt_action", "l3_mc_action"})); + EXPECT_TRUE(request.getAttrBool("v4")); + EXPECT_TRUE(request.getAttrBool("v6")); + EXPECT_STREQ(request.getAttrMacAddress("src_mac").to_string().c_str(), "02:03:04:05:06:07"); + EXPECT_EQ(request.getAttrPacketAction("ttl_action"), SAI_PACKET_ACTION_COPY); + EXPECT_EQ(request.getAttrPacketAction("ip_opt_action"), SAI_PACKET_ACTION_DROP); + EXPECT_EQ(request.getAttrPacketAction("l3_mc_action"), SAI_PACKET_ACTION_LOG); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, simpleKeyEmptyAttrs) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + {"empty", "empty"}, + } + }; + + try + { + TestRequest1 request; + + EXPECT_NO_THROW(request.parse(t)); + + EXPECT_STREQ(request.getOperation().c_str(), "SET"); + EXPECT_STREQ(request.getFullKey().c_str(), "key1"); + EXPECT_STREQ(request.getKeyString(0).c_str(), "key1"); + EXPECT_EQ(request.getAttrFieldNames(), std::unordered_set()); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, complexKey) +{ + KeyOpFieldsValuesTuple t {"key1|02:03:04:05:06:07|key2", "SET", + { + { "v4", "false" }, + { "v6", "false" }, + { "src_mac", "02:03:04:05:06:07" }, + { "ttl_action", "copy" }, + { "ip_opt_action", "drop" }, + { "l3_mc_action", "log" }, + { "just_string", "test_string"}, + } + }; + + try + { + TestRequest2 request; + + EXPECT_NO_THROW(request.parse(t)); + + EXPECT_STREQ(request.getOperation().c_str(), "SET"); + EXPECT_STREQ(request.getFullKey().c_str(), "key1|02:03:04:05:06:07|key2"); + EXPECT_STREQ(request.getKeyString(0).c_str(), "key1"); + EXPECT_STREQ(request.getKeyMacAddress(1).to_string().c_str(), "02:03:04:05:06:07"); + EXPECT_STREQ(request.getKeyString(2).c_str(), "key2"); + EXPECT_TRUE(request.getAttrFieldNames() == (std::unordered_set{"v4", "v6", "src_mac", "ttl_action", "ip_opt_action", "l3_mc_action", "just_string"})); + EXPECT_FALSE(request.getAttrBool("v4")); + EXPECT_FALSE(request.getAttrBool("v6")); + EXPECT_STREQ(request.getAttrMacAddress("src_mac").to_string().c_str(), "02:03:04:05:06:07"); + EXPECT_EQ(request.getAttrPacketAction("ttl_action"), SAI_PACKET_ACTION_COPY); + EXPECT_EQ(request.getAttrPacketAction("ip_opt_action"), SAI_PACKET_ACTION_DROP); + EXPECT_EQ(request.getAttrPacketAction("l3_mc_action"), SAI_PACKET_ACTION_LOG); + EXPECT_STREQ(request.getAttrString("just_string").c_str(), "test_string"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, deleteOperation1) +{ + KeyOpFieldsValuesTuple t {"key1", "DEL", + { + } + }; + + try + { + TestRequest1 request; + + EXPECT_NO_THROW(request.parse(t)); + + EXPECT_STREQ(request.getOperation().c_str(), "DEL"); + EXPECT_STREQ(request.getFullKey().c_str(), "key1"); + EXPECT_STREQ(request.getKeyString(0).c_str(), "key1"); + EXPECT_TRUE(request.getAttrFieldNames() == (std::unordered_set{ })); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, deleteOperation2) +{ + KeyOpFieldsValuesTuple t {"key1|02:03:04:05:06:07|key2", "DEL", + { + } + }; + + try + { + TestRequest2 request; + + EXPECT_NO_THROW(request.parse(t)); + + EXPECT_STREQ(request.getOperation().c_str(), "DEL"); + EXPECT_STREQ(request.getFullKey().c_str(), "key1|02:03:04:05:06:07|key2"); + EXPECT_STREQ(request.getKeyString(0).c_str(), "key1"); + EXPECT_STREQ(request.getKeyMacAddress(1).to_string().c_str(), "02:03:04:05:06:07"); + EXPECT_STREQ(request.getKeyString(2).c_str(), "key2"); + EXPECT_TRUE(request.getAttrFieldNames() == (std::unordered_set{ })); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, deleteOperationWithAttr) +{ + KeyOpFieldsValuesTuple t {"key1", "DEL", + { + { "v4", "true" } + } + }; + + try + { + TestRequest1 request; + request.parse(t); + FAIL() << "Expected std::invalid_argument"; + } + catch (const std::invalid_argument& e) + { + EXPECT_STREQ(e.what(), "Delete operation request contains attributes"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::invalid_argument, not other exception"; + } +} + +TEST(request_parser, wrongOperation) +{ + KeyOpFieldsValuesTuple t {"key1", "ABC", + { + { "v4", "true" } + } + }; + + try + { + TestRequest1 request; + request.parse(t); + FAIL() << "Expected std::invalid_argument"; + } + catch (const std::invalid_argument& e) + { + EXPECT_STREQ(e.what(), "Wrong operation: ABC"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::invalid_argument, not other exception"; + } +} + +TEST(request_parser, wrongkey1) +{ + KeyOpFieldsValuesTuple t {"key1|key2", "SET", + { + { "v4", "true" } + } + }; + + try + { + TestRequest1 request; + request.parse(t); + FAIL() << "Expected std::invalid_argument"; + } + catch (const std::invalid_argument& e) + { + EXPECT_STREQ(e.what(), "Wrong number of key items. Expected 1 item(s). Key: 'key1|key2'"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::invalid_argument, not other exception"; + } +} + +TEST(request_parser, wrongkey2) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "v4", "true" } + } + }; + + try + { + TestRequest2 request; + request.parse(t); + FAIL() << "Expected std::invalid_argument"; + } + catch (const std::invalid_argument& e) + { + EXPECT_STREQ(e.what(), "Wrong number of key items. Expected 3 item(s). Key: 'key1'"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::invalid_argument, not other exception"; + } +} + +TEST(request_parser, wrongkeyType1) +{ + KeyOpFieldsValuesTuple t {"key1|key2|key3", "SET", + { + { "v4", "true" } + } + }; + + try + { + TestRequest2 request; + request.parse(t); + FAIL() << "Expected std::invalid_argument"; + } + catch (const std::invalid_argument& e) + { + EXPECT_STREQ(e.what(), "Invalid mac address: key2"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::invalid_argument, not other exception"; + } +} + +TEST(request_parser, wrongAttributeNotFound) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "v5", "true" } + } + }; + + try + { + TestRequest1 request; + request.parse(t); + FAIL() << "Expected std::invalid_argument"; + } + catch (const std::invalid_argument& e) + { + EXPECT_STREQ(e.what(), "Unknown attribute name: v5"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::invalid_argument, not other exception"; + } +} + +TEST(request_parser, wrongRequiredAttribute) +{ + KeyOpFieldsValuesTuple t {"key1|02:03:04:05:06:07|key3", "SET", + { + { "v4", "true" } + } + }; + + try + { + TestRequest2 request; + request.parse(t); + FAIL() << "Expected std::invalid_argument"; + } + catch (const std::invalid_argument& e) + { + EXPECT_STREQ(e.what(), "Mandatory attribute 'just_string' not found"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::invalid_argument, not other exception"; + } +} + +TEST(request_parser, wrongAttrTypeBoolean) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "v4", "true1" } + } + }; + + try + { + TestRequest1 request; + request.parse(t); + FAIL() << "Expected std::invalid_argument"; + } + catch (const std::invalid_argument& e) + { + EXPECT_STREQ(e.what(), "Can't parse boolean value 'true1'"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::invalid_argument, not other exception"; + } +} + +TEST(request_parser, wrongAttrTypeMac) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "src_mac", "33456" } + } + }; + + try + { + TestRequest1 request; + request.parse(t); + FAIL() << "Expected std::invalid_argument"; + } + catch (const std::invalid_argument& e) + { + EXPECT_STREQ(e.what(), "Invalid mac address: 33456"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::invalid_argument, not other exception"; + } +} + +TEST(request_parser, wrongAttrTypePacketAction) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "ttl_action", "something" } + } + }; + + try + { + TestRequest1 request; + request.parse(t); + FAIL() << "Expected std::invalid_argument"; + } + catch (const std::invalid_argument& e) + { + EXPECT_STREQ(e.what(), "Wrong packet action attribute value 'something'"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::invalid_argument, not other exception"; + } +} + +TEST(request_parser, correctAttrTypePacketAction1) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "ttl_action", "drop" }, + { "ip_opt_action", "forward" }, + { "l3_mc_action", "copy" }, + } + }; + try + { + TestRequest1 request; + + EXPECT_NO_THROW(request.parse(t)); + + EXPECT_EQ(request.getAttrPacketAction("ttl_action"), SAI_PACKET_ACTION_DROP); + EXPECT_EQ(request.getAttrPacketAction("ip_opt_action"), SAI_PACKET_ACTION_FORWARD); + EXPECT_EQ(request.getAttrPacketAction("l3_mc_action"), SAI_PACKET_ACTION_COPY); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, correctAttrTypePacketAction2) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "ttl_action", "copy_cancel" }, + { "ip_opt_action", "trap" }, + { "l3_mc_action", "log" }, + } + }; + + try + { + TestRequest1 request; + + EXPECT_NO_THROW(request.parse(t)); + + EXPECT_EQ(request.getAttrPacketAction("ttl_action"), SAI_PACKET_ACTION_COPY_CANCEL); + EXPECT_EQ(request.getAttrPacketAction("ip_opt_action"), SAI_PACKET_ACTION_TRAP); + EXPECT_EQ(request.getAttrPacketAction("l3_mc_action"), SAI_PACKET_ACTION_LOG); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, correctAttrTypePacketAction3) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "ttl_action", "deny" }, + { "ip_opt_action", "transit" }, + { "l3_mc_action", "log" }, + } + }; + + try + { + TestRequest1 request; + + EXPECT_NO_THROW(request.parse(t)); + + EXPECT_EQ(request.getAttrPacketAction("ttl_action"), SAI_PACKET_ACTION_DENY); + EXPECT_EQ(request.getAttrPacketAction("ip_opt_action"), SAI_PACKET_ACTION_TRANSIT); + EXPECT_EQ(request.getAttrPacketAction("l3_mc_action"), SAI_PACKET_ACTION_LOG); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, correctParseAndClear) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "ttl_action", "deny" }, + { "ip_opt_action", "transit" }, + { "l3_mc_action", "log" }, + } + }; + try + { + TestRequest1 request; + + EXPECT_NO_THROW(request.parse(t)); + + EXPECT_NO_THROW(request.clear()); + + EXPECT_NO_THROW(request.parse(t)); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, incorrectParseAndClear) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "ttl_action", "deny" }, + { "ip_opt_action", "transit" }, + { "l3_mc_action", "log" }, + } + }; + + try + { + TestRequest1 request; + + EXPECT_NO_THROW(request.parse(t)); + + request.parse(t); + FAIL() << "Expected std::logic_error"; + } + catch (const std::logic_error& e) + { + EXPECT_STREQ(e.what(), "The parser already has a parsed request"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::logic_error, not other exception"; + } +} + +TEST(request_parser, correctClear) +{ + KeyOpFieldsValuesTuple t1 {"key1|02:03:04:05:06:07|key2", "SET", + { + { "v4", "false" }, + { "v6", "false" }, + { "src_mac", "02:03:04:05:06:07" }, + { "ttl_action", "copy" }, + { "ip_opt_action", "drop" }, + { "l3_mc_action", "log" }, + { "just_string", "test_string"}, + } + }; + + KeyOpFieldsValuesTuple t2 {"key3|f2:f3:f4:f5:f6:f7|key4", "SET", + { + { "v4", "true" }, + { "src_mac", "f2:f3:f4:f5:f6:f7" }, + { "ttl_action", "log" }, + { "ip_opt_action", "copy" }, + { "l3_mc_action", "log" }, + { "just_string", "string"}, + } + }; + + KeyOpFieldsValuesTuple t3 {"key5|52:53:54:55:56:57|key6", "DEL", + { + } + }; + + try + { + TestRequest2 request; + + // parse t1 + + EXPECT_NO_THROW(request.parse(t1)); + + EXPECT_STREQ(request.getOperation().c_str(), "SET"); + EXPECT_STREQ(request.getFullKey().c_str(), "key1|02:03:04:05:06:07|key2"); + EXPECT_STREQ(request.getKeyString(0).c_str(), "key1"); + EXPECT_STREQ(request.getKeyMacAddress(1).to_string().c_str(), "02:03:04:05:06:07"); + EXPECT_STREQ(request.getKeyString(2).c_str(), "key2"); + EXPECT_TRUE(request.getAttrFieldNames() == (std::unordered_set{"v4", "v6", "src_mac", "ttl_action", "ip_opt_action", "l3_mc_action", "just_string"})); + EXPECT_FALSE(request.getAttrBool("v4")); + EXPECT_FALSE(request.getAttrBool("v6")); + EXPECT_STREQ(request.getAttrMacAddress("src_mac").to_string().c_str(), "02:03:04:05:06:07"); + EXPECT_EQ(request.getAttrPacketAction("ttl_action"), SAI_PACKET_ACTION_COPY); + EXPECT_EQ(request.getAttrPacketAction("ip_opt_action"), SAI_PACKET_ACTION_DROP); + EXPECT_EQ(request.getAttrPacketAction("l3_mc_action"), SAI_PACKET_ACTION_LOG); + EXPECT_STREQ(request.getAttrString("just_string").c_str(), "test_string"); + + EXPECT_NO_THROW(request.clear()); + + // parse t2 + + EXPECT_NO_THROW(request.parse(t2)); + + EXPECT_STREQ(request.getOperation().c_str(), "SET"); + EXPECT_STREQ(request.getFullKey().c_str(), "key3|f2:f3:f4:f5:f6:f7|key4"); + EXPECT_STREQ(request.getKeyString(0).c_str(), "key3"); + EXPECT_STREQ(request.getKeyMacAddress(1).to_string().c_str(), "f2:f3:f4:f5:f6:f7"); + EXPECT_STREQ(request.getKeyString(2).c_str(), "key4"); + EXPECT_TRUE(request.getAttrFieldNames() == (std::unordered_set{"v4", "src_mac", "ttl_action", "ip_opt_action", "l3_mc_action", "just_string"})); + EXPECT_TRUE(request.getAttrBool("v4")); + EXPECT_STREQ(request.getAttrMacAddress("src_mac").to_string().c_str(), "f2:f3:f4:f5:f6:f7"); + EXPECT_EQ(request.getAttrPacketAction("ttl_action"), SAI_PACKET_ACTION_LOG); + EXPECT_EQ(request.getAttrPacketAction("ip_opt_action"), SAI_PACKET_ACTION_COPY); + EXPECT_EQ(request.getAttrPacketAction("l3_mc_action"), SAI_PACKET_ACTION_LOG); + EXPECT_STREQ(request.getAttrString("just_string").c_str(), "string"); + + EXPECT_NO_THROW(request.clear()); + + // parse t3 + + EXPECT_NO_THROW(request.parse(t3)); + + EXPECT_STREQ(request.getOperation().c_str(), "DEL"); + EXPECT_STREQ(request.getFullKey().c_str(), "key5|52:53:54:55:56:57|key6"); + EXPECT_STREQ(request.getKeyString(0).c_str(), "key5"); + EXPECT_STREQ(request.getKeyMacAddress(1).to_string().c_str(), "52:53:54:55:56:57"); + EXPECT_STREQ(request.getKeyString(2).c_str(), "key6"); + EXPECT_TRUE(request.getAttrFieldNames() == (std::unordered_set{ })); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +TEST(request_parser, anotherKeySeparator) +{ + KeyOpFieldsValuesTuple t {"key1:key2", "SET", + { + { "v4", "false" }, + { "v6", "false" }, + } + }; + + try + { + TestRequest3 request; + + EXPECT_NO_THROW(request.parse(t)); + + EXPECT_STREQ(request.getOperation().c_str(), "SET"); + EXPECT_STREQ(request.getFullKey().c_str(), "key1:key2"); + EXPECT_STREQ(request.getKeyString(0).c_str(), "key1"); + EXPECT_STREQ(request.getKeyString(1).c_str(), "key2"); + EXPECT_TRUE(request.getAttrFieldNames() == (std::unordered_set{"v4", "v6"})); + EXPECT_FALSE(request.getAttrBool("v4")); + EXPECT_FALSE(request.getAttrBool("v6")); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Got unexpected exception"; + } +} + +const request_description_t request_description4 = { + { REQ_T_STRING, }, + { + { "v4", REQ_T_BOOL }, + { "v5", REQ_T_NOT_USED }, + }, + { } // no mandatory attributes +}; + +class TestRequest4 : public Request +{ +public: + TestRequest4() : Request(request_description4, '|') { } +}; + +TEST(request_parser, notDefinedAttrType) +{ + KeyOpFieldsValuesTuple t {"key1", "SET", + { + { "v4", "false" }, + { "v5", "abcde" }, + } + }; + try + { + TestRequest4 request; + request.parse(t); + FAIL() << "Expected std::logic_error"; + } + catch (const std::logic_error& e) + { + EXPECT_STREQ(e.what(), "Not implemented attribute type parser for attribute:v5"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::logic_error, not other exception"; + } +} + +const request_description_t request_description5 = { + { REQ_T_STRING, REQ_T_NOT_USED}, + { + { "v4", REQ_T_BOOL }, + }, + { } // no mandatory attributes +}; + +class TestRequest5 : public Request +{ +public: + TestRequest5() : Request(request_description5, '|') { } +}; + +TEST(request_parser, notDefinedKeyType) +{ + KeyOpFieldsValuesTuple t {"key1|abcde", "SET", + { + { "v4", "false" }, + } + }; + + try + { + TestRequest5 request; + request.parse(t); + FAIL() << "Expected std::logic_error"; + } + catch (const std::logic_error& e) + { + EXPECT_STREQ(e.what(), "Not implemented key type parser. Key 'key1|abcde'. Key item:abcde"); + } + catch (const std::exception& e) + { + FAIL() << "Got unexpected exception " << e.what(); + } + catch (...) + { + FAIL() << "Expected std::logic_error, not other exception"; + } +} diff --git a/tests/test_acl.py b/tests/test_acl.py index 14218347e3..4ab939fe49 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -21,7 +21,7 @@ def test_AclTableCreation(self, dvs): bind_ports = ["Ethernet0", "Ethernet4"] # create ACL_TABLE in config db - tbl = swsscommon.Table(db, "ACL_TABLE", '|') + tbl = swsscommon.Table(db, "ACL_TABLE") fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) tbl.set("test", fvs) @@ -95,7 +95,7 @@ def test_AclRuleL4SrcPort(self, dvs): adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) # create acl rule - tbl = swsscommon.Table(db, "ACL_RULE", '|') + tbl = swsscommon.Table(db, "ACL_RULE") fvs = swsscommon.FieldValuePairs([("priority", "55"), ("PACKET_ACTION", "FORWARD"), ("L4_SRC_PORT", "65000")]) tbl.set("test|acl_test_rule", fvs) @@ -136,3 +136,646 @@ def test_AclRuleL4SrcPort(self, dvs): (status, fvs) = atbl.get(acl_entry[0]) assert status == False + + def test_AclTableDeletion(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # get ACL_TABLE in config db + tbl = swsscommon.Table(db, "ACL_TABLE") + tbl._del("test") + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + keys = atbl.getKeys() + # only the default table was left + assert len(keys) == 1 + + def test_V6AclTableCreation(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + bind_ports = ["Ethernet0", "Ethernet4", "Ethernet8"] + # create ACL_TABLE in config db + tbl = swsscommon.Table(db, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "testv6"), ("type", "L3V6"), ("ports", ",".join(bind_ports))]) + tbl.set("test-aclv6", fvs) + + time.sleep(1) + + # check acl table in asic db + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table group in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_table_groups = atbl.getKeys() + assert len(acl_table_groups) == 3 + + for k in acl_table_groups: + (status, fvs) = atbl.get(k) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_ACL_STAGE": + assert fv[1] == "SAI_ACL_STAGE_INGRESS" + elif fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_ACL_BIND_POINT_TYPE_LIST": + assert fv[1] == "1:SAI_ACL_BIND_POINT_TYPE_PORT" + elif fv[0] == "SAI_ACL_TABLE_GROUP_ATTR_TYPE": + assert fv[1] == "SAI_ACL_TABLE_GROUP_TYPE_PARALLEL" + else: + assert False + + # check acl table group member + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER") + keys = atbl.getKeys() + # three ports + assert len(keys) == 3 + + member_groups = [] + for k in keys: + (status, fvs) = atbl.get(k) + assert status == True + + assert len(fvs) == 3 + for fv in fvs: + if fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_GROUP_ID": + assert fv[1] in acl_table_groups + member_groups.append(fv[1]) + elif fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_PRIORITY": + assert True + else: + assert False + + assert set(member_groups) == set(acl_table_groups) + + # check port binding + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + + port_groups = [] + for p in [dvs.asicdb.portnamemap[portname] for portname in bind_ports]: + (status, fvs) = atbl.get(p) + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_INGRESS_ACL": + assert fv[1] in acl_table_groups + port_groups.append(fv[1]) + + assert set(port_groups) == set(acl_table_groups) + + def test_V6AclRuleIPv6Any(self, dvs): + """ + hmset ACL_RULE|test-aclv6|test_rule1 priority 1000 PACKET_ACTION FORWARD IPv6Any + """ + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create acl rule + tbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1001"), ("PACKET_ACTION", "FORWARD"), ("IP_TYPE", "IPv6ANY")]) + tbl.set("test-aclv6|test_rule1", fvs) + + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "1001" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_ACL_IP_TYPE": + assert fv[1] == "SAI_ACL_IP_TYPE_IPV6ANY&mask:0xffffffffffffffff" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_FORWARD" + else: + assert False + + # remove acl rule + tbl._del("test-aclv6|test_rule1") + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + def test_V6AclRuleIPv6AnyDrop(self, dvs): + """ + hmset ACL_RULE|test-aclv6|test_rule2 priority 1002 PACKET_ACTION DROP IPv6Any + """ + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create acl rule + tbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1002"), ("PACKET_ACTION", "DROP"), ("IP_TYPE", "IPv6ANY")]) + tbl.set("test-aclv6|test_rule2", fvs) + + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "1002" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_ACL_IP_TYPE": + assert fv[1] == "SAI_ACL_IP_TYPE_IPV6ANY&mask:0xffffffffffffffff" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + else: + assert False + + # remove acl rule + tbl._del("test-aclv6|test_rule2") + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + def test_V6AclRuleIpProtocol(self, dvs): + """ + hmset ACL_RULE|test-aclv6|test_rule3 priority 1003 PACKET_ACTION DROP IP_PROTOCOL 6 + """ + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create acl rule + tbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1003"), ("PACKET_ACTION", "DROP"), ("IP_PROTOCOL", "6")]) + tbl.set("test-aclv6|test_rule3", fvs) + + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "1003" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_IP_PROTOCOL": + assert fv[1] == "6&mask:0xff" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + else: + assert False + + # remove acl rule + tbl._del("test-aclv6|test_rule3") + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + def test_V6AclRuleSrcIPv6(self, dvs): + """ + hmset ACL_RULE|test-aclv6|test_rule4 priority 1004 PACKET_ACTION DROP SRC_IPV6 2777::0/64 + """ + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create acl rule + tbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1004"), ("PACKET_ACTION", "DROP"), ("SRC_IPV6", "2777::0/64")]) + tbl.set("test-aclv6|test_rule4", fvs) + + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "1004" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_SRC_IPV6": + assert fv[1] == "2777::&mask:ffff:ffff:ffff:ffff::" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + else: + assert False + + # remove acl rule + tbl._del("test-aclv6|test_rule4") + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + def test_V6AclRuleDstIPv6(self, dvs): + """ + hmset ACL_RULE|test-aclv6|test_rule5 priority 1005 PACKET_ACTION DROP DST_IPV6 2002::2/128 + """ + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create acl rule + tbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1005"), ("PACKET_ACTION", "DROP"), ("DST_IPV6", "2002::2/128")]) + tbl.set("test-aclv6|test_rule5", fvs) + + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "1005" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_DST_IPV6": + assert fv[1] == "2002::2&mask:ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + else: + assert False + + # remove acl rule + tbl._del("test-aclv6|test_rule5") + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + def test_V6AclRuleL4SrcPort(self, dvs): + """ + hmset ACL_RULE|test-aclv6|test_rule6 priority 1006 PACKET_ACTION DROP L4_SRC_PORT 65000 + """ + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create acl rule + tbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1006"), ("PACKET_ACTION", "DROP"), ("L4_SRC_PORT", "65000")]) + tbl.set("test-aclv6|test_rule6", fvs) + + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "1006" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_L4_SRC_PORT": + assert fv[1] == "65000&mask:0xffff" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + else: + assert False + + # remove acl rule + tbl._del("test-aclv6|test_rule6") + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + def test_V6AclRuleL4DstPort(self, dvs): + """ + hmset ACL_RULE|test-aclv6|test_rule7 priority 1007 PACKET_ACTION DROP L4_DST_PORT 65001 + """ + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create acl rule + tbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1007"), ("PACKET_ACTION", "DROP"), ("L4_DST_PORT", "65001")]) + tbl.set("test-aclv6|test_rule7", fvs) + + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "1007" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_L4_DST_PORT": + assert fv[1] == "65001&mask:0xffff" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + else: + assert False + + # remove acl rule + tbl._del("test-aclv6|test_rule7") + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + def test_V6AclRuleTCPFlags(self, dvs): + """ + hmset ACL_RULE|test-aclv6|test_rule8 priority 1008 PACKET_ACTION DROP TCP_FLAGS 0x7/0x3f + """ + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create acl rule + tbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1008"), ("PACKET_ACTION", "DROP"), ("TCP_FLAGS", "0x07/0x3f")]) + tbl.set("test-aclv6|test_rule8", fvs) + + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "1008" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_TCP_FLAGS": + assert fv[1] == "7&mask:0x3f" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + else: + assert False + + # remove acl rule + tbl._del("test-aclv6|test_rule8") + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + def test_V6AclRuleL4SrcPortRange(self, dvs): + """ + hmset ACL_RULE|test-aclv6|test_rule9 priority 1009 PACKET_ACTION DROP L4_SRC_PORT_RANGE 1-100 + """ + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create acl rule + tbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1009"), ("PACKET_ACTION", "DROP"), ("L4_SRC_PORT_RANGE", "1-100")]) + tbl.set("test-aclv6|test_rule9", fvs) + + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "1009" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_ACL_RANGE_TYPE": + aclrange = fv[1] + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + else: + assert False + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_RANGE") + aclrange_obj = aclrange.split(":", 1)[1] + + (status, fvs) = atbl.get(aclrange_obj) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "SAI_ACL_RANGE_ATTR_TYPE": + assert fv[1] == "SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE" + elif fv[0] == "SAI_ACL_RANGE_ATTR_LIMIT": + assert fv[1] == "1,100" + else: + assert False + + # remove acl rule + tbl._del("test-aclv6|test_rule9") + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + def test_V6AclRuleL4DstPortRange(self, dvs): + """ + hmset ACL_RULE|test-aclv6|test_rule10 priority 1010 PACKET_ACTION DROP L4_DST_PORT_RANGE 101-200 + """ + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create acl rule + tbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "1010"), ("PACKET_ACTION", "DROP"), ("L4_DST_PORT_RANGE", "101-200")]) + tbl.set("test-aclv6|test_rule10", fvs) + + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs, adb) + + # check acl table in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + + acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entry) == 1 + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == True + assert len(fvs) == 6 + for fv in fvs: + if fv[0] == "SAI_ACL_ENTRY_ATTR_TABLE_ID": + assert fv[1] == test_acl_table_id + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ADMIN_STATE": + assert fv[1] == "true" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_PRIORITY": + assert fv[1] == "1010" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_ACL_RANGE_TYPE": + aclrange = fv[1] + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION": + assert fv[1] == "SAI_PACKET_ACTION_DROP" + else: + assert False + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_RANGE") + aclrange_obj = aclrange.split(":", 1)[1] + + (status, fvs) = atbl.get(aclrange_obj) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "SAI_ACL_RANGE_ATTR_TYPE": + assert fv[1] == "SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE" + elif fv[0] == "SAI_ACL_RANGE_ATTR_LIMIT": + assert fv[1] == "101,200" + else: + assert False + + # remove acl rule + tbl._del("test-aclv6|test_rule10") + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + def test_V6AclTableDeletion(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # get ACL_TABLE in config db + tbl = swsscommon.Table(db, "ACL_TABLE") + tbl._del("test-aclv6") + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + keys = atbl.getKeys() + # only the default table was left + assert len(keys) == 1 diff --git a/tests/test_crm.py b/tests/test_crm.py new file mode 100644 index 0000000000..fa859de63b --- /dev/null +++ b/tests/test_crm.py @@ -0,0 +1,529 @@ +from swsscommon import swsscommon +import os +import re +import time +import json +import redis + + +def getCrmCounterValue(dvs, key, counter): + + counters_db = swsscommon.DBConnector(swsscommon.COUNTERS_DB, dvs.redis_sock, 0) + crm_stats_table = swsscommon.Table(counters_db, 'CRM') + + for k in crm_stats_table.get(key)[1]: + if k[0] == counter: + return int(k[1]) + + +def setReadOnlyAttr(dvs, obj, attr, val): + + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + tbl = swsscommon.Table(db, "ASIC_STATE:{0}".format(obj)) + keys = tbl.getKeys() + + assert len(keys) == 1 + + swVid = keys[0] + r = redis.Redis(unix_socket_path=dvs.redis_sock, db=swsscommon.ASIC_DB) + swRid = r.hget("VIDTORID", swVid) + + assert swRid is not None + + ntf = swsscommon.NotificationProducer(db, "SAI_VS_UNITTEST_CHANNEL") + fvp = swsscommon.FieldValuePairs() + ntf.send("enable_unittests", "true", fvp) + fvp = swsscommon.FieldValuePairs([(attr, val)]) + key = "SAI_OBJECT_TYPE_SWITCH:" + swRid + + ntf.send("set_ro", key, fvp) + + +def test_CrmFdbEntry(dvs): + + dvs.runcmd("crm config polling interval 1") + + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') + + time.sleep(2) + + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') + + app_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + cfg_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + # create a FDB entry + tbl = swsscommon.ProducerStateTable(app_db, "FDB_TABLE") + fvs = swsscommon.FieldValuePairs([("port","Ethernet8"),("type","dynamic")]) + tbl.set("Vlan2:52-54-00-25-06-E9", fvs) + + # create vlan + tbl = swsscommon.Table(cfg_db, "VLAN") + fvs = swsscommon.FieldValuePairs([("vlanid", "2")]) + tbl.set("Vlan2", fvs) + + # create vlan member + tbl = swsscommon.Table(cfg_db, "VLAN_MEMBER") + fvs = swsscommon.FieldValuePairs([("tagging_mode", "untagged")]) + tbl.set("Vlan2|Ethernet8", fvs) + + # update available counter + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '999') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') + + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 + + # update available counter + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_FDB_ENTRY', '1000') + + time.sleep(2) + + # get counters + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_fdb_entry_available') + + assert new_avail_counter == avail_counter + + +def test_CrmIpv4Route(dvs): + + dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") + + dvs.runcmd("crm config polling interval 1") + + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '1000') + + # add static neighbor + dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1"), ("ifname", "Ethernet0")]) + + time.sleep(2) + + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') + + # add route and update available counter + ps.set("2.2.2.0/24", fvs) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '999') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') + + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 + + # remove route and update available counter + 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") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_ROUTE_ENTRY', '1000') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_route_available') + + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + + +def test_CrmIpv6Route(dvs): + + # Enable IPv6 routing + dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") + time.sleep(2) + + dvs.runcmd("ifconfig Ethernet0 inet6 add fc00::1/126 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") + + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '1000') + + # get neighbor and arp entry + dvs.servers[0].runcmd("ping6 -c 4 fc00::1") + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","fc00::2"), ("ifname", "Ethernet0")]) + + time.sleep(2) + + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') + + # add route and update available counter + ps.set("2001::/64", fvs) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '999') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') + + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 + + # remove route and update available counter + ps._del("2001::/64") + dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_ROUTE_ENTRY', '1000') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_route_available') + + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + + +def test_CrmIpv4Nexthop(dvs): + + dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") + + dvs.runcmd("crm config polling interval 1") + + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '1000') + + time.sleep(2) + + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') + + # add nexthop and update available counter + dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '999') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') + + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 + + # remove nexthop and update available counter + dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEXTHOP_ENTRY', '1000') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_nexthop_available') + + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + + +def test_CrmIpv6Nexthop(dvs): + + # Enable IPv6 routing + dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") + time.sleep(2) + + dvs.runcmd("ifconfig Ethernet0 inet6 add fc00::1/126 up") + + dvs.runcmd("crm config polling interval 1") + + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '1000') + + time.sleep(2) + + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') + + # add nexthop and update available counter + dvs.runcmd("ip -6 neigh replace fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '999') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') + + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 + + # remove nexthop and update available counter + dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEXTHOP_ENTRY', '1000') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_nexthop_available') + + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + + +def test_CrmIpv4Neighbor(dvs): + + dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") + + dvs.runcmd("crm config polling interval 1") + + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '1000') + + time.sleep(2) + + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') + + # add neighbor and update available counter + dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '999') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') + + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 + + # remove neighbor and update available counter + dvs.runcmd("ip neigh del 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV4_NEIGHBOR_ENTRY', '1000') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv4_neighbor_available') + + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + + +def test_CrmIpv6Neighbor(dvs): + + # Enable IPv6 routing + dvs.runcmd("sysctl net.ipv6.conf.all.disable_ipv6=0") + time.sleep(2) + + dvs.runcmd("ifconfig Ethernet0 inet6 add fc00::1/126 up") + + dvs.runcmd("crm config polling interval 1") + + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '1000') + + time.sleep(2) + + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') + + # add neighbor and update available counter + dvs.runcmd("ip -6 neigh replace fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '999') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') + + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 + + # remove neighbor and update available counter + dvs.runcmd("ip -6 neigh del fc00::2 lladdr 11:22:33:44:55:66 dev Ethernet0") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_IPV6_NEIGHBOR_ENTRY', '1000') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_ipv6_neighbor_available') + + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + + +def test_CrmNexthopGroup(dvs): + + dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") + dvs.runcmd("ifconfig Ethernet4 10.0.0.2/31 up") + + dvs.runcmd("crm config polling interval 1") + + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '1000') + + # add neighbors + dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + dvs.runcmd("ip neigh replace 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3"), ("ifname", "Ethernet0,Ethernet4")]) + + time.sleep(2) + + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') + + # add route and update available counter + ps.set("2.2.2.0/24", fvs) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '999') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') + + assert new_used_counter - used_counter == 1 + assert avail_counter - new_avail_counter == 1 + + # remove route and update available counter + 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") + dvs.runcmd("ip neigh del 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_ENTRY', '1000') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_available') + + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + + +def test_CrmNexthopGroupMember(dvs): + + dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") + dvs.runcmd("ifconfig Ethernet4 10.0.0.2/31 up") + + dvs.runcmd("crm config polling interval 1") + + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '1000') + + # add neighbors + dvs.runcmd("ip neigh replace 10.0.0.1 lladdr 11:22:33:44:55:66 dev Ethernet0") + dvs.runcmd("ip neigh replace 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3"), ("ifname", "Ethernet0,Ethernet4")]) + + time.sleep(2) + + # get counters + used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') + avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') + + # add route and update available counter + ps.set("2.2.2.0/24", fvs) + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '998') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') + + assert new_used_counter - used_counter == 2 + assert avail_counter - new_avail_counter == 2 + + # remove route and update available counter + 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") + dvs.runcmd("ip neigh del 10.0.0.3 lladdr 11:22:33:44:55:66 dev Ethernet4") + setReadOnlyAttr(dvs, 'SAI_OBJECT_TYPE_SWITCH', 'SAI_SWITCH_ATTR_AVAILABLE_NEXT_HOP_GROUP_MEMBER_ENTRY', '1000') + + time.sleep(2) + + # get counters + new_used_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_used') + new_avail_counter = getCrmCounterValue(dvs, 'STATS', 'crm_stats_nexthop_group_member_available') + + assert new_used_counter == used_counter + assert new_avail_counter == avail_counter + + +def test_CrmAcl(dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + dvs.runcmd("crm config polling interval 1") + + bind_ports = ["Ethernet0", "Ethernet4"] + + # create ACL table + ttbl = swsscommon.Table(db, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + ttbl.set("test", fvs) + + # create ACL rule + rtbl = swsscommon.Table(db, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("priority", "55"), ("PACKET_ACTION", "FORWARD"), ("L4_SRC_PORT", "65000")]) + rtbl.set("test|acl_test_rule", fvs) + + time.sleep(2) + + table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') + assert table_used_counter == 1 + + # get ACL table key + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + acl_tables = [k for k in atbl.getKeys() if k not in dvs.asicdb.default_acl_table] + key = "ACL_TABLE_STATS:{0}".format(acl_tables[0].replace('oid:', '')) + + entry_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_entry_used') + assert entry_used_counter == 1 + + cnt_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_counter_used') + assert entry_used_counter == 1 + + # remove ACL rule + rtbl._del("test|acl_test_rule") + + time.sleep(2) + + entry_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_entry_used') + assert entry_used_counter == 0 + + cnt_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_counter_used') + assert cnt_used_counter == 0 + + # remove ACL table + ttbl._del("test") + + time.sleep(2) + + table_used_counter = getCrmCounterValue(dvs, 'ACL_STATS:INGRESS:PORT', 'crm_stats_acl_table_used') + assert table_used_counter == 0 + diff --git a/tests/test_dirbcast.py b/tests/test_dirbcast.py new file mode 100644 index 0000000000..bf7d50c3c0 --- /dev/null +++ b/tests/test_dirbcast.py @@ -0,0 +1,79 @@ +from swsscommon import swsscommon +import time +import re +import json + +def test_DirectedBroadcast(dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create vlan in config db + tbl = swsscommon.Table(db, "VLAN") + fvs = swsscommon.FieldValuePairs([("vlanid", "100")]) + tbl.set("Vlan100", fvs) + + # create a vlan member in config db + tbl = swsscommon.Table(db, "VLAN_MEMBER") + fvs = swsscommon.FieldValuePairs([("tagging_mode", "tagged")]) + tbl.set("Vlan100|Ethernet24", fvs) + + time.sleep(1) + + # create vlan interface in config db + tbl = swsscommon.Table(db, "VLAN_INTERFACE") + fvs = swsscommon.FieldValuePairs([("family", "IPv4")]) + tbl.set("Vlan100|192.169.0.1/27", fvs) + + time.sleep(1) + + # check vlan in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") + keys = atbl.getKeys() + vlan_oid = None + + for key in keys: + if key == dvs.asicdb.default_vlan_id: + continue + + (status, fvs) = atbl.get(key) + assert status == True + + if fvs[0][0] == "SAI_VLAN_ATTR_VLAN_ID": + assert fvs[0][1] == '100' + vlan_oid = key + + assert vlan_oid != None + + # check router interface in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + keys = atbl.getKeys() + rif_oid = None + + for key in keys: + (status, fvs) = atbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_VLAN_ID": + assert vlan_oid == fv[1] + rif_oid = key + + assert rif_oid != None + + # check neighbor entry in asic db + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEIGHBOR_ENTRY") + keys = atbl.getKeys() + dir_bcast = False + + for key in keys: + neigh = json.loads(key) + + if neigh['ip'] == "192.169.0.31": + dir_bcast = True + assert neigh['rif'] == rif_oid + (status, fvs) = atbl.get(key) + assert status == True + if fvs[0][0] == "SAI_NEIGHBOR_ENTRY_ATTR_DST_MAC_ADDRESS": + assert fvs[0][1] == "FF:FF:FF:FF:FF:FF" + + assert dir_bcast diff --git a/tests/test_fdb.py b/tests/test_fdb.py new file mode 100644 index 0000000000..d873a9f6df --- /dev/null +++ b/tests/test_fdb.py @@ -0,0 +1,131 @@ +from swsscommon import swsscommon +import time +import json +from pprint import pprint + + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + + +def get_map_iface_bridge_port_id(asic_db, dvs): + port_id_2_iface = dvs.asicdb.portoidmap + tbl = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") + iface_2_bridge_port_id = {} + for key in tbl.getKeys(): + status, data = tbl.get(key) + assert status + values = dict(data) + iface_id = values["SAI_BRIDGE_PORT_ATTR_PORT_ID"] + iface_name = port_id_2_iface[iface_id] + iface_2_bridge_port_id[iface_name] = key + + return iface_2_bridge_port_id + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + + +def is_fdb_entry_exists(db, table, key_values, attributes): + tbl = swsscommon.Table(db, table) + keys = tbl.getKeys() + + exists = True + extra_info = [] + key_found = False + for key in keys: + d_key = json.loads(key) + for k, v in key_values: + if k not in d_key or v != d_key[k]: + continue + + key_found = True + + status, fvs = tbl.get(key) + assert status, "Error reading from table %s" % table + + d_attributes = dict(attributes) + for k, v in fvs: + if k in d_attributes and d_attributes[k] == v: + del d_attributes[k] + + if len(d_attributes) != 0: + exists = False + extra_info.append("Desired attributes %s was not found for key %s" % (str(d_attributes), key)) + + break + + if not key_found: + exists = False + extra_info.append("Desired key with parameters %s was not found" % str(key_values)) + + return exists, extra_info + + +def test_FDBAddedAfterMemberCreated(dvs): + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + # create a FDB entry in Application DB + create_entry_pst( + appl_db, + "FDB_TABLE", "Vlan2:52-54-00-25-06-E9", + [ + ("port", "Ethernet0"), + ("type", "dynamic"), + ] + ) + + # check that the FDB entry wasn't inserted into ASIC DB + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 0, "The fdb entry leaked to ASIC" + + # create vlan + create_entry_tbl( + conf_db, + "VLAN", "Vlan2", + [ + ("vlanid", "2"), + ] + ) + + # create vlan member entry in application db + create_entry_tbl( + conf_db, + "VLAN_MEMBER", "Vlan2|Ethernet0", + [ + ("tagging_mode", "untagged"), + ] + ) + + # check that the vlan information was propagated + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN") == 2, "The 2 vlan wasn't created" + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_BRIDGE_PORT") == 1, "The bridge port wasn't created" + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VLAN_MEMBER") == 1, "The vlan member wasn't added" + + # Get mapping between interface name and its bridge port_id + iface_2_bridge_port_id = get_map_iface_bridge_port_id(asic_db, dvs) + + # check that the FDB entry was inserted into ASIC DB + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY") == 1, "The fdb entry wasn't inserted to ASIC" + + ok, extra = is_fdb_entry_exists(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_FDB_ENTRY", + [("mac", "52-54-00-25-06-E9"), ("vlan", "2")], + [("SAI_FDB_ENTRY_ATTR_TYPE", "SAI_FDB_ENTRY_TYPE_DYNAMIC"), + ("SAI_FDB_ENTRY_ATTR_BRIDGE_PORT_ID", iface_2_bridge_port_id["Ethernet0"]), + ('SAI_FDB_ENTRY_ATTR_PACKET_ACTION', 'SAI_PACKET_ACTION_FORWARD')] + ) + assert ok, str(extra) diff --git a/tests/test_nhg.py b/tests/test_nhg.py new file mode 100644 index 0000000000..bf41c793d2 --- /dev/null +++ b/tests/test_nhg.py @@ -0,0 +1,128 @@ +from swsscommon import swsscommon +import os +import re +import time +import json + +def test_route_nhg(dvs): + + dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") + dvs.runcmd("ifconfig Ethernet4 10.0.0.2/31 up") + dvs.runcmd("ifconfig Ethernet8 10.0.0.4/31 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") + dvs.runcmd("arp -s 10.0.0.5 00:00:00:00:00:03") + + dvs.servers[0].runcmd("ip link set down dev eth0") == 0 + dvs.servers[1].runcmd("ip link set down dev eth0") == 0 + dvs.servers[2].runcmd("ip link set down dev eth0") == 0 + + dvs.servers[0].runcmd("ip link set up dev eth0") == 0 + dvs.servers[1].runcmd("ip link set up dev eth0") == 0 + dvs.servers[2].runcmd("ip link set up dev eth0") == 0 + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + ps = swsscommon.ProducerStateTable(db, "ROUTE_TABLE") + fvs = swsscommon.FieldValuePairs([("nexthop","10.0.0.1,10.0.0.3,10.0.0.5"), ("ifname", "Ethernet0,Ethernet4,Ethernet8")]) + + ps.set("2.2.2.0/24", fvs) + + time.sleep(1) + + # check if route was propagated to ASIC DB + + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + rtbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + nhgtbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP") + nhg_member_tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_NEXT_HOP_GROUP_MEMBER") + + keys = rtbl.getKeys() + + found_route = False + for k in keys: + rt_key = json.loads(k) + + if rt_key['dest'] == "2.2.2.0/24": + found_route = True + break + + assert found_route + + # assert the route points to next hop group + (status, fvs) = rtbl.get(k) + + for v in fvs: + if v[0] == "SAI_ROUTE_ENTRY_ATTR_NEXT_HOP_ID": + nhgid = v[1] + + (status, fvs) = nhgtbl.get(nhgid) + + assert status + + keys = nhg_member_tbl.getKeys() + + assert len(keys) == 3 + + for k in keys: + (status, fvs) = nhg_member_tbl.get(k) + + for v in fvs: + if v[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": + assert v[1] == nhgid + + # bring links down one-by-one + for i in [0, 1, 2]: + dvs.servers[i].runcmd("ip link set down dev eth0") == 0 + + time.sleep(1) + + tbl = swsscommon.Table(db, "PORT_TABLE") + (status, fvs) = tbl.get("Ethernet%d" % (i * 4)) + + assert status == True + + oper_status = "unknown" + + for v in fvs: + if v[0] == "oper_status": + oper_status = v[1] + break + + assert oper_status == "down" + + keys = nhg_member_tbl.getKeys() + + assert len(keys) == 2 - i + + # bring links up one-by-one + for i in [0, 1, 2]: + dvs.servers[i].runcmd("ip link set up dev eth0") == 0 + + time.sleep(1) + + tbl = swsscommon.Table(db, "PORT_TABLE") + (status, fvs) = tbl.get("Ethernet0") + + assert status == True + + oper_status = "unknown" + + for v in fvs: + if v[0] == "oper_status": + oper_status = v[1] + break + + assert oper_status == "up" + + keys = nhg_member_tbl.getKeys() + + assert len(keys) == i + 1 + + for k in keys: + (status, fvs) = nhg_member_tbl.get(k) + + for v in fvs: + if v[0] == "SAI_NEXT_HOP_GROUP_MEMBER_ATTR_NEXT_HOP_GROUP_ID": + assert v[1] == nhgid diff --git a/tests/test_setro.py b/tests/test_setro.py new file mode 100755 index 0000000000..1f619e6646 --- /dev/null +++ b/tests/test_setro.py @@ -0,0 +1,40 @@ +from swsscommon import swsscommon +import time +import json +import redis +from pprint import pprint + +def test_SetReadOnlyAttribute(dvs): + + db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + tbl = swsscommon.Table(db, "ASIC_STATE:SAI_OBJECT_TYPE_SWITCH") + + keys = tbl.getKeys() + + assert len(keys) == 1 + + swVid = keys[0] + + r = redis.Redis(unix_socket_path=dvs.redis_sock, db=swsscommon.ASIC_DB) + + swRid = r.hget("VIDTORID", swVid) + + assert swRid is not None + + ntf = swsscommon.NotificationProducer(db, "SAI_VS_UNITTEST_CHANNEL") + + fvp = swsscommon.FieldValuePairs() + + ntf.send("enable_unittests", "true", fvp) + + fvp = swsscommon.FieldValuePairs([('SAI_SWITCH_ATTR_PORT_MAX_MTU', '42')]) + + key = "SAI_OBJECT_TYPE_SWITCH:" + swRid + + print key + + ntf.send("set_ro", key, fvp) + + # make action on appdb so orchagent will get RO value + # read asic db to see if orchagent behaved correctly diff --git a/tests/test_speed.py b/tests/test_speed.py new file mode 100644 index 0000000000..c4f92a7ef7 --- /dev/null +++ b/tests/test_speed.py @@ -0,0 +1,85 @@ +""" + test_speed.py implements list of tests to check speed set on + interfaces and correct buffer manager behavior on speed change + + These tests need to be run in prepared environment and with the + SONiC version compiled for PLATFORM=vs + + See README.md for details +""" +from swsscommon import swsscommon +import time +import re +import json +import os + +class TestSpeedSet(object): + num_ports = 32 + def test_SpeedAndBufferSet(self, dvs): + speed_list = ['50000', '25000', '40000', '10000', '100000'] + + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + cfg_port_table = swsscommon.Table(cdb, "PORT") + cfg_buffer_profile_table = swsscommon.Table(cdb, "BUFFER_PROFILE") + cfg_buffer_pg_table = swsscommon.Table(cdb, "BUFFER_PG") + asic_port_table = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + asic_profile_table = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_BUFFER_PROFILE") + + buffer_profiles = cfg_buffer_profile_table.getKeys() + expected_buffer_profiles_num = len(buffer_profiles) + # buffers.json used for the test defines 7 static profiles: + # "ingress_lossless_profile" + # "ingress_lossy_profile" + # "egress_lossless_profile" + # "egress_lossy_profile" + # "pg_lossy_profile" + # "q_lossless_profile" + # "q_lossy_profile" + # check if they get the DB + assert expected_buffer_profiles_num == 7 + # and if they were successfully created on ASIC + assert len(asic_profile_table.getKeys()) == expected_buffer_profiles_num + + for speed in speed_list: + fvs = swsscommon.FieldValuePairs([("speed", speed)]) + # set same speed on all ports + for i in range(0, self.num_ports): + cfg_port_table.set("Ethernet%d" % (i*4), fvs) + + time.sleep(1) # let configuration settle down + + # check the speed was set + asic_port_records = asic_port_table.getKeys() + assert len(asic_port_records) == (self.num_ports + 1) # +CPU port + num_set = 0 + for k in asic_port_records: + (status, fvs) = asic_port_table.get(k) + assert status == True + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_SPEED": + assert fv[1] == speed + num_set += 1 + # make sure speed is set for all "num_ports" ports + assert num_set == self.num_ports + + # check number of created profiles + expected_buffer_profiles_num += 1 # new speed should add new PG profile + current_buffer_profiles = cfg_buffer_profile_table.getKeys() + assert len(current_buffer_profiles) == expected_buffer_profiles_num + # make sure the same number of profiles are created on ASIC + assert len(asic_profile_table.getKeys()) == expected_buffer_profiles_num + + # check new profile name + expected_new_profile_name = "pg_lossless_%s_300m_profile" % speed + assert current_buffer_profiles.index(expected_new_profile_name) > -1 + + # check correct profile is set for all ports + pg_tables = cfg_buffer_pg_table.getKeys() + for i in range(0, self.num_ports): + expected_pg_table = "Ethernet%d|3-4" % (i*4) + assert pg_tables.index(expected_pg_table) > -1 + (status, fvs) = cfg_buffer_pg_table.get(expected_pg_table) + for fv in fvs: + if fv[0] == "profile": + assert fv[1] == "[BUFFER_PROFILE|%s]" % expected_new_profile_name diff --git a/tests/test_vlan.py b/tests/test_vlan.py index 85bc74a319..76b56b9c1a 100644 --- a/tests/test_vlan.py +++ b/tests/test_vlan.py @@ -10,7 +10,7 @@ def test_VlanMemberCreation(dvs): # create vlan in config db - tbl = swsscommon.Table(db, "VLAN", '|') + tbl = swsscommon.Table(db, "VLAN") fvs = swsscommon.FieldValuePairs([("vlanid", "2")]) tbl.set("Vlan2", fvs) @@ -38,7 +38,7 @@ def test_VlanMemberCreation(dvs): assert vlan_oid != None # create vlan member in config db - tbl = swsscommon.Table(db, "VLAN_MEMBER", '|') + tbl = swsscommon.Table(db, "VLAN_MEMBER") fvs = swsscommon.FieldValuePairs([("tagging_mode", "untagged")]) tbl.set("Vlan2|Ethernet0", fvs) diff --git a/tests/test_vrf.py b/tests/test_vrf.py new file mode 100644 index 0000000000..9f3431ee6a --- /dev/null +++ b/tests/test_vrf.py @@ -0,0 +1,235 @@ +from swsscommon import swsscommon +import time +import json +import random +import time +from pprint import pprint + + +def create_entry(tbl, key, pairs): + fvs = swsscommon.FieldValuePairs(pairs) + tbl.set(key, fvs) + + # FIXME: better to wait until DB create them + time.sleep(1) + + +def create_entry_tbl(db, table, key, pairs): + tbl = swsscommon.Table(db, table) + create_entry(tbl, key, pairs) + + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + + +def delete_entry_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()) + + +def entries(db, table): + tbl = swsscommon.Table(db, table) + return set(tbl.getKeys()) + + +def is_vrf_attributes_correct(db, table, key, expected_attributes): + tbl = swsscommon.Table(db, table) + keys = set(tbl.getKeys()) + assert key in keys, "The created key wasn't found" + + status, fvs = tbl.get(key) + assert status, "Got an error when get a key" + + # filter the fake 'NULL' attribute out + fvs = filter(lambda x : x != ('NULL', 'NULL'), fvs) + + attr_keys = {entry[0] for entry in fvs} + assert attr_keys == set(expected_attributes.keys()) + + for name, value in fvs: + assert expected_attributes[name] == value, "Wrong value %s for the attribute %s = %s" % \ + (value, name, expected_attributes[name]) + + +def vrf_create(asic_db, conf_db, vrf_name, attributes, expected_attributes): + # check that the vrf wasn't exist before + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") == 1, "The initial state is incorrect" + + # read existing entries in the DB + initial_entries = entries(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") + + # create a fake attribute if we don't have attributes in the request + if len(attributes) == 0: + attributes = [('empty', 'empty')] + + # create the VRF entry in Config DB + create_entry_tbl(conf_db, "VRF", vrf_name, attributes) + + # check that the vrf entry was created + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") == 2, "The vrf wasn't created" + + # find the id of the entry which was added + added_entry_id = list(entries(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") - initial_entries)[0] + + # check correctness of the created attributes + is_vrf_attributes_correct( + asic_db, + "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER", + added_entry_id, + expected_attributes, + ) + + state = { + 'initial_entries': initial_entries, + 'entry_id': added_entry_id, + } + + return state + + +def vrf_remove(asic_db, conf_db, vrf_name, state): + # delete the created vrf entry + delete_entry_tbl(conf_db, "VRF", vrf_name) + + # check that the vrf entry was removed + assert how_many_entries_exist(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER") == 1, "The vrf wasn't removed" + + # check that the correct vrf entry was removed + assert state['initial_entries'] == entries(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER"), "The incorrect entry was removed" + + +def vrf_update(asic_db, conf_db, vrf_name, attributes, expected_attributes, state): + # update the VRF entry in Config DB + create_entry_tbl(conf_db, "VRF", vrf_name, attributes) + + # check correctness of the created attributes + is_vrf_attributes_correct( + asic_db, + "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER", + state['entry_id'], + expected_attributes, + ) + + +def boolean_gen(): + result = random.choice(['false', 'true']) + return result, result + + +def mac_addr_gen(): + ns = [random.randint(0, 255) for _ in xrange(6)] + ns[0] &= 0xfe + mac = ':'.join("%02x" % n for n in ns) + return mac, mac.upper() + + +def packet_action_gen(): + values = [ + ("drop", "SAI_PACKET_ACTION_DROP"), + ("forward", "SAI_PACKET_ACTION_FORWARD"), + ("copy", "SAI_PACKET_ACTION_COPY"), + ("copy_cancel", "SAI_PACKET_ACTION_COPY_CANCEL"), + ("trap", "SAI_PACKET_ACTION_TRAP"), + ("log", "SAI_PACKET_ACTION_LOG"), + ("deny", "SAI_PACKET_ACTION_DENY"), + ("transit", "SAI_PACKET_ACTION_TRANSIT"), + ] + + r = random.choice(values) + return r[0], r[1] + + +def test_VRFOrch_Comprehensive(dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attributes = [ + ('v4', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE', boolean_gen), + ('v6', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE', boolean_gen), + ('src_mac', 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS', mac_addr_gen), + ('ttl_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_TTL1_PACKET_ACTION', packet_action_gen), + ('ip_opt_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_IP_OPTIONS_PACKET_ACTION', packet_action_gen), + ('l3_mc_action', 'SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION', packet_action_gen), + ] + + random.seed(int(time.clock())) + + for n in xrange(2**len(attributes)): + # generate testcases for all combinations of attributes + req_attr = [] + exp_attr = {} + vrf_name = "vrf_%d" % n + bmask = 0x1 + for an in xrange(len(attributes)): + if (bmask & n) > 0: + req_res, exp_res = attributes[an][2]() + req_attr.append((attributes[an][0], req_res)) + exp_attr[attributes[an][1]] = exp_res + bmask <<= 1 + state = vrf_create(asic_db, conf_db, vrf_name, req_attr, exp_attr) + vrf_remove(asic_db, conf_db, vrf_name, state) + + +def test_VRFOrch(dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + state = vrf_create(asic_db, conf_db, "vrf0", + [ + ], + { + } + ) + vrf_remove(asic_db, conf_db, "vrf0", state) + + state = vrf_create(asic_db, conf_db, "vrf1", + [ + ('v4', 'true'), + ('src_mac', '02:04:06:07:08:09'), + ], + { + 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE': 'true', + 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS': '02:04:06:07:08:09', + } + ) + vrf_remove(asic_db, conf_db, "vrf1", state) + +def test_VRFOrch_Update(dvs): + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + attributes = [ + ('v4', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V4_STATE', boolean_gen), + ('v6', 'SAI_VIRTUAL_ROUTER_ATTR_ADMIN_V6_STATE', boolean_gen), + ('src_mac', 'SAI_VIRTUAL_ROUTER_ATTR_SRC_MAC_ADDRESS', mac_addr_gen), + ('ttl_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_TTL1_PACKET_ACTION', packet_action_gen), + ('ip_opt_action', 'SAI_VIRTUAL_ROUTER_ATTR_VIOLATION_IP_OPTIONS_PACKET_ACTION', packet_action_gen), + ('l3_mc_action', 'SAI_VIRTUAL_ROUTER_ATTR_UNKNOWN_L3_MULTICAST_PACKET_ACTION', packet_action_gen), + ] + + random.seed(int(time.clock())) + + state = vrf_create(asic_db, conf_db, "vrf_a", + [ + ], + { + } + ) + + # try to update each attribute + req_attr = [] + exp_attr = {} + for attr in attributes: + req_res, exp_res = attr[2]() + req_attr.append((attr[0], req_res)) + exp_attr[attr[1]] = exp_res + vrf_update(asic_db, conf_db, "vrf_a", req_attr, exp_attr, state) + + vrf_remove(asic_db, conf_db, "vrf_a", state)