diff --git a/README.md b/README.md index 4a23c5e171..e1641405e1 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -Broadcom[![Broadcom](https://sonic-jenkins.westus2.cloudapp.azure.com/job/broadcom/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus.cloudapp.azure.com/job/broadcom/job/sonic-swss-build/) -Cavium[![Cavium](https://sonic-jenkins.westus2.cloudapp.azure.com/job/cavium/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus.cloudapp.azure.com/job/cavium/job/sonic-swss-build/) -Mellanox[![Mellanox](https://sonic-jenkins.westus2.cloudapp.azure.com/job/mellanox/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus.cloudapp.azure.com/job/mellanox/job/sonic-swss-build/) -P4[![P4](https://sonic-jenkins.westus2.cloudapp.azure.com/job/p4/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus.cloudapp.azure.com/job/p4/job/sonic-swss-build/) +Broadcom[![Broadcom](https://sonic-jenkins.westus2.cloudapp.azure.com/job/broadcom/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/broadcom/job/sonic-swss-build/) +Cavium[![Cavium](https://sonic-jenkins.westus2.cloudapp.azure.com/job/cavium/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/cavium/job/sonic-swss-build/) +Mellanox[![Mellanox](https://sonic-jenkins.westus2.cloudapp.azure.com/job/mellanox/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/mellanox/job/sonic-swss-build/) +P4[![P4](https://sonic-jenkins.westus2.cloudapp.azure.com/job/p4/job/sonic-swss-build/badge/icon)](https://sonic-jenkins.westus2.cloudapp.azure.com/job/p4/job/sonic-swss-build/) # SONiC - SWitch State Service - SWSS diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index 258d7659be..9585140477 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -1,7 +1,7 @@ -INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/orchagent +INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/orchagent -I $(top_srcdir)/warmrestart CFLAGS_SAI = -I /usr/include/sai -bin_PROGRAMS = vlanmgrd intfmgrd buffermgrd +bin_PROGRAMS = vlanmgrd portmgrd intfmgrd buffermgrd if DEBUG DBGFLAGS = -ggdb -DDEBUG @@ -9,11 +9,17 @@ else DBGFLAGS = -g endif -vlanmgrd_SOURCES = vlanmgrd.cpp vlanmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h +vlanmgrd_SOURCES = vlanmgrd.cpp vlanmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp \ + shellcmd.h $(top_srcdir)/warmrestart/warm_restart.cpp $(top_srcdir)/warmrestart/warm_restart.h vlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vlanmgrd_LDADD = -lswsscommon +portmgrd_SOURCES = portmgrd.cpp portmgr.cpp $(top_srcdir)/orchagent/orch.cpp $(top_srcdir)/orchagent/request_parser.cpp shellcmd.h +portmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) +portmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) +portmgrd_LDADD = -lswsscommon + 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) diff --git a/cfgmgr/buffermgr.cpp b/cfgmgr/buffermgr.cpp index 89b01970bc..6376b694e0 100644 --- a/cfgmgr/buffermgr.cpp +++ b/cfgmgr/buffermgr.cpp @@ -15,7 +15,6 @@ 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), @@ -31,9 +30,12 @@ void BufferMgr::readPgProfileLookupFile(string file) { SWSS_LOG_NOTICE("Read lookup configuration file..."); + m_pgfile_processed = false; + ifstream infile(file); if (!infile.is_open()) { + SWSS_LOG_WARN("PG profile lookup file: %s is not readable", file.c_str()); return; } @@ -69,6 +71,7 @@ void BufferMgr::readPgProfileLookupFile(string file) ); } + m_pgfile_processed = true; infile.close(); } @@ -211,7 +214,7 @@ void BufferMgr::doTask(Consumer &consumer) 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") + if (m_pgfile_processed && table_name == CFG_PORT_TABLE_NAME && fvField(i) == "speed") { // create/update profile for port task_status = doSpeedUpdateTask(port, fvValue(i)); diff --git a/cfgmgr/buffermgr.h b/cfgmgr/buffermgr.h index 3c00988e65..d3ef1d50a8 100644 --- a/cfgmgr/buffermgr.h +++ b/cfgmgr/buffermgr.h @@ -33,12 +33,12 @@ class BufferMgr : public Orch using Orch::doTask; private: - Table m_statePortTable; Table m_cfgPortTable; Table m_cfgCableLenTable; Table m_cfgBufferProfileTable; Table m_cfgBufferPgTable; Table m_cfgLosslessPgPoolTable; + bool m_pgfile_processed; pg_profile_lookup_t m_pgProfileLookup; port_cable_length_t m_cableLenLookup; diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp index 7e30fa1bd9..fb14df97da 100644 --- a/cfgmgr/intfmgr.cpp +++ b/cfgmgr/intfmgr.cpp @@ -25,12 +25,20 @@ IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c { } -bool IntfMgr::setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr) +bool IntfMgr::setIntfIp(const string &alias, const string &opCmd, + const string &ipPrefixStr, const bool ipv4) { stringstream cmd; string res; - cmd << IP_CMD << " address " << opCmd << " " << ipPrefixStr << " dev " << alias;; + if (ipv4) + { + cmd << IP_CMD << " address " << opCmd << " " << ipPrefixStr << " dev " << alias; + } + else + { + cmd << IP_CMD << " -6 address " << opCmd << " " << ipPrefixStr << " dev " << alias; + } int ret = swss::exec(cmd.str(), res); return (ret == 0); } @@ -72,34 +80,24 @@ void IntfMgr::doTask(Consumer &consumer) { KeyOpFieldsValuesTuple t = it->second; - string keySeparator = CONFIGDB_KEY_SEPARATOR; - vector keys = tokenize(kfvKey(t), keySeparator[0]); - string alias(keys[0]); + vector keys = tokenize(kfvKey(t), config_db_key_delimiter); - if (alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) + if (keys.size() != 2) { - /* handle IP over vlan Only for now, skip the rest */ + SWSS_LOG_ERROR("Invalid key %s", kfvKey(t).c_str()); it = consumer.m_toSync.erase(it); continue; } - size_t pos = kfvKey(t).find(CONFIGDB_KEY_SEPARATOR); - if (pos == string::npos) - { - SWSS_LOG_DEBUG("Invalid key %s", kfvKey(t).c_str()); - it = consumer.m_toSync.erase(it); - continue; - } - IpPrefix ip_prefix(kfvKey(t).substr(pos+1)); - - SWSS_LOG_DEBUG("intfs doTask: %s", (dumpTuple(consumer, t)).c_str()); + string alias(keys[0]); + IpPrefix ip_prefix(keys[1]); string op = kfvOp(t); if (op == SET_COMMAND) { /* - * Don't proceed if port/lag/VLAN is not ready yet. - * The pending task will be checked periodially and retried. + * Don't proceed if port/LAG/VLAN is not ready yet. + * The pending task will be checked periodically and retried. * TODO: Subscribe to stateDB for port/lag/VLAN state and retry * pending tasks immediately upon state change. */ @@ -109,18 +107,17 @@ void IntfMgr::doTask(Consumer &consumer) it++; continue; } - string opCmd("add"); - string ipPrefixStr = ip_prefix.to_string(); - setIntfIp(alias, opCmd, ipPrefixStr); + setIntfIp(alias, "add", ip_prefix.to_string(), ip_prefix.isV4()); } else if (op == DEL_COMMAND) { - string opCmd("del"); - string ipPrefixStr = ip_prefix.to_string(); - setIntfIp(alias, opCmd, ipPrefixStr); + setIntfIp(alias, "del", ip_prefix.to_string(), ip_prefix.isV4()); + } + else + { + SWSS_LOG_ERROR("Unknown operation: %s", op.c_str()); } it = consumer.m_toSync.erase(it); - continue; } } diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h index b7460cb3ac..3d79d6d477 100644 --- a/cfgmgr/intfmgr.h +++ b/cfgmgr/intfmgr.h @@ -21,7 +21,7 @@ class IntfMgr : public Orch Table m_cfgIntfTable, m_cfgVlanIntfTable; Table m_statePortTable, m_stateLagTable, m_stateVlanTable; - bool setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr); + bool setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr, const bool ipv4 = true); void doTask(Consumer &consumer); bool isIntfStateOk(const string &alias); }; diff --git a/cfgmgr/portmgr.cpp b/cfgmgr/portmgr.cpp new file mode 100644 index 0000000000..1dc74b9367 --- /dev/null +++ b/cfgmgr/portmgr.cpp @@ -0,0 +1,109 @@ +#include +#include "logger.h" +#include "dbconnector.h" +#include "producerstatetable.h" +#include "tokenize.h" +#include "ipprefix.h" +#include "portmgr.h" +#include "exec.h" +#include "shellcmd.h" + +using namespace std; +using namespace swss; + +PortMgr::PortMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : + Orch(cfgDb, tableNames), + m_cfgPortTable(cfgDb, CFG_PORT_TABLE_NAME), + m_cfgLagTable(cfgDb, CFG_LAG_TABLE_NAME), + m_statePortTable(stateDb, STATE_PORT_TABLE_NAME), + m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME) +{ +} + +bool PortMgr::setPortMtu(const string &alias, const string &mtu) +{ + stringstream cmd; + string res; + + cmd << IP_CMD << " link set dev " << alias << " mtu " << mtu; + return exec(cmd.str(), res) == 0; +} + +bool PortMgr::setPortAdminStatus(const string &alias, const bool up) +{ + stringstream cmd; + string res; + + cmd << IP_CMD << " link set dev " << alias << (up ? " up" : " down"); + return exec(cmd.str(), res) == 0; +} + +bool PortMgr::isPortStateOk(const string &table, const string &alias) +{ + vector temp; + + if (table == CFG_PORT_TABLE_NAME) + { + if (m_statePortTable.get(alias, temp)) + { + SWSS_LOG_INFO("Port %s is ready", alias.c_str()); + return true; + } + } + else if (table == CFG_LAG_TABLE_NAME) + { + if (m_stateLagTable.get(alias, temp)) + { + SWSS_LOG_INFO("Lag %s is ready", alias.c_str()); + return true; + } + } + + return false; +} + +void PortMgr::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto table = consumer.getTableName(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string alias = kfvKey(t); + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + if (!isPortStateOk(table, alias)) + { + SWSS_LOG_INFO("Port %s is not ready, pending", alias.c_str()); + it++; + continue; + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == "mtu") + { + auto mtu = fvValue(i); + setPortMtu(alias, mtu); + SWSS_LOG_NOTICE("Configure %s MTU to %s", + alias.c_str(), mtu.c_str()); + } + else if (fvField(i) == "admin_status") + { + auto status = fvValue(i); + setPortAdminStatus(alias, status == "up"); + SWSS_LOG_NOTICE("Configure %s %s", + alias.c_str(), status.c_str()); + } + } + } + + it = consumer.m_toSync.erase(it); + } +} diff --git a/cfgmgr/portmgr.h b/cfgmgr/portmgr.h new file mode 100644 index 0000000000..341f95c588 --- /dev/null +++ b/cfgmgr/portmgr.h @@ -0,0 +1,33 @@ +#ifndef __INTFMGR__ +#define __INTFMGR__ + +#include "dbconnector.h" +#include "producerstatetable.h" +#include "orch.h" + +#include +#include + +namespace swss { + +class PortMgr : public Orch +{ +public: + PortMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames); + using Orch::doTask; + +private: + Table m_cfgPortTable; + Table m_cfgLagTable; + Table m_statePortTable; + Table m_stateLagTable; + + void doTask(Consumer &consumer); + bool setPortMtu(const string &alias, const string &mtu); + bool setPortAdminStatus(const string &alias, const bool up); + bool isPortStateOk(const string &table, const string &alias); +}; + +} + +#endif diff --git a/cfgmgr/portmgrd.cpp b/cfgmgr/portmgrd.cpp new file mode 100644 index 0000000000..5855c75c3f --- /dev/null +++ b/cfgmgr/portmgrd.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include "dbconnector.h" +#include "select.h" +#include "exec.h" +#include "schema.h" +#include "portmgr.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; + +int main(int argc, char **argv) +{ + Logger::linkToDbNative("portmgrd"); + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("--- Starting portmgrd ---"); + + try + { + vector cfg_port_tables = { + CFG_PORT_TABLE_NAME, + CFG_LAG_TABLE_NAME, + }; + + DBConnector cfgDb(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + + PortMgr portmgr(&cfgDb, &appDb, &stateDb, cfg_port_tables); + + // TODO: add tables in stateDB which interface depends on to monitor list + std::vector cfgOrchList = {&portmgr}; + + swss::Select s; + for (Orch *o : cfgOrchList) + { + s.addSelectables(o->getSelectables()); + } + + 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) + { + portmgr.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/vlanmgr.cpp b/cfgmgr/vlanmgr.cpp index 215fc67eae..bed88c19ce 100644 --- a/cfgmgr/vlanmgr.cpp +++ b/cfgmgr/vlanmgr.cpp @@ -6,6 +6,7 @@ #include "exec.h" #include "tokenize.h" #include "shellcmd.h" +#include "warm_restart.h" using namespace std; using namespace swss; @@ -26,11 +27,26 @@ VlanMgr::VlanMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, c m_statePortTable(stateDb, STATE_PORT_TABLE_NAME), m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME), m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME), + m_stateVlanMemberTable(stateDb, STATE_VLAN_MEMBER_TABLE_NAME), m_appVlanTableProducer(appDb, APP_VLAN_TABLE_NAME), m_appVlanMemberTableProducer(appDb, APP_VLAN_MEMBER_TABLE_NAME) { SWSS_LOG_ENTER(); + if (WarmStart::isWarmStart()) + { + const std::string cmds = std::string("") + + IP_CMD + " link show " + DOT1Q_BRIDGE_NAME + " 2>/dev/null"; + + std::string res; + int ret = swss::exec(cmds, res); + if (ret == 0) + { + // Don't reset vlan aware bridge upon swss docker warm restart. + SWSS_LOG_INFO("vlanmgrd warm start, skipping bridge create"); + return; + } + } // Initialize Linux dot1q bridge and enable vlan filtering // The command should be generated as: // /bin/bash -c "/sbin/ip link del Bridge 2>/dev/null ; @@ -172,17 +188,22 @@ bool VlanMgr::removeHostVlanMember(int vlan_id, const string &port_alias) SWSS_LOG_ENTER(); // 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" + // /bin/bash -c '/sbin/bridge vlan del vid {{vlan_id}} dev {{port_alias}} && + // ( /sbin/bridge vlan show dev {{port_alias}} | /bin/grep -q None; + // ret=$?; if [ $ret -eq 0 ]; then + // /sbin/ip link set {{port_alias}} nomaster; + // elif [ $ret -eq 1 ]; then exit 0; + // else exit $ret; fi )' // When port is not member of any VLAN, it shall be detached from Dot1Q bridge! const std::string cmds = std::string("") - + BASH_CMD + " -c \"" - + BRIDGE_CMD + " vlan del vid " + std::to_string(vlan_id) + " dev " + port_alias + " && " + + 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\""; + + GREP_CMD + " -q None; ret=$?; if [ $ret -eq 0 ]; then " + + IP_CMD + " link set " + port_alias + " nomaster; " + + "elif [ $ret -eq 1 ]; then exit 0; " + + "else exit $ret; fi )\'"; std::string res; EXEC_WITH_ERROR_THROW(cmds, res); @@ -231,6 +252,19 @@ void VlanMgr::doVlanTask(Consumer &consumer) vector fvVector; string members; + /* + * Don't program vlan again if state is already set. + * will hit this for docker warm restart. + * Just set the internal data structure and remove the request. + */ + if (isVlanStateOk(key)) + { + m_vlans.insert(key); + it = consumer.m_toSync.erase(it); + SWSS_LOG_DEBUG("%s already created", kfvKey(t).c_str()); + continue; + } + /* Add host VLAN when it has not been created. */ if (m_vlans.find(key) == m_vlans.end()) { @@ -350,6 +384,18 @@ bool VlanMgr::isVlanStateOk(const string &alias) return false; } +bool VlanMgr::isVlanMemberStateOk(const string &vlanMemberKey) +{ + vector temp; + + if (m_stateVlanMemberTable.get(vlanMemberKey, temp)) + { + SWSS_LOG_DEBUG("%s is ready", vlanMemberKey.c_str()); + return true; + } + return false; +} + /* * members is grouped in format like * "Ethernet1,Ethernet2,Ethernet3,Ethernet4,Ethernet5,Ethernet6, @@ -437,6 +483,13 @@ void VlanMgr::doVlanMemberTask(Consumer &consumer) // TODO: store port/lag/VLAN data in local data structure and perform more validations. if (op == SET_COMMAND) { + if (isVlanMemberStateOk(kfvKey(t))) + { + SWSS_LOG_DEBUG("%s already set", kfvKey(t).c_str()); + it = consumer.m_toSync.erase(it); + continue; + } + /* Don't proceed if member port/lag is not ready yet */ if (!isMemberStateOk(port_alias) || !isVlanStateOk(vlan_alias)) { @@ -469,24 +522,36 @@ void VlanMgr::doVlanMemberTask(Consumer &consumer) key += DEFAULT_KEY_SEPARATOR; key += port_alias; m_appVlanMemberTableProducer.set(key, kfvFieldsValues(t)); + + vector fvVector; + FieldValueTuple s("state", "ok"); + fvVector.push_back(s); + m_stateVlanMemberTable.set(kfvKey(t), fvVector); } - it = consumer.m_toSync.erase(it); } else if (op == DEL_COMMAND) { - removeHostVlanMember(vlan_id, port_alias); - key = VLAN_PREFIX + to_string(vlan_id); - key += DEFAULT_KEY_SEPARATOR; - key += port_alias; - m_appVlanMemberTableProducer.del(key); + if (isVlanMemberStateOk(kfvKey(t))) + { + removeHostVlanMember(vlan_id, port_alias); + key = VLAN_PREFIX + to_string(vlan_id); + key += DEFAULT_KEY_SEPARATOR; + key += port_alias; + m_appVlanMemberTableProducer.del(key); + m_stateVlanMemberTable.del(kfvKey(t)); + } + else + { + SWSS_LOG_DEBUG("%s doesn't exist", kfvKey(t).c_str()); + } SWSS_LOG_DEBUG("%s", (dumpTuple(consumer, t)).c_str()); - it = consumer.m_toSync.erase(it); } else { SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); - it = consumer.m_toSync.erase(it); } + /* Other than the case of member port/lag is not ready, no retry will be performed */ + it = consumer.m_toSync.erase(it); } } diff --git a/cfgmgr/vlanmgr.h b/cfgmgr/vlanmgr.h index 4a04da22a5..aff14999c2 100644 --- a/cfgmgr/vlanmgr.h +++ b/cfgmgr/vlanmgr.h @@ -21,7 +21,7 @@ class VlanMgr : public Orch ProducerStateTable m_appVlanTableProducer, m_appVlanMemberTableProducer; Table m_cfgVlanTable, m_cfgVlanMemberTable; Table m_statePortTable, m_stateLagTable; - Table m_stateVlanTable; + Table m_stateVlanTable, m_stateVlanMemberTable; std::set m_vlans; void doTask(Consumer &consumer); @@ -38,6 +38,7 @@ class VlanMgr : public Orch bool isMemberStateOk(const string &alias); bool isVlanStateOk(const string &alias); bool isVlanMacOk(); + bool isVlanMemberStateOk(const string &vlanMemberKey); }; } diff --git a/cfgmgr/vlanmgrd.cpp b/cfgmgr/vlanmgrd.cpp index 2ba33f9dff..2a926c1444 100644 --- a/cfgmgr/vlanmgrd.cpp +++ b/cfgmgr/vlanmgrd.cpp @@ -13,6 +13,7 @@ #include "producerstatetable.h" #include "vlanmgr.h" #include "shellcmd.h" +#include "warm_restart.h" using namespace std; using namespace swss; @@ -56,6 +57,8 @@ int main(int argc, char **argv) DBConnector appDb(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); DBConnector stateDb(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + WarmStart::checkWarmStart("vlanmgrd"); + /* * swss service starts after interfaces-config.service which will have * switch_mac set. diff --git a/configure.ac b/configure.ac index 8f14f83c78..a33396cd3c 100644 --- a/configure.ac +++ b/configure.ac @@ -39,6 +39,11 @@ esac],[gtest=false]) AM_CONDITIONAL(GTEST, test x$gtest = xtrue) CFLAGS_COMMON="-std=c++11 -Wall -fPIC -Wno-write-strings -I/usr/include/libnl3 -I/usr/include/swss" + +AM_CONDITIONAL(sonic_asic_platform_barefoot, test x$CONFIGURED_PLATFORM = xbarefoot) +AM_COND_IF([sonic_asic_platform_barefoot], + [CFLAGS_COMMON+=" -I/opt/bfn/install/include/switchsai"]) + CFLAGS_COMMON+=" -Werror" CFLAGS_COMMON+=" -Wno-reorder" CFLAGS_COMMON+=" -Wcast-align" diff --git a/doc/swss-schema.md b/doc/swss-schema.md index 8ab08ff382..663574ce48 100644 --- a/doc/swss-schema.md +++ b/doc/swss-schema.md @@ -1,11 +1,13 @@ Schema data is defined in ABNF [RFC5234](https://tools.ietf.org/html/rfc5234) syntax. -### Definitions of common tokens +## Definitions of common tokens name = 1*DIGIT/1*ALPHA ref_hash_key_reference = "[" hash_key "]" ;The token is a refernce to another valid DB key. hash_key = name ; a valid key name (i.e. exists in DB) +## Application DB schema + ### PORT_TABLE Stores information for physical switch ports managed by the switch chip. device_names are defined in [port_config.ini](../portsyncd/port_config.ini). Ports to the CPU (ie: management port) and logical ports (loopback) are not declared in the PORT_TABLE. See INTF_TABLE. @@ -313,6 +315,7 @@ and reflects the LAG ports into the redis under: `LAG_TABLE::port` key = TUNNEL_DECAP_TABLE:name ;field value tunnel_type = "IPINIP" + src_ip = IP dst_ip = IP1,IP2 ;IP addresses separated by "," dscp_mode = "uniform" / "pipe" ecn_mode = "copy_from_outer" / "standard" ;standard: Behavior defined in RFC 6040 section 4.2 @@ -320,18 +323,22 @@ and reflects the LAG ports into the redis under: `LAG_TABLE::port` IP = dec-octet "." dec-octet "." dec-octet "." dec-octet + "src_ip" field is optional + Example: 127.0.0.1:6379> hgetall TUNNEL_DECAP_TABLE:NETBOUNCER 1) "dscp_mode" 2) "uniform" - 3) "dst_ip" + 3) "src_ip" 4) "127.0.0.1" - 5) "ecn_mode" - 6) "copy_from_outer" - 7) "ttl_mode" - 8) "uniform" - 9) "tunnel_type" - 10) "IPINIP" + 5) "dst_ip" + 6) "127.0.0.1" + 7) "ecn_mode" + 8) "copy_from_outer" + 9) "ttl_mode" + 10) "uniform" + 11) "tunnel_type" + 12) "IPINIP" --------------------------------------------- @@ -620,7 +627,44 @@ Equivalent RedisDB entry: 12) "0" 127.0.0.1:6379> -### Configuration files + +## Configuration DB schema + +### WARM\_RESTART + ;Stores system warm start configuration + ;Status: work in progress + + key = WARM_RESTART:name ; name is the name of SONiC docker or "system" for global configuration. + + enable = "true" / "false" ; Default value as false. + ; If "system" warm start knob is true, docker level knob will be ignored. + ; If "system" warm start knob is false, docker level knob takes effect. + + timer_name = 1*255VCHAR, ; timer_name is the name of warm start timer for the whole system or the specific docker, + ; Ex. "neighbor_timer", "bgp_timer". The name should not contain "," character. + ; There could be more than one timer in a docker or the system, separated by ",". + + timer_duration = 1*4DIGIT, ; timer duration, in seconds. Separated by "," if there are more than on timer. + + +## State DB schema + +### WARM\_RESTART\_TABLE + ;Stores application and orchdameon warm start status + ;Status: work in progress + + key = WARM_RESTART_TABLE:process_name ; process_name is a unique process identifier. + restart_count = 1*10DIGIT ; a number between 0 and 2147483647, + ; count of warm start times. + + state = "init" / "restored" / "reconciled" ; init: process init with warm start enabled. + ; restored: process restored to the previous + ; state using saved data. + ; reconciled: process reconciled with up to date + ; dynanic data like port state, neighbor, routes + ; and so on. + +## Configuration files What configuration files should we have? Do apps, orch agent each need separate files? [port_config.ini](https://github.com/stcheng/swss/blob/mock/portsyncd/port_config.ini) - defines physical port information diff --git a/orchagent/Makefile.am b/orchagent/Makefile.am index d65ff773b9..24249c5ff2 100644 --- a/orchagent/Makefile.am +++ b/orchagent/Makefile.am @@ -1,4 +1,4 @@ -INCLUDES = -I $(top_srcdir) +INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/warmrestart CFLAGS_SAI = -I /usr/include/sai @@ -7,6 +7,7 @@ swssdir = $(datadir)/swss dist_swss_DATA = \ pfc_detect_mellanox.lua \ pfc_detect_broadcom.lua \ + pfc_detect_barefoot.lua \ pfc_restore.lua bin_PROGRAMS = orchagent routeresync @@ -41,6 +42,7 @@ orchagent_SOURCES = \ request_parser.cpp \ vrforch.cpp \ countercheckorch.cpp \ + dtelorch.cpp \ flexcounterorch.cpp\ acltable.h \ aclorch.h \ @@ -64,11 +66,14 @@ orchagent_SOURCES = \ switchorch.h \ swssnet.h \ tunneldecaporch.h \ - crmorch.h + crmorch.h \ request_parser.h \ vrforch.h \ + dtelorch.h \ countercheckorch.h \ - flexcounterorch.h + flexcounterorch.h \ + $(top_srcdir)/warmrestart/warm_restart.cpp \ + $(top_srcdir)/warmrestart/warm_restart.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 bf8c5597d4..2589e4507c 100644 --- a/orchagent/aclorch.cpp +++ b/orchagent/aclorch.cpp @@ -6,6 +6,7 @@ #include "schema.h" #include "ipprefix.h" #include "converter.h" +#include "tokenize.h" #include "timer.h" #include "crmorch.h" @@ -45,13 +46,36 @@ acl_rule_attr_lookup_t aclMatchLookup = { MATCH_TC, SAI_ACL_ENTRY_ATTR_FIELD_TC }, { MATCH_L4_SRC_PORT_RANGE, (sai_acl_entry_attr_t)SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE }, { MATCH_L4_DST_PORT_RANGE, (sai_acl_entry_attr_t)SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE }, + { MATCH_TUNNEL_VNI, SAI_ACL_ENTRY_ATTR_FIELD_TUNNEL_VNI }, + { MATCH_INNER_ETHER_TYPE, SAI_ACL_ENTRY_ATTR_FIELD_INNER_ETHER_TYPE }, + { MATCH_INNER_IP_PROTOCOL, SAI_ACL_ENTRY_ATTR_FIELD_INNER_IP_PROTOCOL }, + { MATCH_INNER_L4_SRC_PORT, SAI_ACL_ENTRY_ATTR_FIELD_INNER_L4_SRC_PORT }, + { MATCH_INNER_L4_DST_PORT, SAI_ACL_ENTRY_ATTR_FIELD_INNER_L4_DST_PORT } }; acl_rule_attr_lookup_t aclL3ActionLookup = { - { PACKET_ACTION_FORWARD, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, - { PACKET_ACTION_DROP, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, - { PACKET_ACTION_REDIRECT, SAI_ACL_ENTRY_ATTR_ACTION_REDIRECT } + { PACKET_ACTION_FORWARD, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, + { PACKET_ACTION_DROP, SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION }, + { PACKET_ACTION_REDIRECT, SAI_ACL_ENTRY_ATTR_ACTION_REDIRECT } +}; + +acl_rule_attr_lookup_t aclDTelActionLookup = +{ + { ACTION_DTEL_FLOW_OP, SAI_ACL_ENTRY_ATTR_ACTION_ACL_DTEL_FLOW_OP }, + { ACTION_DTEL_INT_SESSION, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_INT_SESSION }, + { ACTION_DTEL_DROP_REPORT_ENABLE, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_DROP_REPORT_ENABLE }, + { ACTION_DTEL_TAIL_DROP_REPORT_ENABLE, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_TAIL_DROP_REPORT_ENABLE }, + { ACTION_DTEL_FLOW_SAMPLE_PERCENT, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_FLOW_SAMPLE_PERCENT }, + { ACTION_DTEL_REPORT_ALL_PACKETS, SAI_ACL_ENTRY_ATTR_ACTION_DTEL_REPORT_ALL_PACKETS } +}; + +acl_dtel_flow_op_type_lookup_t aclDTelFlowOpTypeLookup = +{ + { DTEL_FLOW_OP_NOP, SAI_ACL_DTEL_FLOW_OP_NOP }, + { DTEL_FLOW_OP_POSTCARD, SAI_ACL_DTEL_FLOW_OP_POSTCARD }, + { DTEL_FLOW_OP_INT, SAI_ACL_DTEL_FLOW_OP_INT }, + { DTEL_FLOW_OP_IOAM, SAI_ACL_DTEL_FLOW_OP_IOAM } }; static acl_table_type_lookup_t aclTableTypeLookUp = @@ -59,7 +83,9 @@ static acl_table_type_lookup_t aclTableTypeLookUp = { TABLE_TYPE_L3, ACL_TABLE_L3 }, { TABLE_TYPE_L3V6, ACL_TABLE_L3V6 }, { TABLE_TYPE_MIRROR, ACL_TABLE_MIRROR }, - { TABLE_TYPE_CTRLPLANE, ACL_TABLE_CTRLPLANE } + { TABLE_TYPE_CTRLPLANE, ACL_TABLE_CTRLPLANE }, + { TABLE_TYPE_DTEL_FLOW_WATCHLIST, ACL_TABLE_DTEL_FLOW_WATCHLIST }, + { TABLE_TYPE_DTEL_DROP_WATCHLIST, ACL_TABLE_DTEL_DROP_WATCHLIST } }; static acl_stage_type_lookup_t aclStageLookUp = @@ -201,8 +227,19 @@ bool AclRule::validateAddMatch(string attr_name, string attr_value) } else if(attr_name == MATCH_DSCP) { - value.aclfield.data.u8 = to_uint(attr_value, 0, 0x3F); - value.aclfield.mask.u8 = 0x3F; + /* Support both exact value match and value/mask match */ + auto dscp_data = tokenize(attr_value, '/'); + + value.aclfield.data.u8 = to_uint(dscp_data[0], 0, 0x3F); + + if (dscp_data.size() == 2) + { + value.aclfield.mask.u8 = to_uint(dscp_data[1], 0, 0x3F); + } + else + { + value.aclfield.mask.u8 = 0x3F; + } } else if(attr_name == MATCH_IP_PROTOCOL) { @@ -254,7 +291,22 @@ bool AclRule::validateAddMatch(string attr_name, string attr_value) value.aclfield.data.u8 = to_uint(attr_value); value.aclfield.mask.u8 = 0xFF; } - + else if (attr_name == MATCH_TUNNEL_VNI) + { + value.aclfield.data.u32 = to_uint(attr_value); + value.aclfield.mask.u32 = 0xFFFFFFFF; + } + else if (attr_name == MATCH_INNER_ETHER_TYPE || attr_name == MATCH_INNER_L4_SRC_PORT || + attr_name == MATCH_INNER_L4_DST_PORT) + { + value.aclfield.data.u16 = to_uint(attr_value); + value.aclfield.mask.u16 = 0xFFFF; + } + else if (attr_name == MATCH_INNER_IP_PROTOCOL) + { + value.aclfield.data.u8 = to_uint(attr_value); + value.aclfield.mask.u8 = 0xFF; + } } catch (exception &e) { @@ -456,7 +508,7 @@ AclRuleCounters AclRule::getCounters() return AclRuleCounters(counter_attr[0].value.u64, counter_attr[1].value.u64); } -shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, MirrorOrch *mirror, const string& rule, const string& table, const KeyOpFieldsValuesTuple& data) +shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, MirrorOrch *mirror, DTelOrch *dtel, const string& rule, const string& table, const KeyOpFieldsValuesTuple& data) { string action; bool action_found = false; @@ -465,7 +517,12 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir { string attr_name = toUpper(fvField(itr)); string attr_value = fvValue(itr); - if (attr_name == ACTION_PACKET_ACTION || attr_name == ACTION_MIRROR_ACTION) + if (attr_name == ACTION_PACKET_ACTION || attr_name == ACTION_MIRROR_ACTION || + attr_name == ACTION_DTEL_FLOW_OP || attr_name == ACTION_DTEL_INT_SESSION || + attr_name == ACTION_DTEL_DROP_REPORT_ENABLE || + attr_name == ACTION_DTEL_TAIL_DROP_REPORT_ENABLE || + attr_name == ACTION_DTEL_FLOW_SAMPLE_PERCENT || + attr_name == ACTION_DTEL_REPORT_ALL_PACKETS) { action_found = true; action = attr_name; @@ -478,7 +535,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_L3V6 && type != ACL_TABLE_MIRROR) + if (type != ACL_TABLE_L3 && type != ACL_TABLE_L3V6 && type != ACL_TABLE_MIRROR && type != ACL_TABLE_DTEL_FLOW_WATCHLIST && type != ACL_TABLE_DTEL_DROP_WATCHLIST) { throw runtime_error("Unknown table type."); } @@ -503,6 +560,24 @@ shared_ptr AclRule::makeShared(acl_table_type_t type, AclOrch *acl, Mir { return make_shared(acl, rule, table, type); } + else if (type == ACL_TABLE_DTEL_FLOW_WATCHLIST) + { + if (dtel) + { + return make_shared(acl, dtel, rule, table, type); + } else { + throw runtime_error("DTel feature is not enabled. Watchlists cannot be configured"); + } + } + else if (type == ACL_TABLE_DTEL_DROP_WATCHLIST) + { + if (dtel) + { + return make_shared(acl, dtel, rule, table, type); + } else { + throw runtime_error("DTel feature is not enabled. Watchlists cannot be configured"); + } + } throw runtime_error("Wrong combination of table type and action in rule " + rule); } @@ -936,7 +1011,8 @@ bool AclTable::validate() { // 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; + if (stage == ACL_STAGE_UNKNOWN) return false; + if (portSet.empty()) return false; return true; } @@ -944,49 +1020,12 @@ bool AclTable::create() { SWSS_LOG_ENTER(); - if (stage == ACL_STAGE_UNKNOWN) - { - SWSS_LOG_ERROR("Unknown ACL stage for ACL table %s", id.c_str()); - return false; - } - sai_attribute_t attr; vector table_attrs; - - int32_t range_types_list[] = - { SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE, - SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE - }; - - 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; - for (auto bind : binds) + vector bpoint_list = { SAI_ACL_BIND_POINT_TYPE_PORT, SAI_ACL_BIND_POINT_TYPE_LAG }; + if (type == ACL_TABLE_PFCWD) { - bpoint_list.push_back(bind); + bpoint_list = { SAI_ACL_BIND_POINT_TYPE_PORT }; } attr.id = SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST; @@ -1060,6 +1099,7 @@ bool AclTable::create() if(stage == ACL_STAGE_INGRESS) { + int32_t range_types_list[] = { SAI_ACL_RANGE_TYPE_L4_DST_PORT_RANGE, SAI_ACL_RANGE_TYPE_L4_SRC_PORT_RANGE }; attr.id = SAI_ACL_TABLE_ATTR_FIELD_ACL_RANGE_TYPE; attr.value.s32list.count = (uint32_t)(sizeof(range_types_list) / sizeof(range_types_list[0])); attr.value.s32list.list = range_types_list; @@ -1236,6 +1276,272 @@ AclRuleCounters AclRuleMirror::getCounters() return cnt; } +AclRuleDTelFlowWatchListEntry::AclRuleDTelFlowWatchListEntry(AclOrch *aclOrch, DTelOrch *dtel, string rule, string table, acl_table_type_t type) : + AclRule(aclOrch, rule, table, type), + m_pDTelOrch(dtel) +{ +} + +bool AclRuleDTelFlowWatchListEntry::validateAddAction(string attr_name, string attr_val) +{ + SWSS_LOG_ENTER(); + + sai_attribute_value_t value; + string attr_value = toUpper(attr_val); + sai_object_id_t session_oid; + + if (!m_pDTelOrch || + (attr_name != ACTION_DTEL_FLOW_OP && + attr_name != ACTION_DTEL_INT_SESSION && + attr_name != ACTION_DTEL_FLOW_SAMPLE_PERCENT && + attr_name != ACTION_DTEL_REPORT_ALL_PACKETS)) + { + return false; + } + + if (attr_name == ACTION_DTEL_FLOW_OP) + { + auto it = aclDTelFlowOpTypeLookup.find(attr_value); + + if (it == aclDTelFlowOpTypeLookup.end()) + { + return false; + } + + value.aclaction.parameter.s32 = it->second; + + if (attr_value == DTEL_FLOW_OP_INT) + { + INT_enabled = true; + } + else + { + INT_enabled = false; + } + } + + if (attr_name == ACTION_DTEL_INT_SESSION) + { + m_intSessionId = attr_value; + + bool ret = m_pDTelOrch->getINTSessionOid(attr_value, session_oid); + if (ret) + { + value.aclaction.parameter.oid = session_oid; + + // Increase session reference count regardless of state to deny + // attempt to remove INT session with attached ACL rules. + if (!m_pDTelOrch->increaseINTSessionRefCount(m_intSessionId)) + { + SWSS_LOG_ERROR("Failed to increase INT session %s reference count", m_intSessionId.c_str()); + return false; + } + + INT_session_valid = true; + } else { + SWSS_LOG_ERROR("Invalid INT session id %s used for ACL action", m_intSessionId.c_str()); + INT_session_valid = false; + } + } + + if (attr_name == ACTION_DTEL_FLOW_SAMPLE_PERCENT) + { + value.aclaction.parameter.u8 = to_uint(attr_value); + } + + value.aclaction.enable = true; + + if (attr_name == ACTION_DTEL_REPORT_ALL_PACKETS) + { + value.aclaction.parameter.booldata = (attr_value == DTEL_ENABLED) ? true : false; + value.aclaction.enable = (attr_value == DTEL_ENABLED) ? true : false; + } + + m_actions[aclDTelActionLookup[attr_name]] = value; + + return true; +} + +bool AclRuleDTelFlowWatchListEntry::validate() +{ + SWSS_LOG_ENTER(); + + if(!m_pDTelOrch) + { + return false; + } + + if (m_matches.size() == 0 || m_actions.size() == 0) + { + return false; + } + + return true; +} + +bool AclRuleDTelFlowWatchListEntry::create() +{ + SWSS_LOG_ENTER(); + + if (!m_pDTelOrch) + { + return false; + } + + if (INT_enabled && !INT_session_valid) + { + return true; + } + + if (!AclRule::create()) + { + return false; + } + + return true; +} + +bool AclRuleDTelFlowWatchListEntry::remove() +{ + if (!m_pDTelOrch) + { + return false; + } + + if (INT_enabled && !INT_session_valid) + { + return true; + } + + if (!AclRule::remove()) + { + return false; + } + + if (INT_enabled && INT_session_valid) + { + if (!m_pDTelOrch->decreaseINTSessionRefCount(m_intSessionId)) + { + SWSS_LOG_ERROR("Could not decrement INT session %s reference count", m_intSessionId.c_str()); + return false; + } + } + + return true; +} + +void AclRuleDTelFlowWatchListEntry::update(SubjectType type, void *cntx) +{ + sai_attribute_value_t value; + sai_object_id_t session_oid = SAI_NULL_OBJECT_ID; + + if (!m_pDTelOrch) + { + return; + } + + if (type != SUBJECT_TYPE_INT_SESSION_CHANGE || !INT_enabled) + { + return; + } + + DTelINTSessionUpdate *update = static_cast(cntx); + + if (m_intSessionId != update->session_id) + { + return; + } + + if (update->active) + { + SWSS_LOG_INFO("Activating INT watchlist %s for session %s", m_id.c_str(), m_intSessionId.c_str()); + + bool ret = m_pDTelOrch->getINTSessionOid(m_intSessionId, session_oid); + if (!ret) + { + SWSS_LOG_ERROR("Invalid INT session id used for ACL action"); + return; + } + + value.aclaction.enable = true; + value.aclaction.parameter.oid = session_oid; + + // Increase session reference count regardless of state to deny + // attempt to remove INT session with attached ACL rules. + if (!m_pDTelOrch->increaseINTSessionRefCount(m_intSessionId)) + { + throw runtime_error("Failed to increase INT session reference count"); + } + + m_actions[SAI_ACL_ENTRY_ATTR_ACTION_DTEL_INT_SESSION] = value; + + INT_session_valid = true; + + create(); + } + else + { + SWSS_LOG_INFO("Deactivating INT watchlist %s for session %s", m_id.c_str(), m_intSessionId.c_str()); + remove(); + INT_session_valid = false; + } +} + +AclRuleDTelDropWatchListEntry::AclRuleDTelDropWatchListEntry(AclOrch *aclOrch, DTelOrch *dtel, string rule, string table, acl_table_type_t type) : + AclRule(aclOrch, rule, table, type), + m_pDTelOrch(dtel) +{ +} + +bool AclRuleDTelDropWatchListEntry::validateAddAction(string attr_name, string attr_val) +{ + SWSS_LOG_ENTER(); + + if (!m_pDTelOrch) + { + return false; + } + + sai_attribute_value_t value; + string attr_value = toUpper(attr_val); + + if (attr_name != ACTION_DTEL_DROP_REPORT_ENABLE && + attr_name != ACTION_DTEL_TAIL_DROP_REPORT_ENABLE) + { + return false; + } + + + value.aclaction.parameter.booldata = (attr_value == DTEL_ENABLED) ? true : false; + value.aclaction.enable = (attr_value == DTEL_ENABLED) ? true : false; + + m_actions[aclDTelActionLookup[attr_name]] = value; + + return true; +} + +bool AclRuleDTelDropWatchListEntry::validate() +{ + SWSS_LOG_ENTER(); + + if (!m_pDTelOrch) + { + return false; + } + + if (m_matches.size() == 0 || m_actions.size() == 0) + { + return false; + } + + return true; +} + +void AclRuleDTelDropWatchListEntry::update(SubjectType, void *) +{ + // Do nothing +} + AclRange::AclRange(sai_acl_range_type_t type, sai_object_id_t oid, int min, int max): m_oid(oid), m_refCnt(0), m_min(min), m_max(max), m_type(type) { @@ -1361,11 +1667,7 @@ bool AclRange::remove() return true; } -AclOrch::AclOrch(DBConnector *db, vector tableNames, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch) : - Orch(db, tableNames), - m_mirrorOrch(mirrorOrch), - m_neighOrch(neighOrch), - m_routeOrch(routeOrch) +void AclOrch::init(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch) { SWSS_LOG_ENTER(); @@ -1391,24 +1693,62 @@ AclOrch::AclOrch(DBConnector *db, vector tableNames, PortsOrch *portOrch // initialized before thread start. auto interv = timespec { .tv_sec = COUNTERS_READ_INTERVAL, .tv_nsec = 0 }; auto timer = new SelectableTimer(interv); - auto executor = new ExecutableTimer(timer, this); - Orch::addExecutor("", executor); + auto executor = new ExecutableTimer(timer, this, "ACL_POLL_TIMER"); + Orch::addExecutor(executor); timer->start(); } +AclOrch::AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch) : + Orch(connectors), + m_mirrorOrch(mirrorOrch), + m_neighOrch(neighOrch), + m_routeOrch(routeOrch), + m_dTelOrch(NULL) +{ + SWSS_LOG_ENTER(); + + init(connectors, portOrch, mirrorOrch, neighOrch, routeOrch); +} + +AclOrch::AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch, DTelOrch *dtelOrch) : + Orch(connectors), + m_mirrorOrch(mirrorOrch), + m_neighOrch(neighOrch), + m_routeOrch(routeOrch), + m_dTelOrch(dtelOrch) +{ + SWSS_LOG_ENTER(); + + init(connectors, portOrch, mirrorOrch, neighOrch, routeOrch); + + if (m_dTelOrch) + { + m_dTelOrch->attach(this); + } + + createDTelWatchListTables(); +} + AclOrch::~AclOrch() { m_mirrorOrch->detach(this); + if(m_dTelOrch) + { + m_dTelOrch->detach(this); + } + m_bCollectCounters = false; m_sleepGuard.notify_all(); + + deleteDTelWatchListTables(); } void AclOrch::update(SubjectType type, void *cntx) { SWSS_LOG_ENTER(); - if (type != SUBJECT_TYPE_MIRROR_SESSION_CHANGE) + if (type != SUBJECT_TYPE_MIRROR_SESSION_CHANGE && type != SUBJECT_TYPE_INT_SESSION_CHANGE) { return; } @@ -1445,6 +1785,11 @@ void AclOrch::doTask(Consumer &consumer) unique_lock lock(m_countersMutex); doAclRuleTask(consumer); } + else if (table_name == STATE_LAG_TABLE_NAME) + { + unique_lock lock(m_countersMutex); + doAclTablePortUpdateTask(consumer); + } else { SWSS_LOG_ERROR("Invalid table %s", table_name.c_str()); @@ -1545,7 +1890,7 @@ void AclOrch::doAclTableTask(Consumer &consumer) { KeyOpFieldsValuesTuple t = it->second; string key = kfvKey(t); - size_t found = key.find('|'); + size_t found = key.find(consumer.getConsumerTable()->getTableNameSeparator().c_str()); string table_id = key.substr(0, found); string op = kfvOp(t); @@ -1580,7 +1925,7 @@ void AclOrch::doAclTableTask(Consumer &consumer) } else if (attr_name == TABLE_PORTS) { - bool suc = processPorts(attr_value, [&](sai_object_id_t portOid) { + bool suc = processPorts(newTable, attr_value, [&](sai_object_id_t portOid) { newTable.link(portOid); }); @@ -1645,7 +1990,7 @@ void AclOrch::doAclRuleTask(Consumer &consumer) { KeyOpFieldsValuesTuple t = it->second; string key = kfvKey(t); - size_t found = key.find('|'); + size_t found = key.find(consumer.getConsumerTable()->getTableNameSeparator().c_str()); string table_id = key.substr(0, found); string rule_id = key.substr(found + 1); string op = kfvOp(t); @@ -1667,7 +2012,7 @@ void AclOrch::doAclRuleTask(Consumer &consumer) continue; } - 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, m_dTelOrch, rule_id, table_id, t); for (const auto& itr : kfvFieldsValues(t)) { @@ -1725,17 +2070,79 @@ void AclOrch::doAclRuleTask(Consumer &consumer) } } -bool AclOrch::processPorts(string portsList, std::function inserter) +void AclOrch::doAclTablePortUpdateTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + size_t found = key.find(consumer.getConsumerTable()->getTableNameSeparator().c_str()); + string port_alias = key.substr(0, found); + string op = kfvOp(t); + + SWSS_LOG_INFO("doAclTablePortUpdateTask: OP: %s, port_alias: %s", op.c_str(), port_alias.c_str()); + + if (op == SET_COMMAND) + { + for (auto itmap : m_AclTables) + { + auto table = itmap.second; + if (table.pendingPortSet.find(port_alias) != table.pendingPortSet.end()) + { + SWSS_LOG_INFO("found the port: %s in ACL table: %s pending port list, bind it to ACL table.", port_alias.c_str(), table.description.c_str()); + + bool suc = processPendingPort(table, port_alias, [&](sai_object_id_t portOid) { + table.link(portOid); + }); + + if (!suc) + { + SWSS_LOG_ERROR("Failed to bind the ACL table: %s to port: %s", table.description.c_str(), port_alias.c_str()); + } + else + { + table.pendingPortSet.erase(port_alias); + SWSS_LOG_DEBUG("port: %s bound to ACL table table: %s, remove it from pending list", port_alias.c_str(), table.description.c_str()); + } + } + } + } + else if (op == DEL_COMMAND) + { + for (auto itmap : m_AclTables) + { + auto table = itmap.second; + if (table.portSet.find(port_alias) != table.portSet.end()) + { + /*TODO: update the ACL table after port/lag deleted*/ + table.pendingPortSet.emplace(port_alias); + SWSS_LOG_INFO("Add deleted port: %s to the pending list of ACL table: %s", port_alias.c_str(), table.description.c_str()); + } + } + } + else + { + SWSS_LOG_ERROR("Unknown operation type %s", op.c_str()); + } + it = consumer.m_toSync.erase(it); + } +} + +bool AclOrch::processPorts(AclTable &aclTable, string portsList, std::function inserter) { SWSS_LOG_ENTER(); vector strList; - SWSS_LOG_INFO("Processing ACL table port list %s", portsList.c_str()); + SWSS_LOG_DEBUG("Processing ACL table port list %s", portsList.c_str()); split(portsList, strList, ','); set strSet(strList.begin(), strList.end()); + aclTable.portSet = strSet; if (strList.size() != strSet.size()) { @@ -1751,33 +2158,52 @@ bool AclOrch::processPorts(string portsList, std::functiongetPort(alias, port)) { - SWSS_LOG_ERROR("Failed to process port. Port %s doesn't exist", alias.c_str()); - return false; + SWSS_LOG_INFO("Port %s not configured yet, add it to ACL table %s pending list", alias.c_str(), aclTable.description.c_str()); + aclTable.pendingPortSet.emplace(alias); + continue; } - switch (port.m_type) + if (gPortsOrch->getAclBindPortId(alias, port_id)) { - case Port::PHY: - if (port.m_lag_member_id != SAI_NULL_OBJECT_ID) - { - SWSS_LOG_ERROR("Failed to process port. Bind table to LAG member %s is not allowed", alias.c_str()); - return false; - } - inserter(port.m_port_id); - break; - case Port::LAG: - inserter(port.m_lag_id); - break; - case Port::VLAN: - inserter(port.m_vlan_info.vlan_oid); - break; - default: - SWSS_LOG_ERROR("Failed to process port. Incorrect port %s type %d", alias.c_str(), port.m_type); - return false; - } + inserter(port_id); + } + else + { + return false; + } + } + + return true; +} + +bool AclOrch::processPendingPort(AclTable &aclTable, string portAlias, std::function inserter) +{ + SWSS_LOG_ENTER(); + + SWSS_LOG_DEBUG("Processing ACL table port %s", portAlias.c_str()); + + sai_object_id_t port_id; + + Port port; + if (!gPortsOrch->getPort(portAlias, port)) + { + SWSS_LOG_INFO("Port %s not configured yet, add it to ACL table %s pending list", portAlias.c_str(), aclTable.description.c_str()); + aclTable.pendingPortSet.insert(portAlias); + return true; + } + + if (gPortsOrch->getAclBindPortId(portAlias, port_id)) + { + inserter(port_id); + aclTable.bind(port_id); + } + else + { + return false; } return true; @@ -1899,13 +2325,9 @@ sai_status_t AclOrch::bindAclTable(sai_object_id_t table_oid, AclTable &aclTable { if (bind) { - SWSS_LOG_ERROR("Port list is not configured for %s table", aclTable.id.c_str()); - return SAI_STATUS_FAILURE; - } - else - { - return SAI_STATUS_SUCCESS; + SWSS_LOG_WARN("Binding port list is empty for %s table", aclTable.id.c_str()); } + return SAI_STATUS_SUCCESS; } if (bind) @@ -1919,3 +2341,203 @@ sai_status_t AclOrch::bindAclTable(sai_object_id_t table_oid, AclTable &aclTable return status; } + +sai_status_t AclOrch::createDTelWatchListTables() +{ + SWSS_LOG_ENTER(); + + AclTable flowWLTable, dropWLTable; + sai_object_id_t table_oid; + + sai_status_t status; + sai_attribute_t attr; + vector table_attrs; + + /* Create Flow watchlist ACL table */ + + flowWLTable.id = TABLE_TYPE_DTEL_FLOW_WATCHLIST; + flowWLTable.type = ACL_TABLE_DTEL_FLOW_WATCHLIST; + flowWLTable.description = "Dataplane Telemetry Flow Watchlist table"; + + attr.id = SAI_ACL_TABLE_ATTR_ACL_STAGE; + attr.value.s32 = SAI_ACL_STAGE_INGRESS; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST; + vector bpoint_list; + bpoint_list.push_back(SAI_ACL_BIND_POINT_TYPE_SWITCH); + attr.value.s32list.count = 1; + attr.value.s32list.list = bpoint_list.data(); + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE; + 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); + + 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); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_L4_DST_PORT; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_IP_PROTOCOL; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_TUNNEL_VNI; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_INNER_ETHER_TYPE; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_INNER_SRC_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_INNER_DST_IP; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_ACL_ACTION_TYPE_LIST; + int32_t acl_action_list[4]; + acl_action_list[0] = SAI_ACL_ACTION_TYPE_ACL_DTEL_FLOW_OP; + acl_action_list[1] = SAI_ACL_ACTION_TYPE_DTEL_INT_SESSION; + acl_action_list[2] = SAI_ACL_ACTION_TYPE_DTEL_REPORT_ALL_PACKETS; + acl_action_list[3] = SAI_ACL_ACTION_TYPE_DTEL_FLOW_SAMPLE_PERCENT; + attr.value.s32list.count = 4; + attr.value.s32list.list = acl_action_list; + table_attrs.push_back(attr); + + status = sai_acl_api->create_acl_table(&table_oid, gSwitchId, (uint32_t)table_attrs.size(), table_attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create table %s", flowWLTable.description.c_str()); + return status; + } + + m_AclTables[table_oid] = flowWLTable; + SWSS_LOG_INFO("Successfully created ACL table %s, oid: %lX", flowWLTable.description.c_str(), table_oid); + + /* Create Drop watchlist ACL table */ + + table_attrs.clear(); + + dropWLTable.id = TABLE_TYPE_DTEL_DROP_WATCHLIST; + dropWLTable.type = ACL_TABLE_DTEL_DROP_WATCHLIST; + dropWLTable.description = "Dataplane Telemetry Drop Watchlist table"; + + attr.id = SAI_ACL_TABLE_ATTR_ACL_STAGE; + attr.value.s32 = SAI_ACL_STAGE_INGRESS; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_ACL_BIND_POINT_TYPE_LIST; + bpoint_list.clear(); + bpoint_list.push_back(SAI_ACL_BIND_POINT_TYPE_SWITCH); + attr.value.s32list.count = 1; + attr.value.s32list.list = bpoint_list.data(); + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_ETHER_TYPE; + 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); + + 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); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_L4_DST_PORT; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_FIELD_IP_PROTOCOL; + attr.value.booldata = true; + table_attrs.push_back(attr); + + attr.id = SAI_ACL_TABLE_ATTR_ACL_ACTION_TYPE_LIST; + acl_action_list[0] = SAI_ACL_ACTION_TYPE_DTEL_DROP_REPORT_ENABLE; + acl_action_list[1] = SAI_ACL_ACTION_TYPE_DTEL_TAIL_DROP_REPORT_ENABLE; + attr.value.s32list.count = 2; + attr.value.s32list.list = acl_action_list; + table_attrs.push_back(attr); + + status = sai_acl_api->create_acl_table(&table_oid, gSwitchId, (uint32_t)table_attrs.size(), table_attrs.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to create table %s", dropWLTable.description.c_str()); + return status; + } + + m_AclTables[table_oid] = dropWLTable; + SWSS_LOG_INFO("Successfully created ACL table %s, oid: %lX", dropWLTable.description.c_str(), table_oid); + + return status; +} + +sai_status_t AclOrch::deleteDTelWatchListTables() +{ + SWSS_LOG_ENTER(); + + AclTable flowWLTable, dropWLTable; + sai_object_id_t table_oid; + string table_id = TABLE_TYPE_DTEL_FLOW_WATCHLIST; + + sai_status_t status; + + table_oid = getTableById(table_id); + + if (table_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Failed to find ACL table %s", table_id.c_str()); + return SAI_STATUS_FAILURE; + } + + status = sai_acl_api->remove_acl_table(table_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to delete table %s", table_id.c_str()); + return status; + } + + m_AclTables.erase(table_oid); + + table_id = TABLE_TYPE_DTEL_DROP_WATCHLIST; + + table_oid = getTableById(table_id); + + if (table_oid == SAI_NULL_OBJECT_ID) + { + SWSS_LOG_INFO("Failed to find ACL table %s", table_id.c_str()); + return SAI_STATUS_FAILURE; + } + + status = sai_acl_api->remove_acl_table(table_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to delete table %s", table_id.c_str()); + return status; + } + + m_AclTables.erase(table_oid); + + return SAI_STATUS_SUCCESS; +} diff --git a/orchagent/aclorch.h b/orchagent/aclorch.h index c96dbd8d30..990848d562 100644 --- a/orchagent/aclorch.h +++ b/orchagent/aclorch.h @@ -11,6 +11,7 @@ #include "orch.h" #include "portsorch.h" #include "mirrororch.h" +#include "dtelorch.h" #include "observer.h" // ACL counters update interval in the DB @@ -27,6 +28,8 @@ #define TABLE_TYPE_MIRROR "MIRROR" #define TABLE_TYPE_PFCWD "PFCWD" #define TABLE_TYPE_CTRLPLANE "CTRLPLANE" +#define TABLE_TYPE_DTEL_FLOW_WATCHLIST "DTEL_FLOW_WATCHLIST" +#define TABLE_TYPE_DTEL_DROP_WATCHLIST "DTEL_DROP_WATCHLIST" #define RULE_PRIORITY "PRIORITY" #define MATCH_SRC_IP "SRC_IP" @@ -43,14 +46,33 @@ #define MATCH_L4_SRC_PORT_RANGE "L4_SRC_PORT_RANGE" #define MATCH_L4_DST_PORT_RANGE "L4_DST_PORT_RANGE" #define MATCH_TC "TC" +#define MATCH_TUNNEL_VNI "TUNNEL_VNI" +#define MATCH_INNER_ETHER_TYPE "INNER_ETHER_TYPE" +#define MATCH_INNER_IP_PROTOCOL "INNER_IP_PROTOCOL" +#define MATCH_INNER_L4_SRC_PORT "INNER_L4_SRC_PORT" +#define MATCH_INNER_L4_DST_PORT "INNER_L4_DST_PORT" #define ACTION_PACKET_ACTION "PACKET_ACTION" #define ACTION_MIRROR_ACTION "MIRROR_ACTION" +#define ACTION_DTEL_FLOW_OP "FLOW_OP" +#define ACTION_DTEL_INT_SESSION "INT_SESSION" +#define ACTION_DTEL_DROP_REPORT_ENABLE "DROP_REPORT_ENABLE" +#define ACTION_DTEL_TAIL_DROP_REPORT_ENABLE "TAIL_DROP_REPORT_ENABLE" +#define ACTION_DTEL_FLOW_SAMPLE_PERCENT "FLOW_SAMPLE_PERCENT" +#define ACTION_DTEL_REPORT_ALL_PACKETS "REPORT_ALL_PACKETS" #define PACKET_ACTION_FORWARD "FORWARD" #define PACKET_ACTION_DROP "DROP" #define PACKET_ACTION_REDIRECT "REDIRECT" +#define DTEL_FLOW_OP_NOP "NOP" +#define DTEL_FLOW_OP_POSTCARD "POSTCARD" +#define DTEL_FLOW_OP_INT "INT" +#define DTEL_FLOW_OP_IOAM "IOAM" + +#define DTEL_ENABLED "TRUE" +#define DTEL_DISABLED "FALSE" + #define IP_TYPE_ANY "ANY" #define IP_TYPE_IP "IP" #define IP_TYPE_NON_IP "NON_IP" @@ -71,12 +93,15 @@ typedef enum ACL_TABLE_L3V6, ACL_TABLE_MIRROR, ACL_TABLE_PFCWD, - ACL_TABLE_CTRLPLANE + ACL_TABLE_CTRLPLANE, + ACL_TABLE_DTEL_FLOW_WATCHLIST, + ACL_TABLE_DTEL_DROP_WATCHLIST } acl_table_type_t; typedef map acl_table_type_lookup_t; typedef map acl_rule_attr_lookup_t; typedef map acl_ip_type_lookup_t; +typedef map acl_dtel_flow_op_type_lookup_t; typedef tuple acl_range_properties_t; class AclOrch; @@ -163,7 +188,7 @@ class AclRule return m_counterOid; } - static shared_ptr makeShared(acl_table_type_t type, AclOrch *acl, MirrorOrch *mirror, const string& rule, const string& table, const KeyOpFieldsValuesTuple&); + static shared_ptr makeShared(acl_table_type_t type, AclOrch *acl, MirrorOrch *mirror, DTelOrch *dtel, const string& rule, const string& table, const KeyOpFieldsValuesTuple&); virtual ~AclRule() {} protected: @@ -236,6 +261,35 @@ class AclRuleMirror: public AclRule MirrorOrch *m_pMirrorOrch; }; +class AclRuleDTelFlowWatchListEntry: public AclRule +{ +public: + AclRuleDTelFlowWatchListEntry(AclOrch *m_pAclOrch, DTelOrch *m_pDTelOrch, string rule, string table, acl_table_type_t type); + bool validateAddAction(string attr_name, string attr_value); + bool validate(); + bool create(); + bool remove(); + void update(SubjectType, void *); + +protected: + DTelOrch *m_pDTelOrch; + string m_intSessionId; + bool INT_enabled; + bool INT_session_valid; +}; + +class AclRuleDTelDropWatchListEntry: public AclRule +{ +public: + AclRuleDTelDropWatchListEntry(AclOrch *m_pAclOrch, DTelOrch *m_pDTelOrch, string rule, string table, acl_table_type_t type); + bool validateAddAction(string attr_name, string attr_value); + bool validate(); + void update(SubjectType, void *); + +protected: + DTelOrch *m_pDTelOrch; +}; + class AclTable { sai_object_id_t m_oid; public: @@ -248,6 +302,10 @@ class AclTable { std::map ports; // Map rule name to rule data map> rules; + // Set to store the ACL table port alias + set portSet; + // Set to store the not cofigured ACL table port alias + set pendingPortSet; AclTable() : type(ACL_TABLE_UNKNOWN) @@ -294,7 +352,8 @@ inline void split(string str, Iterable& out, char delim = ' ') class AclOrch : public Orch, public Observer { public: - AclOrch(DBConnector *db, vector tableNames, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch); + AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch); + AclOrch(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch, DTelOrch *m_dTelOrch); ~AclOrch(); void update(SubjectType, void *); @@ -309,6 +368,7 @@ class AclOrch : public Orch, public Observer MirrorOrch *m_mirrorOrch; NeighOrch *m_neighOrch; RouteOrch *m_routeOrch; + DTelOrch *m_dTelOrch; bool addAclTable(AclTable &aclTable, string table_id); bool removeAclTable(string table_id); @@ -319,7 +379,9 @@ class AclOrch : public Orch, public Observer void doTask(Consumer &consumer); void doAclTableTask(Consumer &consumer); void doAclRuleTask(Consumer &consumer); + void doAclTablePortUpdateTask(Consumer &consumer); void doTask(SelectableTimer &timer); + void init(vector& connectors, PortsOrch *portOrch, MirrorOrch *mirrorOrch, NeighOrch *neighOrch, RouteOrch *routeOrch); static void collectCountersThread(AclOrch *pAclOrch); @@ -329,8 +391,11 @@ class AclOrch : public Orch, public Observer bool processAclTableType(string type, acl_table_type_t &table_type); bool processAclTableStage(string stage, acl_stage_type_t &acl_stage); - bool processPorts(string portsList, std::function inserter); + bool processPorts(AclTable &aclTable, string portsList, std::function inserter); + bool processPendingPort(AclTable &aclTable, string portAlias, std::function inserter); bool validateAclTable(AclTable &aclTable); + sai_status_t createDTelWatchListTables(); + sai_status_t deleteDTelWatchListTables(); //vector m_AclTables; map m_AclTables; diff --git a/orchagent/bufferorch.cpp b/orchagent/bufferorch.cpp index 170fa178b6..ef77aa862a 100644 --- a/orchagent/bufferorch.cpp +++ b/orchagent/bufferorch.cpp @@ -29,6 +29,7 @@ BufferOrch::BufferOrch(DBConnector *db, vector &tableNames) : Orch(db, t { SWSS_LOG_ENTER(); initTableHandlers(); + initBufferReadyLists(db); }; void BufferOrch::initTableHandlers() @@ -42,6 +43,78 @@ void BufferOrch::initTableHandlers() m_bufferHandlerMap.insert(buffer_handler_pair(CFG_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME, &BufferOrch::processEgressBufferProfileList)); } +void BufferOrch::initBufferReadyLists(DBConnector *db) +{ + SWSS_LOG_ENTER(); + + Table pg_table(db, CFG_BUFFER_PG_TABLE_NAME); + initBufferReadyList(pg_table); + + Table queue_table(db, CFG_BUFFER_QUEUE_TABLE_NAME); + initBufferReadyList(queue_table); +} + +void BufferOrch::initBufferReadyList(Table& table) +{ + SWSS_LOG_ENTER(); + + // init all ports with an empty list + for (const auto& it: gPortsOrch->getAllPorts()) + { + if (it.second.m_type == Port::PHY) + { + const auto& port_name = it.first; + m_port_ready_list_ref[port_name] = {}; + } + } + + std::vector keys; + table.getKeys(keys); + + // populate the lists with buffer configuration information + for (const auto& key: keys) + { + m_ready_list[key] = false; + + auto tokens = tokenize(key, config_db_key_delimiter); + if (tokens.size() != 2) + { + SWSS_LOG_ERROR("Wrong format of a table '%s' key '%s'. Skip it", table.getTableName().c_str(), key.c_str()); + continue; + } + + auto port_names = tokenize(tokens[0], list_item_delimiter); + + for(const auto& port_name: port_names) + { + m_port_ready_list_ref[port_name].push_back(key); + } + } +} + +bool BufferOrch::isPortReady(const std::string& port_name) const +{ + SWSS_LOG_ENTER(); + + const auto it = m_port_ready_list_ref.find(port_name); + if (it == m_port_ready_list_ref.cend()) + { + // we got a port name which wasn't in our gPortsOrch->getAllPorts() list + // so make the port ready, because we don't have any buffer configuration for it + return true; + } + + const auto& list_of_keys = it->second; + + bool result = true; + for (const auto& key: list_of_keys) + { + result = result && m_ready_list.at(key); + } + + return result; +} + task_process_status BufferOrch::processBufferPool(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -315,7 +388,7 @@ task_process_status BufferOrch::processQueue(Consumer &consumer) auto it = consumer.m_toSync.begin(); KeyOpFieldsValuesTuple tuple = it->second; sai_object_id_t sai_buffer_profile; - string key = kfvKey(tuple); + const string key = kfvKey(tuple); string op = kfvOp(tuple); vector tokens; sai_uint32_t range_low, range_high; @@ -374,6 +447,16 @@ task_process_status BufferOrch::processQueue(Consumer &consumer) } } } + + if (m_ready_list.find(key) != m_ready_list.end()) + { + m_ready_list[key] = true; + } + else + { + SWSS_LOG_ERROR("Queue profile '%s' was inserted after BufferOrch init", key.c_str()); + } + return task_process_status::task_success; } @@ -386,7 +469,7 @@ task_process_status BufferOrch::processPriorityGroup(Consumer &consumer) auto it = consumer.m_toSync.begin(); KeyOpFieldsValuesTuple tuple = it->second; sai_object_id_t sai_buffer_profile; - string key = kfvKey(tuple); + const string key = kfvKey(tuple); string op = kfvOp(tuple); vector tokens; sai_uint32_t range_low, range_high; @@ -451,6 +534,16 @@ task_process_status BufferOrch::processPriorityGroup(Consumer &consumer) } } } + + if (m_ready_list.find(key) != m_ready_list.end()) + { + m_ready_list[key] = true; + } + else + { + SWSS_LOG_ERROR("PG profile '%s' was inserted after BufferOrch init", key.c_str()); + } + return task_process_status::task_success; } diff --git a/orchagent/bufferorch.h b/orchagent/bufferorch.h index f05f8071aa..23f56e6147 100644 --- a/orchagent/bufferorch.h +++ b/orchagent/bufferorch.h @@ -1,7 +1,9 @@ #ifndef SWSS_BUFFORCH_H #define SWSS_BUFFORCH_H +#include #include +#include #include "orch.h" #include "portsorch.h" @@ -26,6 +28,7 @@ class BufferOrch : public Orch { public: BufferOrch(DBConnector *db, vector &tableNames); + bool isPortReady(const std::string& port_name) const; static type_map m_buffer_type_maps; private: typedef task_process_status (BufferOrch::*buffer_table_handler)(Consumer& consumer); @@ -34,6 +37,8 @@ class BufferOrch : public Orch virtual void doTask(Consumer& consumer); void initTableHandlers(); + void initBufferReadyLists(DBConnector *db); + void initBufferReadyList(Table& table); task_process_status processBufferPool(Consumer &consumer); task_process_status processBufferProfile(Consumer &consumer); task_process_status processQueue(Consumer &consumer); @@ -42,6 +47,8 @@ class BufferOrch : public Orch task_process_status processEgressBufferProfileList(Consumer &consumer); buffer_table_handler_map m_bufferHandlerMap; + std::unordered_map m_ready_list; + std::unordered_map> m_port_ready_list_ref; }; #endif /* SWSS_BUFFORCH_H */ diff --git a/orchagent/countercheckorch.cpp b/orchagent/countercheckorch.cpp index f404c07e51..aa46758626 100644 --- a/orchagent/countercheckorch.cpp +++ b/orchagent/countercheckorch.cpp @@ -30,8 +30,8 @@ CounterCheckOrch::CounterCheckOrch(DBConnector *db, vector &tableNames): 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); + auto executor = new ExecutableTimer(timer, this, "MC_COUNTERS_POLL"); + Orch::addExecutor(executor); timer->start(); } diff --git a/orchagent/crmorch.cpp b/orchagent/crmorch.cpp index ab6c0b72d7..56f7e5bb38 100644 --- a/orchagent/crmorch.cpp +++ b/orchagent/crmorch.cpp @@ -12,6 +12,7 @@ #define CRM_THRESHOLD_LOW_DEFAULT 70 #define CRM_THRESHOLD_HIGH_DEFAULT 85 #define CRM_EXCEEDED_MSG_MAX 10 +#define CRM_ACL_RESOURCE_COUNT 256 extern sai_object_id_t gSwitchId; extern sai_switch_api_t *sai_switch_api; @@ -161,8 +162,11 @@ CrmOrch::CrmOrch(DBConnector *db, string tableName): 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); + // The CRM stats needs to be populated again + m_countersCrmTable->del(CRM_COUNTERS_TABLE_KEY); + + auto executor = new ExecutableTimer(m_timer.get(), this, "CRM_COUNTERS_POLL"); + Orch::addExecutor(executor); m_timer->start(); } @@ -332,7 +336,7 @@ void CrmOrch::decCrmAclUsedCounter(CrmResourceType resource, sai_acl_stage_t sta { m_resourcesMap.at(resource).countersMap[getCrmAclKey(stage, point)].usedCounter--; - // Remove ACL table related counters + // Remove ACL table related counters if (resource == CrmResourceType::CRM_ACL_TABLE) { auto & cntMap = m_resourcesMap.at(CrmResourceType::CRM_ACL_TABLE).countersMap; @@ -436,20 +440,18 @@ void CrmOrch::getResAvailableCounters() 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; + vector resources(CRM_ACL_RESOURCE_COUNT); + attr.value.aclresource.count = CRM_ACL_RESOURCE_COUNT; + attr.value.aclresource.list = resources.data(); sai_status_t status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); - if ((status != SAI_STATUS_SUCCESS) && (status != SAI_STATUS_BUFFER_OVERFLOW)) + if (status == SAI_STATUS_BUFFER_OVERFLOW) { - SWSS_LOG_ERROR("Failed to get switch attribute %u , rv:%d", attr.id, status); - break; + resources.resize(attr.value.aclresource.count); + attr.value.aclresource.list = resources.data(); + status = sai_switch_api->get_switch_attribute(gSwitchId, 1, &attr); } - 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); @@ -601,7 +603,7 @@ string CrmOrch::getCrmAclKey(sai_acl_stage_t stage, sai_acl_bind_point_type_t bi case SAI_ACL_BIND_POINT_TYPE_VLAN: key += ":VLAN"; break; - case SAI_ACL_BIND_POINT_TYPE_ROUTER_INTFERFACE: + case SAI_ACL_BIND_POINT_TYPE_ROUTER_INTERFACE: key += ":RIF"; break; case SAI_ACL_BIND_POINT_TYPE_SWITCH: diff --git a/orchagent/dtelorch.cpp b/orchagent/dtelorch.cpp new file mode 100644 index 0000000000..52b55cd841 --- /dev/null +++ b/orchagent/dtelorch.cpp @@ -0,0 +1,1407 @@ +#include "dtelorch.h" + +#include "logger.h" +#include "schema.h" +#include "converter.h" +#include "ipprefix.h" +#include "swssnet.h" + +using namespace std; +using namespace swss; + +extern sai_switch_api_t* sai_switch_api; +extern sai_dtel_api_t* sai_dtel_api; +extern sai_object_id_t gVirtualRouterId; +extern sai_object_id_t gSwitchId; + +dtelEventLookup_t dTelEventLookup = +{ + { EVENT_TYPE_FLOW_STATE, SAI_DTEL_EVENT_TYPE_FLOW_STATE }, + { EVENT_TYPE_FLOW_REPORT_ALL_PACKETS, SAI_DTEL_EVENT_TYPE_FLOW_REPORT_ALL_PACKETS }, + { EVENT_TYPE_FLOW_TCPFLAG, SAI_DTEL_EVENT_TYPE_FLOW_TCPFLAG }, + { EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH, SAI_DTEL_EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH }, + { EVENT_TYPE_QUEUE_REPORT_TAIL_DROP, SAI_DTEL_EVENT_TYPE_QUEUE_REPORT_TAIL_DROP }, + { EVENT_TYPE_DROP_REPORT, SAI_DTEL_EVENT_TYPE_DROP_REPORT } +}; + +DTelOrch::DTelOrch(DBConnector *db, vector tableNames, PortsOrch *portOrch) : + Orch(db, tableNames), + m_portOrch(portOrch) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + + sai_status_t status = sai_dtel_api->create_dtel(&dtelId, gSwitchId, 0, {}); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Error creating DTel id"); + return; + } + + attr.id = SAI_DTEL_ATTR_INT_L4_DSCP; + + attr.value.aclfield.data.u8 = 0x11; + attr.value.aclfield.mask.u8 = 0x3f; + + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set default INT L4 DSCP value/mask"); + return; + } +} + +DTelOrch::~DTelOrch() +{ + sai_status_t status = sai_dtel_api->remove_dtel(dtelId); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Error deleting DTel id"); + return; + } +} + +bool DTelOrch::intSessionExists(const string& name) +{ + SWSS_LOG_ENTER(); + + return m_dTelINTSessionTable.find(name) != m_dTelINTSessionTable.end(); +} + +bool DTelOrch::getINTSessionOid(const string& name, sai_object_id_t& oid) +{ + SWSS_LOG_ENTER(); + + if (!intSessionExists(name)) + { + return false; + } + + oid = m_dTelINTSessionTable[name].intSessionOid; + + return true; +} + +bool DTelOrch::increaseINTSessionRefCount(const string& name) +{ + SWSS_LOG_ENTER(); + + if (!intSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: INT session does not exist"); + return false; + } + + ++m_dTelINTSessionTable[name].refCount; + + return true; +} + +bool DTelOrch::decreaseINTSessionRefCount(const string& name) +{ + SWSS_LOG_ENTER(); + + if (!intSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: INT session does not exist"); + return false; + } + + if (m_dTelINTSessionTable[name].refCount < 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Session reference counter could not be less than 0"); + return false; + } + + --m_dTelINTSessionTable[name].refCount; + + if(m_dTelINTSessionTable[name].refCount == 0) + { + deleteINTSession(name); + } + + return true; +} + +int64_t DTelOrch::getINTSessionRefCount(const string& name) +{ + if (!intSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: INT session does not exist"); + return -1; + } + + return m_dTelINTSessionTable[name].refCount; +} + +bool DTelOrch::reportSessionExists(const string& name) +{ + SWSS_LOG_ENTER(); + + return m_dTelReportSessionTable.find(name) != m_dTelReportSessionTable.end(); +} + +bool DTelOrch::getReportSessionOid(const string& name, sai_object_id_t& oid) +{ + SWSS_LOG_ENTER(); + + if (!reportSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: Report session does not exist"); + return false; + } + + oid = m_dTelReportSessionTable[name].reportSessionOid; + + return true; +} + +bool DTelOrch::increaseReportSessionRefCount(const string& name) +{ + SWSS_LOG_ENTER(); + + if (!reportSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: Report session does not exist"); + return false; + } + + ++m_dTelReportSessionTable[name].refCount; + + return true; +} + +bool DTelOrch::decreaseReportSessionRefCount(const string& name) +{ + SWSS_LOG_ENTER(); + + if (!reportSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: Report session does not exist"); + return false; + } + + if (m_dTelReportSessionTable[name].refCount < 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Session reference counter could not be less than 0"); + return false; + } + + --m_dTelReportSessionTable[name].refCount; + + if (m_dTelReportSessionTable[name].refCount == 0) + { + deleteReportSession(name); + } + + return true; +} + +int64_t DTelOrch::getReportSessionRefCount(const string& name) +{ + if (!reportSessionExists(name)) + { + SWSS_LOG_ERROR("DTEL ERROR: Report session does not exist"); + return -1; + } + + return m_dTelReportSessionTable[name].refCount; +} + +bool DTelOrch::isQueueReportEnabled(const string& port, const string& queue) +{ + SWSS_LOG_ENTER(); + + auto port_entry_iter = m_dTelPortTable.find(port); + + if (port_entry_iter == m_dTelPortTable.end() || + (port_entry_iter->second.queueTable).find(queue) == (port_entry_iter->second.queueTable).end()) + { + return false; + } + + return true; +} + +bool DTelOrch::getQueueReportOid(const string& port, const string& queue, sai_object_id_t& oid) +{ + SWSS_LOG_ENTER(); + + if (!isQueueReportEnabled(port, queue)) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue report not enabled on port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + oid = m_dTelPortTable[port].queueTable[queue].queueReportOid; + + return true; +} + +bool DTelOrch::removePortQueue(const string& port, const string& queue) +{ + if (!isQueueReportEnabled(port, queue)) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue report not enabled on port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + m_dTelPortTable[port].queueTable.erase(queue); + + if (m_dTelPortTable[port].queueTable.empty()) + { + m_dTelPortTable.erase(port); + } + + return true; +} + +bool DTelOrch::addPortQueue(const string& port, const string& queue, DTelQueueReportEntry **qreport) +{ + if (isQueueReportEnabled(port, queue)) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue report already enabled on port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + m_dTelPortTable[port] = DTelPortEntry(); + m_dTelPortTable[port].queueTable[queue] = DTelQueueReportEntry(); + *qreport = &m_dTelPortTable[port].queueTable[queue]; + return true; +} + +bool DTelOrch::isEventConfigured(const string& event) +{ + SWSS_LOG_ENTER(); + + if (m_dtelEventTable.find(event) == m_dtelEventTable.end()) + { + return false; + } + + return true; +} + +bool DTelOrch::getEventOid(const string& event, sai_object_id_t& oid) +{ + SWSS_LOG_ENTER(); + + if (!isEventConfigured(event)) + { + SWSS_LOG_ERROR("DTEL ERROR: Event not configured %s", event.c_str()); + return false; + } + + oid = m_dtelEventTable[event].eventOid; + + return true; +} + +void DTelOrch::addEvent(const string& event, const sai_object_id_t& event_oid, const string& report_session_id) +{ + if (isEventConfigured(event)) + { + SWSS_LOG_ERROR("DTEL ERROR: Event is already configured %s", event.c_str()); + return; + } + + DTelEventEntry event_entry; + event_entry.eventOid = event_oid; + event_entry.reportSessionId = report_session_id; + m_dtelEventTable[event] = event_entry; + + increaseReportSessionRefCount(report_session_id); +} + +void DTelOrch::removeEvent(const string& event) +{ + if (!isEventConfigured(event)) + { + SWSS_LOG_ERROR("DTEL ERROR: Event not configured %s", event.c_str()); + return; + } + + string& report_session_id = m_dtelEventTable[event].reportSessionId; + + decreaseReportSessionRefCount(report_session_id); + + m_dtelEventTable.erase(event); +} + +sai_status_t DTelOrch::updateSinkPortList() +{ + sai_attribute_t attr; + attr.id = SAI_DTEL_ATTR_SINK_PORT_LIST; + sai_status_t status = SAI_STATUS_SUCCESS; + + vector port_list; + + for (auto it = sinkPortList.begin(); it != sinkPortList.end(); it++) + { + if (it->second != 0) + { + port_list.push_back(it->second); + } + } + + attr.value.objlist.count = (uint32_t)port_list.size(); + if (port_list.size() == 0) + { + attr.value.objlist.list = {}; + } else { + attr.value.objlist.list = port_list.data(); + } + + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set INT sink port list"); + return status; + } + + return status; +} + +bool DTelOrch::addSinkPortToCache(const Port& port) +{ + if (sinkPortList.find(port.m_alias) == sinkPortList.end()) + { + return false; + } + + if (port.m_type != Port::PHY) + { + SWSS_LOG_ERROR("DTEL ERROR: Only physical ports supported as INT sink. %s is not a physical port", port.m_alias.c_str()); + return false; + } + + sinkPortList[port.m_alias] = port.m_port_id; + return true; +} + +bool DTelOrch::removeSinkPortFromCache(const string &port_alias) +{ + if (sinkPortList.find(port_alias) == sinkPortList.end()) + { + return false; + } + + sinkPortList[port_alias] = 0; + return true; +} + +void DTelOrch::update(SubjectType type, void *cntx) +{ + sai_status_t status; + + if (type != SUBJECT_TYPE_PORT_CHANGE) + { + return; + } + + PortUpdate *update = static_cast(cntx); + + /* Check if sink ports need to be updated */ + if (update->add) + { + if (addSinkPortToCache(update->port)) + { + status = updateSinkPortList(); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to update sink port list on port add"); + return; + } + } + } else { + if (removeSinkPortFromCache(update->port.m_alias)) + { + status = updateSinkPortList(); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to update sink port list on port remove"); + return; + } + } + } + + /* Check if queue reports need to be updated */ + auto port_entry_iter = m_dTelPortTable.find(update->port.m_alias); + + if (port_entry_iter == m_dTelPortTable.end()) + { + return; + } + + dTelPortQueueTable_t qTable = port_entry_iter->second.queueTable; + + for (auto it = qTable.begin(); it != qTable.end(); it++ ) + { + DTelQueueReportEntry qreport = it->second; + if (update->add) + { + if (qreport.queueReportOid != 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue report already enabled for port %s, queue %d", update->port.m_alias.c_str(), qreport.q_ind); + return; + } + + if (update->port.m_type != Port::PHY) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue reporting applies only to physical ports. %s is not a physical port", update->port.m_alias.c_str()); + return; + } + + qreport.queueOid = update->port.m_queue_ids[qreport.q_ind]; + + status = enableQueueReport(update->port.m_alias, qreport); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to update queue report for queue %d on port add %s", qreport.q_ind, update->port.m_alias.c_str()); + return; + } + } else { + if (qreport.queueReportOid == 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue report already disabled for port %s, queue %d", update->port.m_alias.c_str(), qreport.q_ind); + return; + } + + status = disableQueueReport(update->port.m_alias, it->first); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to update queue report for queue %d on port remove %s", qreport.q_ind, update->port.m_alias.c_str()); + return; + } + + qreport.queueOid = 0; + } + } +} + +void DTelOrch::doDtelTableTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + sai_status_t status; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string table_attr = kfvKey(t); + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + if (table_attr == INT_ENDPOINT) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_INT_ENDPOINT_ENABLE; + attr.value.booldata = (fvValue(e) == ENABLED) ? true : false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable INT endpoint mode"); + goto dtel_table_continue; + } + } + else if (table_attr == INT_TRANSIT) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_INT_TRANSIT_ENABLE; + attr.value.booldata = (fvValue(e) == ENABLED) ? true : false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable INT transit mode"); + goto dtel_table_continue; + } + } + else if (table_attr == POSTCARD) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_POSTCARD_ENABLE; + attr.value.booldata = (fvValue(e) == ENABLED) ? true : false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable DTel postcard"); + goto dtel_table_continue; + } + } + else if (table_attr == DROP_REPORT) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_DROP_REPORT_ENABLE; + attr.value.booldata = (fvValue(e) == ENABLED) ? true : false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable drop report"); + goto dtel_table_continue; + } + } + else if (table_attr == QUEUE_REPORT) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_QUEUE_REPORT_ENABLE; + attr.value.booldata = (fvValue(e) == ENABLED) ? true : false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable queue report"); + goto dtel_table_continue; + } + } + else if (table_attr == SWITCH_ID) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_SWITCH_ID; + attr.value.u32 = to_uint(fvValue(e)); + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set switch id"); + goto dtel_table_continue; + } + } + else if (table_attr == FLOW_STATE_CLEAR_CYCLE) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_FLOW_STATE_CLEAR_CYCLE; + attr.value.u16 = to_uint(fvValue(e)); + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set Dtel flow state clear cycle"); + goto dtel_table_continue; + } + } + else if (table_attr == LATENCY_SENSITIVITY) + { + FieldValueTuple e = kfvFieldsValues(t)[0]; + + attr.id = SAI_DTEL_ATTR_LATENCY_SENSITIVITY; + attr.value.u16 = to_uint(fvValue(e)); + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set Dtel latency sensitivity"); + goto dtel_table_continue; + } + } + else if (table_attr == SINK_PORT_LIST) + { + Port port; + + for (auto i : kfvFieldsValues(t)) + { + if (m_portOrch->getPort(fvField(i), port)) + { + if (port.m_type != Port::PHY) + { + SWSS_LOG_ERROR("DTEL ERROR: Only physical ports supported as INT sink. %s is not a physical port", fvField(i).c_str()); + goto dtel_table_continue; + } + + sinkPortList[fvField(i)] = port.m_port_id; + } else { + SWSS_LOG_ERROR("DTEL ERROR: Port to be added a sink port %s doesn't exist", fvField(i).c_str()); + sinkPortList[fvField(i)] = 0; + } + } + + if (sinkPortList.size() == 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Invalid INT sink port list"); + goto dtel_table_continue; + } + + status = updateSinkPortList(); + if (status != SAI_STATUS_SUCCESS) + { + goto dtel_table_continue; + } + } + else if (table_attr == INT_L4_DSCP) + { + attr.id = SAI_DTEL_ATTR_INT_L4_DSCP; + + if ((uint32_t)(kfvFieldsValues(t).size()) != 2) + { + SWSS_LOG_ERROR("DTEL ERROR: INT L4 DSCP attribute requires value and mask"); + goto dtel_table_continue; + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == INT_L4_DSCP_VALUE) + { + attr.value.aclfield.data.u8 = to_uint(fvValue(i)); + } + else if (fvField(i) == INT_L4_DSCP_MASK) + { + attr.value.aclfield.mask.u8 = to_uint(fvValue(i)); + } + } + + if (attr.value.aclfield.data.u8 == 0 || + attr.value.aclfield.mask.u8 == 0) + { + SWSS_LOG_ERROR("DTEL ERROR: Invalid INT L4 DSCP value/mask"); + goto dtel_table_continue; + } + + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set INT L4 DSCP value/mask"); + goto dtel_table_continue; + } + } + + } + else if (op == DEL_COMMAND) + { + if (table_attr == INT_ENDPOINT) + { + attr.id = SAI_DTEL_ATTR_INT_ENDPOINT_ENABLE; + attr.value.booldata = false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable INT endpoint mode"); + goto dtel_table_continue; + } + } + else if (table_attr == INT_TRANSIT) + { + attr.id = SAI_DTEL_ATTR_INT_TRANSIT_ENABLE; + attr.value.booldata = false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable INT transit mode"); + goto dtel_table_continue; + } + } + else if (table_attr == POSTCARD) + { + attr.id = SAI_DTEL_ATTR_POSTCARD_ENABLE; + attr.value.booldata = false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable postcard mode"); + goto dtel_table_continue; + } + } + else if (table_attr == DROP_REPORT) + { + attr.id = SAI_DTEL_ATTR_DROP_REPORT_ENABLE; + attr.value.booldata = false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable drop report"); + goto dtel_table_continue; + } + } + else if (table_attr == QUEUE_REPORT) + { + attr.id = SAI_DTEL_ATTR_QUEUE_REPORT_ENABLE; + attr.value.booldata = false; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable queue report"); + goto dtel_table_continue; + } + } + else if (table_attr == SWITCH_ID) + { + auto i = kfvFieldsValues(t); + + attr.id = SAI_DTEL_ATTR_SWITCH_ID; + attr.value.u32 = 0; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to reset switch id"); + goto dtel_table_continue; + } + } + else if (table_attr == FLOW_STATE_CLEAR_CYCLE) + { + auto i = kfvFieldsValues(t); + + attr.id = SAI_DTEL_ATTR_FLOW_STATE_CLEAR_CYCLE; + attr.value.u16 = 0; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to reset flow state clear cycle"); + goto dtel_table_continue; + } + } + else if (table_attr == LATENCY_SENSITIVITY) + { + auto i = kfvFieldsValues(t); + + attr.id = SAI_DTEL_ATTR_LATENCY_SENSITIVITY; + attr.value.u16 = 0; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to reset latency sensitivity"); + goto dtel_table_continue; + } + } + else if (table_attr == SINK_PORT_LIST) + { + sinkPortList.clear(); + status = updateSinkPortList(); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to reset sink port list"); + goto dtel_table_continue; + } + } + else if (table_attr == INT_L4_DSCP) + { + attr.id = SAI_DTEL_ATTR_INT_L4_DSCP; + attr.value.aclfield.data.u8 = 0; + attr.value.aclfield.mask.u8 = 0; + status = sai_dtel_api->set_dtel_attribute(dtelId, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to reset INT L4 DSCP value/mask"); + goto dtel_table_continue; + } + } + } + +dtel_table_continue: + it = consumer.m_toSync.erase(it); + } +} + +bool DTelOrch::deleteReportSession(const string &report_session_id) +{ + sai_object_id_t report_session_oid; + sai_status_t status; + + if (!reportSessionExists(report_session_id)) + { + SWSS_LOG_ERROR("DTEL ERROR: Report session %s does not exist", report_session_id.c_str()); + return false; + } + + if (!getReportSessionOid(report_session_id, report_session_oid)) + { + SWSS_LOG_ERROR("DTEL ERROR: Could not get report session oid for session %s", report_session_id.c_str()); + return false; + } + + status = sai_dtel_api->remove_dtel_report_session(report_session_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to delete report session %s", report_session_id.c_str()); + return false; + } + + m_dTelReportSessionTable.erase(report_session_id); + return true; +} + +void DTelOrch::doDtelReportSessionTableTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + sai_object_id_t report_session_oid; + + KeyOpFieldsValuesTuple t = it->second; + string report_session_id = kfvKey(t); + + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + vector report_session_attr; + vector dst_ip_list; + sai_ip_address_t dst_ip; + sai_attribute_t rs_attr; + + /* If report session already exists, delete it first */ + if (reportSessionExists(report_session_id)) + { + if (!deleteReportSession(report_session_id)) + { + goto report_session_table_continue; + } + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == SRC_IP) + { + IpAddress ip(fvValue(i)); + rs_attr.id = SAI_DTEL_REPORT_SESSION_ATTR_SRC_IP; + copy(rs_attr.value.ipaddr, ip); + report_session_attr.push_back(rs_attr); + } + else if (fvField(i) == DST_IP_LIST) + { + dst_ip.addr_family = SAI_IP_ADDR_FAMILY_IPV4; + size_t prev = 0; + size_t next = fvValue(i).find(';'); + while (next != std::string::npos) + { + IpAddress ip(fvValue(i).substr(prev, next - prev)); + copy(dst_ip, ip); + dst_ip_list.push_back(dst_ip); + prev = next + 1; + next = fvValue(i).find(';', prev); + } + + /* Add the last IP */ + IpAddress ip(fvValue(i).substr(prev)); + copy(dst_ip, ip); + dst_ip_list.push_back(dst_ip); + + rs_attr.id = SAI_DTEL_REPORT_SESSION_ATTR_DST_IP_LIST; + rs_attr.value.ipaddrlist.count = (uint32_t)dst_ip_list.size(); + rs_attr.value.ipaddrlist.list = dst_ip_list.data(); + report_session_attr.push_back(rs_attr); + } + else if (fvField(i) == VRF) + { + rs_attr.id = SAI_DTEL_REPORT_SESSION_ATTR_VIRTUAL_ROUTER_ID; + /* TODO: find a way to convert vrf to oid */ + rs_attr.value.oid = gVirtualRouterId; + report_session_attr.push_back(rs_attr); + } + else if (fvField(i) == TRUNCATE_SIZE) + { + rs_attr.id = SAI_DTEL_REPORT_SESSION_ATTR_TRUNCATE_SIZE; + rs_attr.value.u16 = to_uint(fvValue(i)); + report_session_attr.push_back(rs_attr); + } + else if (fvField(i) == UDP_DEST_PORT) + { + rs_attr.id = SAI_DTEL_REPORT_SESSION_ATTR_UDP_DST_PORT; + rs_attr.value.u16 = to_uint(fvValue(i)); + report_session_attr.push_back(rs_attr); + } + } + + status = sai_dtel_api->create_dtel_report_session(&report_session_oid, + gSwitchId, (uint32_t)report_session_attr.size(), report_session_attr.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set INT EP report session %s", report_session_id.c_str()); + goto report_session_table_continue; + } + + DTelReportSessionEntry rs_entry = { }; + rs_entry.reportSessionOid = report_session_oid; + m_dTelReportSessionTable[report_session_id] = rs_entry; + } + else if (op == DEL_COMMAND) + { + if (!reportSessionExists(report_session_id)) + { + goto report_session_table_continue; + } + + if(getReportSessionRefCount(report_session_id) > 1) + { + decreaseReportSessionRefCount(report_session_id); + goto report_session_table_continue; + } + + if (!deleteReportSession(report_session_id)) + { + goto report_session_table_continue; + } + } + +report_session_table_continue: + it = consumer.m_toSync.erase(it); + } +} + +bool DTelOrch::deleteINTSession(const string &int_session_id) +{ + sai_object_id_t int_session_oid; + sai_status_t status; + + if (!intSessionExists(int_session_id)) + { + SWSS_LOG_ERROR("DTEL ERROR: INT session %s does not exist", int_session_id.c_str()); + return false; + } + + if (!getINTSessionOid(int_session_id, int_session_oid)) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to get INT session oid %s", int_session_id.c_str()); + return false; + } + + status = sai_dtel_api->remove_dtel_int_session(int_session_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to delete INT session %s", int_session_id.c_str()); + return false; + } + + m_dTelINTSessionTable.erase(int_session_id); + + /* Notify all interested parties about INT session being deleted */ + DTelINTSessionUpdate update = {int_session_id, false}; + notify(SUBJECT_TYPE_INT_SESSION_CHANGE, static_cast(&update)); + return true; +} + +void DTelOrch::doDtelINTSessionTableTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + sai_object_id_t int_session_oid; + + KeyOpFieldsValuesTuple t = it->second; + string int_session_id = kfvKey(t); + + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + vector int_session_attr; + sai_attribute_t s_attr; + + /* If INT session already exists, delete it first */ + if (intSessionExists(int_session_id)) + { + if (!deleteINTSession(int_session_id)) + { + goto int_session_table_continue; + } + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == COLLECT_SWITCH_ID) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_COLLECT_SWITCH_ID; + s_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + int_session_attr.push_back(s_attr); + } + else if (fvField(i) == COLLECT_INGRESS_TIMESTAMP) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_COLLECT_INGRESS_TIMESTAMP; + s_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + int_session_attr.push_back(s_attr); + } + else if (fvField(i) == COLLECT_EGRESS_TIMESTAMP) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_COLLECT_EGRESS_TIMESTAMP; + s_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + int_session_attr.push_back(s_attr); + } + else if (fvField(i) == COLLECT_SWITCH_PORTS) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_COLLECT_SWITCH_PORTS; + s_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + int_session_attr.push_back(s_attr); + } + else if (fvField(i) == COLLECT_QUEUE_INFO) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_COLLECT_QUEUE_INFO; + s_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + int_session_attr.push_back(s_attr); + } + else if (fvField(i) == MAX_HOP_COUNT) + { + s_attr.id = SAI_DTEL_INT_SESSION_ATTR_MAX_HOP_COUNT; + s_attr.value.u16 = to_uint(fvValue(i)); + int_session_attr.push_back(s_attr); + } + } + + status = sai_dtel_api->create_dtel_int_session(&int_session_oid, + gSwitchId, (uint32_t)int_session_attr.size(), int_session_attr.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to set INT session %s", int_session_id.c_str()); + goto int_session_table_continue; + } + + DTelINTSessionEntry is_entry; + is_entry.intSessionOid = int_session_oid; + m_dTelINTSessionTable[int_session_id] = is_entry; + + /* Notify all interested parties about INT session being added */ + DTelINTSessionUpdate update = {int_session_id, true}; + notify(SUBJECT_TYPE_INT_SESSION_CHANGE, static_cast(&update)); + } + else if (op == DEL_COMMAND) + { + if (!intSessionExists(int_session_id)) + { + goto int_session_table_continue; + } + + if(getINTSessionRefCount(int_session_id) > 1) + { + decreaseINTSessionRefCount(int_session_id); + goto int_session_table_continue; + } + + if (!deleteINTSession(int_session_id)) + { + goto int_session_table_continue; + } + } + +int_session_table_continue: + it = consumer.m_toSync.erase(it); + } +} + +bool DTelOrch::disableQueueReport(const string &port, const string &queue) +{ + sai_object_id_t queue_report_oid; + sai_status_t status; + + if (!isQueueReportEnabled(port, queue)) + { + SWSS_LOG_ERROR("DTEL ERROR: queue report not enabled for port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + if (!getQueueReportOid(port, queue, queue_report_oid)) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to get queue report oid for port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + if (queue_report_oid == 0) + { + return true; + } + + status = sai_dtel_api->remove_dtel_queue_report(queue_report_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to disable queue report for port %s, queue %s", port.c_str(), queue.c_str()); + return false; + } + + m_dTelPortTable[port].queueTable[queue].queueReportOid = 0; + + return true; +} + +sai_status_t DTelOrch::enableQueueReport(const string& port, DTelQueueReportEntry& qreport) +{ + sai_attribute_t qr_attr; + sai_status_t status = SAI_STATUS_SUCCESS; + + if (qreport.queueOid == 0) + { + qreport.queueReportOid = 0; + return status; + } + + qr_attr.id = SAI_DTEL_QUEUE_REPORT_ATTR_QUEUE_ID; + qr_attr.value.oid = qreport.queueOid; + qreport.queue_report_attr.push_back(qr_attr); + + status = sai_dtel_api->create_dtel_queue_report(&qreport.queueReportOid, + gSwitchId, (uint32_t)qreport.queue_report_attr.size(), qreport.queue_report_attr.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to enable queue report on port %s, queue %d", port.c_str(), qreport.q_ind); + return status; + } + + return status; +} + +void DTelOrch::doDtelQueueReportTableTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + string key = kfvKey(t); + size_t found = key.find('|'); + string port = key.substr(0, found); + string queue_id = key.substr(found + 1); + Port port_obj; + uint32_t q_ind = stoi(queue_id); + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + vector queue_report_attr; + sai_attribute_t qr_attr; + DTelQueueReportEntry *qreport; + + /* If queue report is already enabled in port/queue, disable it first */ + if (isQueueReportEnabled(port, queue_id)) + { + if (!disableQueueReport(port, queue_id)) + { + goto queue_report_table_continue; + } + + if (!removePortQueue(port, queue_id)) + { + goto queue_report_table_continue; + } + } + + if (!addPortQueue(port, queue_id, &qreport)) + { + goto queue_report_table_continue; + } + + qreport->q_ind = q_ind; + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == REPORT_TAIL_DROP) + { + qr_attr.id = SAI_DTEL_QUEUE_REPORT_ATTR_TAIL_DROP; + qr_attr.value.booldata = (fvValue(i) == ENABLED) ? true : false; + qreport->queue_report_attr.push_back(qr_attr); + } + else if (fvField(i) == QUEUE_DEPTH_THRESHOLD) + { + qr_attr.id = SAI_DTEL_QUEUE_REPORT_ATTR_DEPTH_THRESHOLD; + qr_attr.value.u32 = to_uint(fvValue(i)); + qreport->queue_report_attr.push_back(qr_attr); + } + else if (fvField(i) == QUEUE_LATENCY_THRESHOLD) + { + qr_attr.id = SAI_DTEL_QUEUE_REPORT_ATTR_LATENCY_THRESHOLD; + qr_attr.value.u32 = to_uint(fvValue(i)); + qreport->queue_report_attr.push_back(qr_attr); + } + else if (fvField(i) == THRESHOLD_BREACH_QUOTA) + { + qr_attr.id = SAI_DTEL_QUEUE_REPORT_ATTR_BREACH_QUOTA; + qr_attr.value.u32 = to_uint(fvValue(i)); + qreport->queue_report_attr.push_back(qr_attr); + } + } + + if (m_portOrch->getPort(port, port_obj)) + { + if (port_obj.m_type != Port::PHY) + { + SWSS_LOG_ERROR("DTEL ERROR: Queue reporting applies only to physical ports. %s is not a physical port", port.c_str()); + goto queue_report_table_continue; + } + + qreport->queueOid = port_obj.m_queue_ids[qreport->q_ind]; + } else { + SWSS_LOG_ERROR("DTEL ERROR: Port for queue reporting %s doesn't exist", port.c_str()); + qreport->queueOid = 0; + } + + status = enableQueueReport(port, *qreport); + if (status != SAI_STATUS_SUCCESS) + { + goto queue_report_table_continue; + } + } + else if (op == DEL_COMMAND) + { + if (!disableQueueReport(port, queue_id)) + { + goto queue_report_table_continue; + } + + if (!removePortQueue(port, queue_id)) + { + goto queue_report_table_continue; + } + } + +queue_report_table_continue: + it = consumer.m_toSync.erase(it); + } +} + +bool DTelOrch::unConfigureEvent(string &event) +{ + sai_object_id_t event_oid; + sai_status_t status; + + if (!isEventConfigured(event)) + { + SWSS_LOG_ERROR("DTEL ERROR: Event is not configured %s", event.c_str()); + return false; + } + + if (!getEventOid(event, event_oid)) + { + SWSS_LOG_ERROR("DTEL ERROR: Could not get event oid for event %s", event.c_str()); + return false; + } + + status = sai_dtel_api->remove_dtel_event(event_oid); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to remove event %s", event.c_str()); + return false; + } + + removeEvent(event); + return true; +} + +void DTelOrch::doDtelEventTableTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + sai_status_t status; + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + sai_object_id_t event_oid; + string report_session_id; + + KeyOpFieldsValuesTuple t = it->second; + string event = kfvKey(t); + + string op = kfvOp(t); + + if (op == SET_COMMAND) + { + vector event_attr; + sai_attribute_t e_attr; + e_attr.id = SAI_DTEL_EVENT_ATTR_TYPE; + e_attr.value.s32 = dTelEventLookup[event]; + event_attr.push_back(e_attr); + + /* If event is already configured, un-configure it first */ + if (isEventConfigured(event)) + { + if (!unConfigureEvent(event)) + { + goto event_table_continue; + } + } + + for (auto i : kfvFieldsValues(t)) + { + if (fvField(i) == EVENT_REPORT_SESSION) + { + if (!reportSessionExists(fvValue(i))) + { + goto event_table_skip; + } + + e_attr.id = SAI_DTEL_EVENT_ATTR_REPORT_SESSION; + + if (!getReportSessionOid(fvValue(i), e_attr.value.oid)) + { + SWSS_LOG_ERROR("DTEL ERROR: Could not get report session oid for event %s, session %s", fvValue(i).c_str(), event.c_str()); + goto event_table_continue; + } + event_attr.push_back(e_attr); + report_session_id = fvValue(i); + } + else if (fvField(i) == EVENT_DSCP_VALUE) + { + e_attr.id = SAI_DTEL_EVENT_ATTR_DSCP_VALUE; + e_attr.value.u8 = to_uint(fvValue(i)); + event_attr.push_back(e_attr); + } + } + + status = sai_dtel_api->create_dtel_event(&event_oid, + gSwitchId, (uint32_t)event_attr.size(), event_attr.data()); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("DTEL ERROR: Failed to create event %s", event.c_str()); + goto event_table_continue; + } + + addEvent(event, event_oid, report_session_id); + } + else if (op == DEL_COMMAND) + { + if (!unConfigureEvent(event)) + { + goto event_table_continue; + } + } + +event_table_continue: + it = consumer.m_toSync.erase(it); + continue; + +event_table_skip: + it++; + } +} + +void DTelOrch::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + string table_name = consumer.getTableName(); + + if (table_name == CFG_DTEL_TABLE_NAME) + { + doDtelTableTask(consumer); + } + else if (table_name == CFG_DTEL_REPORT_SESSION_TABLE_NAME) + { + doDtelReportSessionTableTask(consumer); + } + else if (table_name == CFG_DTEL_INT_SESSION_TABLE_NAME) + { + doDtelINTSessionTableTask(consumer); + } + else if (table_name == CFG_DTEL_QUEUE_REPORT_TABLE_NAME) + { + doDtelQueueReportTableTask(consumer); + } + else if (table_name == CFG_DTEL_EVENT_TABLE_NAME) + { + doDtelEventTableTask(consumer); + } + else + { + SWSS_LOG_ERROR("DTEL ERROR: Invalid table %s", table_name.c_str()); + } +} diff --git a/orchagent/dtelorch.h b/orchagent/dtelorch.h new file mode 100644 index 0000000000..0a0cd2d6b1 --- /dev/null +++ b/orchagent/dtelorch.h @@ -0,0 +1,174 @@ +#ifndef SWSS_DTELORCH_H +#define SWSS_DTELORCH_H + +#include "orch.h" +#include "producerstatetable.h" +#include "portsorch.h" + +#include +#include + +#define INT_ENDPOINT "INT_ENDPOINT" +#define INT_TRANSIT "INT_TRANSIT" +#define POSTCARD "POSTCARD" +#define DROP_REPORT "DROP_REPORT" +#define QUEUE_REPORT "QUEUE_REPORT" +#define SWITCH_ID "SWITCH_ID" +#define FLOW_STATE_CLEAR_CYCLE "FLOW_STATE_CLEAR_CYCLE" +#define LATENCY_SENSITIVITY "LATENCY_SENSITIVITY" +#define SINK_PORT_LIST "SINK_PORT_LIST" +#define INT_L4_DSCP "INT_L4_DSCP" +#define INT_L4_DSCP_VALUE "INT_L4_DSCP_VALUE" +#define INT_L4_DSCP_MASK "INT_L4_DSCP_MASK" +#define SRC_IP "SRC_IP" +#define DST_IP_LIST "DST_IP_LIST" +#define VRF "VRF" +#define TRUNCATE_SIZE "TRUNCATE_SIZE" +#define UDP_DEST_PORT "UDP_DEST_PORT" +#define MAX_HOP_COUNT "MAX_HOP_COUNT" +#define COLLECT_SWITCH_ID "COLLECT_SWITCH_ID" +#define COLLECT_INGRESS_TIMESTAMP "COLLECT_INGRESS_TIMESTAMP" +#define COLLECT_EGRESS_TIMESTAMP "COLLECT_EGRESS_TIMESTAMP" +#define COLLECT_SWITCH_PORTS "COLLECT_SWITCH_PORTS" +#define COLLECT_QUEUE_INFO "COLLECT_QUEUE_INFO" +#define QUEUE_DEPTH_THRESHOLD "QUEUE_DEPTH_THRESHOLD" +#define QUEUE_LATENCY_THRESHOLD "QUEUE_LATENCY_THRESHOLD" +#define THRESHOLD_BREACH_QUOTA "THRESHOLD_BREACH_QUOTA" +#define REPORT_TAIL_DROP "REPORT_TAIL_DROP" +#define EVENT_REPORT_SESSION "EVENT_REPORT_SESSION" +#define EVENT_DSCP_VALUE "EVENT_DSCP_VALUE" +#define EVENT_TYPE_FLOW_STATE "EVENT_TYPE_FLOW_STATE" +#define EVENT_TYPE_FLOW_REPORT_ALL_PACKETS "EVENT_TYPE_FLOW_REPORT_ALL_PACKETS" +#define EVENT_TYPE_FLOW_TCPFLAG "EVENT_TYPE_FLOW_TCPFLAG" +#define EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH "EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH" +#define EVENT_TYPE_QUEUE_REPORT_TAIL_DROP "EVENT_TYPE_QUEUE_REPORT_TAIL_DROP" +#define EVENT_TYPE_DROP_REPORT "EVENT_TYPE_DROP_REPORT" + +#define ENABLED "TRUE" +#define DISABLED "FALSE" + +struct DTelINTSessionEntry +{ + sai_object_id_t intSessionOid; + int64_t refCount; + + DTelINTSessionEntry() : + intSessionOid(0), + refCount(1) + { + } +}; + +struct DTelReportSessionEntry +{ + sai_object_id_t reportSessionOid; + int64_t refCount; + + DTelReportSessionEntry() : + reportSessionOid(0), + refCount(1) + { + } +}; + +struct DTelQueueReportEntry +{ + sai_object_id_t queueReportOid; + vector queue_report_attr; + uint32_t q_ind; + sai_object_id_t queueOid; + + DTelQueueReportEntry() : + queueReportOid(0), + q_ind(0) + { + } +}; + +typedef map dTelPortQueueTable_t; + +struct DTelPortEntry +{ + dTelPortQueueTable_t queueTable; +}; + +struct DTelEventEntry +{ + sai_object_id_t eventOid; + string reportSessionId; + + DTelEventEntry() : + eventOid(0), + reportSessionId("") + { + } +}; + + +typedef map dTelINTSessionTable_t; +typedef map dTelReportSessionTable_t; +typedef map dTelPortTable_t; +typedef map dtelEventTable_t; +typedef map dtelEventLookup_t; +typedef map dtelSinkPortList_t; + +struct DTelINTSessionUpdate +{ + string session_id; + bool active; +}; + +class DTelOrch : public Orch, public Subject +{ +public: + DTelOrch(DBConnector *db, vector tableNames, PortsOrch *portOrch); + ~DTelOrch(); + + bool increaseINTSessionRefCount(const string&); + bool decreaseINTSessionRefCount(const string&); + bool getINTSessionOid(const string& name, sai_object_id_t& oid); + void update(SubjectType, void *); + +private: + + bool intSessionExists(const string& name); + void doTask(Consumer &consumer); + void doDtelTableTask(Consumer &consumer); + void doDtelReportSessionTableTask(Consumer &consumer); + void doDtelINTSessionTableTask(Consumer &consumer); + void doDtelQueueReportTableTask(Consumer &consumer); + void doDtelEventTableTask(Consumer &consumer); + + int64_t getINTSessionRefCount(const string& name); + bool reportSessionExists(const string& name); + bool getReportSessionOid(const string& name, sai_object_id_t& oid); + bool increaseReportSessionRefCount(const string& name); + bool decreaseReportSessionRefCount(const string& name); + int64_t getReportSessionRefCount(const string& name); + bool isQueueReportEnabled(const string& port, const string& queue); + bool getQueueReportOid(const string& port, const string& queue, sai_object_id_t& oid); + bool addPortQueue(const string& port, const string& queue, DTelQueueReportEntry **qreport); + bool removePortQueue(const string& port, const string& queue); + bool isEventConfigured(const string& event); + bool getEventOid(const string& event, sai_object_id_t& oid); + void addEvent(const string& event, const sai_object_id_t& event_oid, const string& report_session_id); + void removeEvent(const string& event); + bool deleteReportSession(const string& report_session_id); + bool deleteINTSession(const string& int_session_id); + bool disableQueueReport(const string& port, const string& queue); + bool unConfigureEvent(string& event); + sai_status_t updateSinkPortList(); + bool addSinkPortToCache(const Port& port); + bool removeSinkPortFromCache(const string& port_alias); + sai_status_t enableQueueReport(const string& port, DTelQueueReportEntry& qreport); + + PortsOrch *m_portOrch; + dTelINTSessionTable_t m_dTelINTSessionTable; + dTelReportSessionTable_t m_dTelReportSessionTable; + dTelPortTable_t m_dTelPortTable; + dtelEventTable_t m_dtelEventTable; + sai_object_id_t dtelId; + dtelSinkPortList_t sinkPortList; +}; + +#endif /* SWSS_DTELORCH_H */ diff --git a/orchagent/fdborch.cpp b/orchagent/fdborch.cpp index cffdbdb548..f5ee04ce21 100644 --- a/orchagent/fdborch.cpp +++ b/orchagent/fdborch.cpp @@ -26,14 +26,14 @@ FdbOrch::FdbOrch(DBConnector *db, string tableName, PortsOrch *port) : { m_portsOrch->attach(this); m_flushNotificationsConsumer = new NotificationConsumer(db, "FLUSHFDBREQUEST"); - auto flushNotifier = new Notifier(m_flushNotificationsConsumer, this); - Orch::addExecutor("", flushNotifier); + auto flushNotifier = new Notifier(m_flushNotificationsConsumer, this, "FLUSHFDBREQUEST"); + 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); + auto fdbNotifier = new Notifier(m_fdbNotificationConsumer, this, "FDB_NOTIFICATIONS"); + Orch::addExecutor(fdbNotifier); } void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_object_id_t bridge_port_id) @@ -53,6 +53,14 @@ void FdbOrch::update(sai_fdb_event_t type, const sai_fdb_entry_t* entry, sai_obj return; } + // we already have such entries + if (m_entries.find(update.entry) != m_entries.end()) + { + SWSS_LOG_INFO("FdbOrch notification: mac %s is already in bv_id 0x%lx", + update.entry.mac.to_string().c_str(), entry->bv_id); + break; + } + update.add = true; { @@ -352,9 +360,9 @@ void FdbOrch::doTask(NotificationConsumer& consumer) } this->update(fdbevent[i].event_type, &fdbevent[i].fdb_entry, oid); - - sai_deserialize_free_fdb_event_ntf(count, fdbevent); } + + sai_deserialize_free_fdb_event_ntf(count, fdbevent); } } diff --git a/orchagent/flexcounterorch.cpp b/orchagent/flexcounterorch.cpp index 69f94284e1..5aaec6b986 100644 --- a/orchagent/flexcounterorch.cpp +++ b/orchagent/flexcounterorch.cpp @@ -5,6 +5,7 @@ #include "notifier.h" #include "redisclient.h" #include "sai_serialize.h" +#include "pfcwdorch.h" extern sai_port_api_t *sai_port_api; @@ -14,8 +15,10 @@ unordered_map flexCounterGroupMap = { {"PORT", PORT_STAT_COUNTER_FLEX_COUNTER_GROUP}, {"QUEUE", QUEUE_STAT_COUNTER_FLEX_COUNTER_GROUP}, + {"PFCWD", PFC_WD_FLEX_COUNTER_GROUP}, }; + FlexCounterOrch::FlexCounterOrch(DBConnector *db, vector &tableNames): Orch(db, tableNames), m_flexCounterDb(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), @@ -61,13 +64,26 @@ void FlexCounterOrch::doTask(Consumer &consumer) const auto &field = fvField(valuePair); const auto &value = fvValue(valuePair); - if (field == POLL_INTERVAL_FIELD) + if (field == POLL_INTERVAL_FIELD) { vector fieldValues; fieldValues.emplace_back(POLL_INTERVAL_FIELD, value); m_flexCounterGroupTable->set(flexCounterGroupMap[key], fieldValues); } - /* In future add the support to disable/enable counter query here.*/ + else if(field == FLEX_COUNTER_STATUS_FIELD) + { + // Currently the counters are disabled by default + // The queue maps will be generated as soon as counters are enabled + gPortsOrch->generateQueueMap(); + + vector fieldValues; + fieldValues.emplace_back(FLEX_COUNTER_STATUS_FIELD, value); + m_flexCounterGroupTable->set(flexCounterGroupMap[key], fieldValues); + } + else + { + SWSS_LOG_NOTICE("Unsupported field %s", field.c_str()); + } } } diff --git a/orchagent/intfsorch.cpp b/orchagent/intfsorch.cpp index e97aed0192..3d67767c6d 100644 --- a/orchagent/intfsorch.cpp +++ b/orchagent/intfsorch.cpp @@ -10,6 +10,7 @@ #include "swssnet.h" #include "tokenize.h" #include "crmorch.h" +#include "bufferorch.h" extern sai_object_id_t gVirtualRouterId; @@ -17,9 +18,10 @@ 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 PortsOrch *gPortsOrch; extern CrmOrch *gCrmOrch; +extern BufferOrch *gBufferOrch; const int intfsorch_pri = 35; @@ -55,6 +57,27 @@ void IntfsOrch::decreaseRouterIntfsRefCount(const string &alias) alias.c_str(), m_syncdIntfses[alias].ref_count); } +bool IntfsOrch::setRouterIntfsMtu(Port &port) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + attr.id = SAI_ROUTER_INTERFACE_ATTR_MTU; + attr.value.u32 = port.m_mtu; + + sai_status_t status = sai_router_intfs_api-> + set_router_interface_attribute(port.m_rif_id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set router interface %s MTU to %u, rv:%d", + port.m_alias.c_str(), port.m_mtu, status); + return false; + } + SWSS_LOG_NOTICE("Set router interface %s MTU to %u", + port.m_alias.c_str(), port.m_mtu); + return true; +} + void IntfsOrch::doTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -97,6 +120,13 @@ void IntfsOrch::doTask(Consumer &consumer) continue; } + // buffer configuration hasn't been applied yet, hold from intf config. + if (!gBufferOrch->isPortReady(alias)) + { + it++; + continue; + } + auto it_intfs = m_syncdIntfses.find(alias); if (it_intfs == m_syncdIntfses.end()) { @@ -150,7 +180,8 @@ void IntfsOrch::doTask(Consumer &consumer) addSubnetRoute(port, ip_prefix); addIp2MeRoute(ip_prefix); - if(port.m_type == Port::VLAN && ip_prefix.isV4()) + + if (port.m_type == Port::VLAN && ip_prefix.isV4()) { addDirectedBroadcast(port, ip_prefix.getBroadcastIp()); } diff --git a/orchagent/intfsorch.h b/orchagent/intfsorch.h index 0c08350d33..61df9dee59 100644 --- a/orchagent/intfsorch.h +++ b/orchagent/intfsorch.h @@ -31,6 +31,8 @@ class IntfsOrch : public Orch void increaseRouterIntfsRefCount(const string&); void decreaseRouterIntfsRefCount(const string&); + + bool setRouterIntfsMtu(Port &port); private: IntfsTable m_syncdIntfses; void doTask(Consumer &consumer); diff --git a/orchagent/main.cpp b/orchagent/main.cpp index f88ad3589b..0f15928b4d 100644 --- a/orchagent/main.cpp +++ b/orchagent/main.cpp @@ -7,6 +7,7 @@ extern "C" { #include #include #include +#include #include #include #include @@ -22,6 +23,7 @@ extern "C" { #include "saihelper.h" #include "notifications.h" #include +#include "warm_restart.h" using namespace std; using namespace swss; @@ -77,11 +79,29 @@ void sighup_handler(int signo) } } +void syncd_apply_view() +{ + SWSS_LOG_NOTICE("Notify syncd APPLY_VIEW"); + + sai_status_t status; + sai_attribute_t attr; + attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD; + attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW; + status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); + + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to notify syncd APPLY_VIEW %d", status); + exit(EXIT_FAILURE); + } +} + int main(int argc, char **argv) { swss::Logger::linkToDbNative("orchagent"); SWSS_LOG_ENTER(); + WarmStart::checkWarmStart("orchagent"); if (signal(SIGHUP, sighup_handler) == SIG_ERR) { @@ -247,30 +267,25 @@ int main(int argc, char **argv) SWSS_LOG_NOTICE("Created underlay router interface ID %lx", gUnderlayIfId); /* Initialize orchestration components */ - DBConnector *appl_db = new DBConnector(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - DBConnector *config_db = new DBConnector(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector appl_db(APPL_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector config_db(CONFIG_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); + DBConnector state_db(STATE_DB, DBConnector::DEFAULT_UNIXSOCKET, 0); - OrchDaemon *orchDaemon = new OrchDaemon(appl_db, config_db); - if (!orchDaemon->init()) - { - SWSS_LOG_ERROR("Failed to initialize orchstration daemon"); - exit(EXIT_FAILURE); - } + auto orchDaemon = make_shared(&appl_db, &config_db, &state_db); try { - SWSS_LOG_NOTICE("Notify syncd APPLY_VIEW"); - - attr.id = SAI_REDIS_SWITCH_ATTR_NOTIFY_SYNCD; - attr.value.s32 = SAI_REDIS_NOTIFY_SYNCD_APPLY_VIEW; - status = sai_switch_api->set_switch_attribute(gSwitchId, &attr); - - if (status != SAI_STATUS_SUCCESS) + if (!orchDaemon->init()) { - SWSS_LOG_ERROR("Failed to notify syncd APPLY_VIEW %d", status); + SWSS_LOG_ERROR("Failed to initialize orchstration daemon"); exit(EXIT_FAILURE); } + if (!WarmStart::isWarmStart()) + { + syncd_apply_view(); + } + orchDaemon->start(); } catch (char const *e) diff --git a/orchagent/mirrororch.cpp b/orchagent/mirrororch.cpp index bfd53636fc..7266f6b87c 100644 --- a/orchagent/mirrororch.cpp +++ b/orchagent/mirrororch.cpp @@ -194,7 +194,8 @@ void MirrorOrch::createEntry(const string& key, const vector& d { SWSS_LOG_ENTER(); - MirrorEntry entry(getenv("platform")); + string platform = getenv("platform") ? getenv("platform") : ""; + MirrorEntry entry(platform); for (auto i : data) { @@ -251,7 +252,7 @@ void MirrorOrch::createEntry(const string& key, const vector& d } } - SWSS_LOG_INFO("Creating mirroring sessions %s\n", key.c_str()); + SWSS_LOG_NOTICE("Create mirror sessions %s", key.c_str()); auto session = m_syncdMirrors.find(key); if (session != m_syncdMirrors.end()) diff --git a/orchagent/notifier.h b/orchagent/notifier.h index 6113c80527..ca9011f23d 100644 --- a/orchagent/notifier.h +++ b/orchagent/notifier.h @@ -2,8 +2,8 @@ class Notifier : public Executor { public: - Notifier(NotificationConsumer *select, Orch *orch) - : Executor(select, orch) + Notifier(NotificationConsumer *select, Orch *orch, const string &name) + : Executor(select, orch, name) { } diff --git a/orchagent/observer.h b/orchagent/observer.h index 4a86d959b5..2853aa2663 100644 --- a/orchagent/observer.h +++ b/orchagent/observer.h @@ -14,6 +14,8 @@ enum SubjectType SUBJECT_TYPE_LAG_MEMBER_CHANGE, SUBJECT_TYPE_VLAN_MEMBER_CHANGE, SUBJECT_TYPE_MIRROR_SESSION_CHANGE, + SUBJECT_TYPE_INT_SESSION_CHANGE, + SUBJECT_TYPE_PORT_CHANGE, }; class Observer diff --git a/orchagent/orch.cpp b/orchagent/orch.cpp index ce6258e543..4825d82d6c 100644 --- a/orchagent/orch.cpp +++ b/orchagent/orch.cpp @@ -66,17 +66,14 @@ vector Orch::getSelectables() return selectables; } -void Consumer::execute() +size_t Consumer::addToSync(std::deque &entries) { SWSS_LOG_ENTER(); - std::deque entries; - getConsumerTable()->pops(entries); - /* Nothing popped */ if (entries.empty()) { - return; + return 0; } for (auto& entry: entries) @@ -123,6 +120,61 @@ void Consumer::execute() m_toSync[key] = KeyOpFieldsValuesTuple(key, op, existing_values); } } + return entries.size(); +} + +// TODO: Table should be const +size_t Consumer::refillToSync(Table* table) +{ + std::deque entries; + vector keys; + table->getKeys(keys); + for (const auto &key: keys) + { + KeyOpFieldsValuesTuple kco; + + kfvKey(kco) = key; + kfvOp(kco) = SET_COMMAND; + + if (!table->get(key, kfvFieldsValues(kco))) + { + continue; + } + entries.push_back(kco); + } + + return addToSync(entries); +} + +size_t Consumer::refillToSync() +{ + ConsumerTableBase *consumerTable = getConsumerTable(); + + auto subTable = dynamic_cast(consumerTable); + if (subTable != NULL) + { + std::deque entries; + subTable->pops(entries); + return addToSync(entries); + } + else + { + // consumerTable is either ConsumerStateTable or ConsumerTable + auto db = consumerTable->getDbConnector(); + string tableName = consumerTable->getTableName(); + auto table = Table(db, tableName); + return refillToSync(&table); + } +} + +void Consumer::execute() +{ + SWSS_LOG_ENTER(); + + std::deque entries; + getConsumerTable()->pops(entries); + + addToSync(entries); drain(); } @@ -133,17 +185,93 @@ void Consumer::drain() m_orch->doTask(*this); } +string Consumer::dumpTuple(KeyOpFieldsValuesTuple &tuple) +{ + string s = getTableName() + getConsumerTable()->getTableNameSeparator() + kfvKey(tuple) + + "|" + kfvOp(tuple); + for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) + { + s += "|" + fvField(*i) + ":" + fvValue(*i); + } + + return s; +} + +void Consumer::dumpPendingTasks(vector &ts) +{ + for (auto &tm : m_toSync) + { + KeyOpFieldsValuesTuple& tuple = tm.second; + + string s = dumpTuple(tuple); + + ts.push_back(s); + } +} + +size_t Orch::addExistingData(const string& tableName) +{ + auto consumer = dynamic_cast(getExecutor(tableName)); + if (consumer == NULL) + { + SWSS_LOG_ERROR("No consumer %s in Orch", tableName.c_str()); + return 0; + } + + return consumer->refillToSync(); +} + +// TODO: Table should be const +size_t Orch::addExistingData(Table *table) +{ + string tableName = table->getTableName(); + Consumer* consumer = dynamic_cast(getExecutor(tableName)); + if (consumer == NULL) + { + SWSS_LOG_ERROR("No consumer %s in Orch", tableName.c_str()); + return 0; + } + + return consumer->refillToSync(table); +} + +bool Orch::bake() +{ + SWSS_LOG_ENTER(); + + for(auto &it : m_consumerMap) + { + string executorName = it.first; + auto executor = it.second; + auto consumer = dynamic_cast(executor.get()); + if (consumer == NULL) + { + continue; + } + + size_t refilled = consumer->refillToSync(); + SWSS_LOG_NOTICE("Add warm input: %s, %zd", executorName.c_str(), refilled); + } + + return true; +} + /* - Validates reference has proper format which is [table_name:object_name] - validates table_name exists - validates object with object_name exists + +- Special case: +- Deem reference format [] as valid, and return true. But in such a case, +- both type_name and object_name are cleared to empty strings as an +- indication to the caller of the special case */ bool Orch::parseReference(type_map &type_maps, string &ref_in, string &type_name, string &object_name) { SWSS_LOG_ENTER(); SWSS_LOG_DEBUG("input:%s", ref_in.c_str()); - if (ref_in.size() < 3) + if (ref_in.size() < 2) { SWSS_LOG_ERROR("invalid reference received:%s\n", ref_in.c_str()); return false; @@ -153,6 +281,17 @@ bool Orch::parseReference(type_map &type_maps, string &ref_in, string &type_name SWSS_LOG_ERROR("malformed reference:%s. Must be surrounded by [ ]\n", ref_in.c_str()); return false; } + if (ref_in.size() == 2) + { + // value set by user is "[]" + // Deem it as a valid format + // clear both type_name and object_name + // as an indication to the caller that + // such a case has been encountered + type_name.clear(); + object_name.clear(); + return true; + } string ref_content = ref_in.substr(1, ref_in.size() - 2); vector tokens; tokens = tokenize(ref_content, delimiter); @@ -208,6 +347,10 @@ ref_resolve_status Orch::resolveFieldRefValue( { return ref_resolve_status::not_resolved; } + else if (ref_type_name.empty() && object_name.empty()) + { + return ref_resolve_status::empty; + } sai_object = (*(type_maps[ref_type_name]))[object_name]; hit = true; } @@ -227,6 +370,21 @@ void Orch::doTask() } } +void Orch::dumpPendingTasks(vector &ts) +{ + for(auto &it : m_consumerMap) + { + Consumer* consumer = dynamic_cast(it.second.get()); + if (consumer == NULL) + { + SWSS_LOG_DEBUG("Executor is not a Consumer"); + continue; + } + + consumer->dumpPendingTasks(ts); + } +} + void Orch::logfileReopen() { gRecordOfs.close(); @@ -248,12 +406,7 @@ void Orch::logfileReopen() void Orch::recordTuple(Consumer &consumer, KeyOpFieldsValuesTuple &tuple) { - string s = consumer.getTableName() + ":" + kfvKey(tuple) - + "|" + kfvOp(tuple); - for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) - { - s += "|" + fvField(*i) + ":" + fvValue(*i); - } + string s = consumer.dumpTuple(tuple); gRecordOfs << getTimestamp() << "|" << s << endl; @@ -267,13 +420,7 @@ void Orch::recordTuple(Consumer &consumer, KeyOpFieldsValuesTuple &tuple) string Orch::dumpTuple(Consumer &consumer, KeyOpFieldsValuesTuple &tuple) { - string s = consumer.getTableName() + ":" + kfvKey(tuple) - + "|" + kfvOp(tuple); - for (auto i = kfvFieldsValues(tuple).begin(); i != kfvFieldsValues(tuple).end(); i++) - { - s += "|" + fvField(*i) + ":" + fvValue(*i); - } - + string s = consumer.dumpTuple(tuple); return s; } @@ -360,21 +507,27 @@ bool Orch::parseIndexRange(const string &input, sai_uint32_t &range_low, sai_uin void Orch::addConsumer(DBConnector *db, string tableName, int pri) { - if (db->getDbId() == CONFIG_DB) + if (db->getDbId() == CONFIG_DB || db->getDbId() == STATE_DB) { - addExecutor(tableName, new Consumer(new SubscriberStateTable(db, tableName, TableConsumable::DEFAULT_POP_BATCH_SIZE, pri), this)); + addExecutor(new Consumer(new SubscriberStateTable(db, tableName, TableConsumable::DEFAULT_POP_BATCH_SIZE, pri), this, tableName)); } else { - addExecutor(tableName, new Consumer(new ConsumerStateTable(db, tableName, gBatchSize, pri), this)); + addExecutor(new Consumer(new ConsumerStateTable(db, tableName, gBatchSize, pri), this, tableName)); } } -void Orch::addExecutor(string executorName, Executor* executor) +void Orch::addExecutor(Executor* executor) { - m_consumerMap.emplace(std::piecewise_construct, - std::forward_as_tuple(executorName), + auto inserted = m_consumerMap.emplace(std::piecewise_construct, + std::forward_as_tuple(executor->getName()), std::forward_as_tuple(executor)); + + // If there is duplication of executorName in m_consumerMap, logic error + if (!inserted.second) + { + SWSS_LOG_THROW("Duplicated executorName in m_consumerMap: %s", executor->getName().c_str()); + } } Executor *Orch::getExecutor(string executorName) diff --git a/orchagent/orch.h b/orchagent/orch.h index eddece4081..dfbadb62bf 100644 --- a/orchagent/orch.h +++ b/orchagent/orch.h @@ -33,6 +33,8 @@ const char config_db_key_delimiter = '|'; #define MLNX_PLATFORM_SUBSTRING "mellanox" #define BRCM_PLATFORM_SUBSTRING "broadcom" +#define BFN_PLATFORM_SUBSTRING "barefoot" +#define VS_PLATFORM_SUBSTRING "vs" #define CONFIGDB_KEY_SEPARATOR "|" #define DEFAULT_KEY_SEPARATOR ":" @@ -66,9 +68,10 @@ class Orch; class Executor : public Selectable { public: - Executor(Selectable *selectable, Orch *orch) + Executor(Selectable *selectable, Orch *orch, const string &name) : m_selectable(selectable) , m_orch(orch) + , m_name(name) { } @@ -89,24 +92,32 @@ class Executor : public Selectable virtual void execute() { } virtual void drain() { } + virtual string getName() const + { + return m_name; + } + protected: Selectable *m_selectable; Orch *m_orch; + // Name for Executor + string m_name; + // Get the underlying selectable Selectable *getSelectable() const { return m_selectable; } }; class Consumer : public Executor { public: - Consumer(TableConsumable *select, Orch *orch) - : Executor(select, orch) + Consumer(ConsumerTableBase *select, Orch *orch, const string &name) + : Executor(select, orch, name) { } - TableConsumable *getConsumerTable() const + ConsumerTableBase *getConsumerTable() const { - return static_cast(getSelectable()); + return static_cast(getSelectable()); } string getTableName() const @@ -114,12 +125,26 @@ class Consumer : public Executor { return getConsumerTable()->getTableName(); } + int getDbId() const + { + return getConsumerTable()->getDbId(); + } + + string dumpTuple(KeyOpFieldsValuesTuple &tuple); + void dumpPendingTasks(vector &ts); + + size_t refillToSync(); + size_t refillToSync(Table* table); void execute(); void drain(); /* Store the latest 'golden' status */ // TODO: hide? SyncMap m_toSync; + +protected: + // Returns: the number of entries added to m_toSync + size_t addToSync(std::deque &entries); }; typedef map> ConsumerMap; @@ -130,6 +155,7 @@ typedef enum field_not_found, multiple_instances, not_resolved, + empty, failure } ref_resolve_status; @@ -147,6 +173,14 @@ class Orch vector getSelectables(); + // add the existing table data (left by warm reboot) to the consumer todo task list. + size_t addExistingData(Table *table); + size_t addExistingData(const string& tableName); + + // Prepare for warm start if Redis contains valid input data + // otherwise fallback to cold start + virtual bool bake(); + /* Iterate all consumers in m_consumerMap and run doTask(Consumer) */ void doTask(); @@ -157,6 +191,8 @@ class Orch /* TODO: refactor recording */ static void recordTuple(Consumer &consumer, KeyOpFieldsValuesTuple &tuple); + + void dumpPendingTasks(vector &ts); protected: ConsumerMap m_consumerMap; @@ -168,7 +204,7 @@ class Orch ref_resolve_status resolveFieldRefArray(type_map&, const string&, KeyOpFieldsValuesTuple&, vector&); /* Note: consumer will be owned by this class */ - void addExecutor(string executorName, Executor* executor); + void addExecutor(Executor* executor); Executor *getExecutor(string executorName); private: void addConsumer(DBConnector *db, string tableName, int pri = default_orch_pri); diff --git a/orchagent/orchdaemon.cpp b/orchagent/orchdaemon.cpp index 91aa39ffc3..63de2326f2 100644 --- a/orchagent/orchdaemon.cpp +++ b/orchagent/orchdaemon.cpp @@ -3,6 +3,7 @@ #include "orchdaemon.h" #include "logger.h" #include +#include "warm_restart.h" #define SAI_SWITCH_ATTR_CUSTOM_RANGE_BASE SAI_SWITCH_ATTR_CUSTOM_RANGE_START #include "sairedis.h" @@ -16,20 +17,24 @@ using namespace swss; extern sai_switch_api_t* sai_switch_api; extern sai_object_id_t gSwitchId; - +extern void syncd_apply_view(); /* * Global orch daemon variables */ PortsOrch *gPortsOrch; FdbOrch *gFdbOrch; +IntfsOrch *gIntfsOrch; NeighOrch *gNeighOrch; RouteOrch *gRouteOrch; AclOrch *gAclOrch; CrmOrch *gCrmOrch; +BufferOrch *gBufferOrch; +SwitchOrch *gSwitchOrch; -OrchDaemon::OrchDaemon(DBConnector *applDb, DBConnector *configDb) : +OrchDaemon::OrchDaemon(DBConnector *applDb, DBConnector *configDb, DBConnector *stateDb) : m_applDb(applDb), - m_configDb(configDb) + m_configDb(configDb), + m_stateDb(stateDb) { SWSS_LOG_ENTER(); } @@ -39,9 +44,6 @@ OrchDaemon::~OrchDaemon() SWSS_LOG_ENTER(); for (Orch *o : m_orchList) delete(o); - - delete(m_configDb); - delete(m_applDb); } bool OrchDaemon::init() @@ -50,7 +52,7 @@ bool OrchDaemon::init() string platform = getenv("platform") ? getenv("platform") : ""; - SwitchOrch *switch_orch = new SwitchOrch(m_applDb, APP_SWITCH_TABLE_NAME); + gSwitchOrch = new SwitchOrch(m_applDb, APP_SWITCH_TABLE_NAME); const int portsorch_base_pri = 40; @@ -58,15 +60,15 @@ bool OrchDaemon::init() { 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_TABLE_NAME, portsorch_base_pri + 4 }, { 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); - gNeighOrch = new NeighOrch(m_applDb, APP_NEIGH_TABLE_NAME, intfs_orch); + gIntfsOrch = new IntfsOrch(m_applDb, APP_INTF_TABLE_NAME); + gNeighOrch = new NeighOrch(m_applDb, APP_NEIGH_TABLE_NAME, gIntfsOrch); 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); @@ -92,22 +94,81 @@ bool OrchDaemon::init() CFG_BUFFER_PORT_INGRESS_PROFILE_LIST_NAME, CFG_BUFFER_PORT_EGRESS_PROFILE_LIST_NAME }; - BufferOrch *buffer_orch = new BufferOrch(m_configDb, buffer_tables); + gBufferOrch = 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, 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 + TableConnector confDbAclTable(m_configDb, CFG_ACL_TABLE_NAME); + TableConnector confDbAclRuleTable(m_configDb, CFG_ACL_RULE_TABLE_NAME); + TableConnector stateDbLagTable(m_stateDb, STATE_LAG_TABLE_NAME); + + vector acl_table_connectors = { + confDbAclTable, + confDbAclRuleTable, + stateDbLagTable }; - gAclOrch = new AclOrch(m_configDb, acl_tables, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch); - 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 dtel_tables = { + CFG_DTEL_TABLE_NAME, + CFG_DTEL_REPORT_SESSION_TABLE_NAME, + CFG_DTEL_INT_SESSION_TABLE_NAME, + CFG_DTEL_QUEUE_REPORT_TABLE_NAME, + CFG_DTEL_EVENT_TABLE_NAME + }; + + /* + * The order of the orch list is important for state restore of warm start and + * the queued processing in m_toSync map after gPortsOrch->isInitDone() is set. + * + * For the multiple consumers in ports_tables, tasks for LAG_TABLE is processed before VLAN_TABLE + * when iterating ConsumerMap. + * That is ensured implicitly by the order of map key, "LAG_TABLE" is smaller than "VLAN_TABLE" in lexicographic order. + */ + m_orchList = { gSwitchOrch, gCrmOrch, gBufferOrch, gPortsOrch, gIntfsOrch, gNeighOrch, gRouteOrch, copp_orch, tunnel_decap_orch, qos_orch}; + + bool initialize_dtel = false; + if (platform == BFN_PLATFORM_SUBSTRING || platform == VS_PLATFORM_SUBSTRING) + { + sai_attr_capability_t capability; + capability.create_implemented = true; + + /* Will uncomment this when saiobject.h support is added to SONiC */ + /* + sai_status_t status; + status = sai_query_attribute_capability(gSwitchId, SAI_OBJECT_TYPE_DTEL, SAI_DTEL_ATTR_SWITCH_ID, &capability); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Could not query Dataplane telemetry capability %d", status); + exit(EXIT_FAILURE); + } + */ + + if (capability.create_implemented) + { + initialize_dtel = true; + } + } + + DTelOrch *dtel_orch = NULL; + if (initialize_dtel) + { + dtel_orch = new DTelOrch(m_configDb, dtel_tables, gPortsOrch); + m_orchList.push_back(dtel_orch); + gAclOrch = new AclOrch(acl_table_connectors, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch, dtel_orch); + } else { + gAclOrch = new AclOrch(acl_table_connectors, gPortsOrch, mirror_orch, gNeighOrch, gRouteOrch); + } + + m_orchList.push_back(gFdbOrch); + m_orchList.push_back(mirror_orch); + m_orchList.push_back(gAclOrch); + m_orchList.push_back(vrf_orch); + + m_select = new Select(); vector flex_counter_tables = { CFG_FLEX_COUNTER_TABLE_NAME @@ -224,6 +285,8 @@ void OrchDaemon::start() { SWSS_LOG_ENTER(); + warmRestoreAndSyncUp(); + for (Orch *o : m_orchList) { m_select->addSelectables(o->getSelectables()); @@ -244,12 +307,6 @@ void OrchDaemon::start() if (ret == Select::TIMEOUT) { - /* Let sairedis to flush all SAI function call to ASIC DB. - * Normally the redis pipeline will flush when enough request - * accumulated. Still it is possible that small amount of - * requests live in it. When the daemon has nothing to do, it - * is a good chance to flush the pipeline */ - flush(); continue; } @@ -263,5 +320,99 @@ void OrchDaemon::start() for (Orch *o : m_orchList) o->doTask(); + /* Let sairedis to flush all SAI function call to ASIC DB. + * Normally the redis pipeline will flush when enough request + * accumulated. Still it is possible that small amount of + * requests live in it. When the daemon has finished events/tasks, it + * is a good chance to flush the pipeline before next select happened. + */ + flush(); } } + +/* + * Try to perform orchagent state restore and dynamic states sync up if + * warm start reqeust is detected. + */ +void OrchDaemon::warmRestoreAndSyncUp() +{ + if (!WarmStart::isWarmStart()) + { + return; + } + + WarmStart::setWarmStartState("orchagent", WarmStart::INIT); + + for (Orch *o : m_orchList) + { + o->bake(); + } + + /* + * First iteration is to handle all the existing data in predefined order. + */ + for (Orch *o : m_orchList) + { + o->doTask(); + } + /* + * Drain remaining data that are out of order like LAG_MEMBER_TABLE and VLAN_MEMBER_TABLE + * since they were checked before LAG_TABLE and VLAN_TABLE. + */ + for (Orch *o : m_orchList) + { + o->doTask(); + } + + /* + * At this point, all the pre-existing data should have been processed properly, and + * orchagent should be in exact same state of pre-shutdown. + * Perform restore validation as needed. + */ + warmRestoreValidation(); + + SWSS_LOG_NOTICE("Orchagent state restore done"); + syncd_apply_view(); + + /* TODO: perform port and fdb state sync up*/ + + /* + * Note. Arp sync up is handled in neighsyncd. + * The "RECONCILED" state of orchagent doesn't mean the state related to neighbor is up to date. + */ + WarmStart::setWarmStartState("orchagent", WarmStart::RECONCILED); +} + +/* + * Get tasks to sync for consumers of each orch being managed by this orch daemon + */ +void OrchDaemon::getTaskToSync(vector &ts) +{ + for (Orch *o : m_orchList) + { + o->dumpPendingTasks(ts); + } +} + + +/* Perform basic validation after start restore for warm start */ +bool OrchDaemon::warmRestoreValidation() +{ + /* + * No pending task should exist for any of the consumer at this point. + * All the prexisting data in appDB and configDb have been read and processed. + */ + vector ts; + getTaskToSync(ts); + if (ts.size() != 0) + { + // TODO: Update this section accordingly once pre-warmStart consistency validation is ready. + SWSS_LOG_NOTICE("There are pending consumer tasks after restore: "); + for(auto &s : ts) + { + SWSS_LOG_NOTICE("%s", s.c_str()); + } + } + WarmStart::setWarmStartState("orchagent", WarmStart::RESTORED); + return true; +} diff --git a/orchagent/orchdaemon.h b/orchagent/orchdaemon.h index 5a7d8aac63..53210b7477 100644 --- a/orchagent/orchdaemon.h +++ b/orchagent/orchdaemon.h @@ -29,14 +29,18 @@ using namespace swss; class OrchDaemon { public: - OrchDaemon(DBConnector *, DBConnector *); + OrchDaemon(DBConnector *, DBConnector *, DBConnector *); ~OrchDaemon(); bool init(); void start(); + void warmRestoreAndSyncUp(); + void getTaskToSync(vector &ts); + bool warmRestoreValidation(); private: DBConnector *m_applDb; DBConnector *m_configDb; + DBConnector *m_stateDb; std::vector m_orchList; Select *m_select; diff --git a/orchagent/pfc_detect_barefoot.lua b/orchagent/pfc_detect_barefoot.lua new file mode 100644 index 0000000000..892be8b5fc --- /dev/null +++ b/orchagent/pfc_detect_barefoot.lua @@ -0,0 +1,91 @@ +-- KEYS - queue IDs +-- ARGV[1] - counters db index +-- ARGV[2] - counters table name +-- ARGV[3] - poll time interval +-- return queue Ids that satisfy criteria + +local counters_db = ARGV[1] +local counters_table_name = ARGV[2] +local poll_time = tonumber(ARGV[3]) + +local rets = {} + +redis.call('SELECT', counters_db) + +-- Iterate through each queue +local n = table.getn(KEYS) +for i = n, 1, -1 do + local counter_keys = redis.call('HKEYS', counters_table_name .. ':' .. KEYS[i]) + local counter_num = 0 + local old_counter_num = 0 + 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_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') + + -- DEBUG CODE START. Uncomment to enable + local debug_storm = redis.call('HGET', counters_table_name .. ':' .. KEYS[i], 'DEBUG_STORM') + -- DEBUG CODE END. + + -- 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 + + -- 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 + +return rets diff --git a/orchagent/pfcwdorch.cpp b/orchagent/pfcwdorch.cpp index 065bfc4d03..de892e30d4 100644 --- a/orchagent/pfcwdorch.cpp +++ b/orchagent/pfcwdorch.cpp @@ -9,7 +9,6 @@ #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" @@ -620,7 +619,7 @@ PfcWdSwOrch::PfcWdSwOrch( vector &tableNames, const vector &portStatIds, const vector &queueStatIds, - const vector &queueAttrIds, + const vector &queueAttrIds, int pollInterval): PfcWdOrch(db, tableNames), m_flexCounterDb(new DBConnector(FLEX_COUNTER_DB, DBConnector::DEFAULT_UNIXSOCKET, 0)), @@ -669,13 +668,13 @@ PfcWdSwOrch::PfcWdSwOrch( auto consumer = new swss::NotificationConsumer( PfcWdSwOrch::getCountersDb().get(), "PFC_WD"); - auto wdNotification = new Notifier(consumer, this); - Orch::addExecutor("PFC_WD", wdNotification); + auto wdNotification = new Notifier(consumer, this, "PFC_WD"); + Orch::addExecutor(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); + auto executor = new ExecutableTimer(timer, this, "PFC_WD_COUNTERS_POLL"); + Orch::addExecutor(executor); timer->start(); } diff --git a/orchagent/pfcwdorch.h b/orchagent/pfcwdorch.h index fada367689..631b3e5740 100644 --- a/orchagent/pfcwdorch.h +++ b/orchagent/pfcwdorch.h @@ -12,6 +12,8 @@ extern "C" { #include "sai.h" } +#define PFC_WD_FLEX_COUNTER_GROUP "PFC_WD" + enum class PfcWdAction { PFC_WD_ACTION_UNKNOWN, diff --git a/orchagent/port.h b/orchagent/port.h index bcedce4e08..4f955dab51 100644 --- a/orchagent/port.h +++ b/orchagent/port.h @@ -11,7 +11,7 @@ extern "C" { #include #define DEFAULT_PORT_VLAN_ID 1 -#define DEFAULT_MTU 9100 +#define DEFAULT_MTU 9100 namespace swss { @@ -66,6 +66,8 @@ class Port int m_index = 0; // PHY_PORT: index int m_ifindex = 0; uint32_t m_mtu = DEFAULT_MTU; + uint32_t m_speed = 0; // Mbps + bool m_autoneg = 0; sai_object_id_t m_port_id = 0; sai_port_fec_mode_t m_fec_mode = SAI_PORT_FEC_MODE_NONE; VlanInfo m_vlan_info; diff --git a/orchagent/portsorch.cpp b/orchagent/portsorch.cpp index a32333bc30..7f5df14fc7 100644 --- a/orchagent/portsorch.cpp +++ b/orchagent/portsorch.cpp @@ -1,4 +1,6 @@ #include "portsorch.h" +#include "intfsorch.h" +#include "bufferorch.h" #include "neighorch.h" #include @@ -29,8 +31,10 @@ 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 IntfsOrch *gIntfsOrch; extern NeighOrch *gNeighOrch; extern CrmOrch *gCrmOrch; +extern BufferOrch *gBufferOrch; #define VLAN_PREFIX "Vlan" #define DEFAULT_VLAN_ID 1 @@ -241,8 +245,8 @@ PortsOrch::PortsOrch(DBConnector *db, vector &tableNames) /* 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); + auto portStatusNotificatier = new Notifier(m_portStatusNotificationConsumer, this, "PORT_STATUS_NOTIFICATIONS"); + Orch::addExecutor(portStatusNotificatier); } void PortsOrch::removeDefaultVlanMembers() @@ -402,6 +406,45 @@ bool PortsOrch::getPortByBridgePortId(sai_object_id_t bridge_port_id, Port &port return false; } +bool PortsOrch::getAclBindPortId(string alias, sai_object_id_t &port_id) +{ + SWSS_LOG_ENTER(); + + Port port; + if (getPort(alias, port)) + { + switch (port.m_type) + { + case Port::PHY: + if (port.m_lag_member_id != SAI_NULL_OBJECT_ID) + { + SWSS_LOG_WARN("Invalid configuration. Bind table to LAG member %s is not allowed", alias.c_str()); + return false; + } + else + { + port_id = port.m_port_id; + } + break; + case Port::LAG: + port_id = port.m_lag_id; + break; + case Port::VLAN: + port_id = port.m_vlan_info.vlan_oid; + break; + default: + SWSS_LOG_ERROR("Failed to process port. Incorrect port %s type %d", alias.c_str(), port.m_type); + return false; + } + + return true; + } + else + { + return false; + } +} + void PortsOrch::setPort(string alias, Port p) { m_portList[alias] = p; @@ -561,7 +604,7 @@ bool PortsOrch::bindAclTable(sai_object_id_t id, sai_object_id_t table_oid, sai_ port.m_egress_acl_table_group_id = groupOid; } - gCrmOrch->incCrmAclUsedCounter(CrmResourceType::CRM_ACL_GROUP, (sai_acl_stage_t) group_attr.value.s32, SAI_ACL_BIND_POINT_TYPE_PORT); + gCrmOrch->incCrmAclUsedCounter(CrmResourceType::CRM_ACL_GROUP, ingress ? SAI_ACL_STAGE_INGRESS : SAI_ACL_STAGE_EGRESS, SAI_ACL_BIND_POINT_TYPE_PORT); switch (port.m_type) { @@ -585,7 +628,7 @@ bool PortsOrch::bindAclTable(sai_object_id_t id, sai_object_id_t table_oid, sai_ { // 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.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); @@ -652,7 +695,7 @@ bool PortsOrch::setPortPvid(Port &port, sai_uint32_t pvid) if (port.m_rif_id) { - SWSS_LOG_ERROR("pvid setting for router interface is not allowed"); + SWSS_LOG_ERROR("pvid setting for router interface %s is not allowed", port.m_alias.c_str()); return false; } @@ -834,7 +877,6 @@ bool PortsOrch::isSpeedSupported(const std::string& alias, sai_object_id_t port_ bool PortsOrch::setPortSpeed(sai_object_id_t port_id, sai_uint32_t speed) { SWSS_LOG_ENTER(); - sai_attribute_t attr; sai_status_t status; @@ -864,6 +906,23 @@ bool PortsOrch::getPortSpeed(sai_object_id_t port_id, sai_uint32_t &speed) return status == SAI_STATUS_SUCCESS; } +bool PortsOrch::setPortAdvSpeed(sai_object_id_t port_id, sai_uint32_t speed) +{ + SWSS_LOG_ENTER(); + sai_attribute_t attr; + sai_status_t status; + vector speeds; + speeds.push_back(speed); + + attr.id = SAI_PORT_ATTR_ADVERTISED_SPEED; + attr.value.u32list.list = speeds.data(); + attr.value.u32list.count = static_cast(speeds.size()); + + status = sai_port_api->set_port_attribute(port_id, &attr); + + return status == SAI_STATUS_SUCCESS; +} + bool PortsOrch::getQueueType(sai_object_id_t queue_id, string &type) { SWSS_LOG_ENTER(); @@ -897,6 +956,31 @@ bool PortsOrch::getQueueType(sai_object_id_t queue_id, string &type) return true; } +bool PortsOrch::setPortAutoNeg(sai_object_id_t id, int an) +{ + SWSS_LOG_ENTER(); + + sai_attribute_t attr; + attr.id = SAI_PORT_ATTR_AUTO_NEG_MODE; + switch(an) { + case 1: + attr.value.booldata = true; + break; + default: + attr.value.booldata = false; + break; + } + + sai_status_t status = sai_port_api->set_port_attribute(id, &attr); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to set AutoNeg %u to port pid:%lx", attr.value.booldata, id); + return false; + } + SWSS_LOG_INFO("Set AutoNeg %u to port pid:%lx", attr.value.booldata, id); + return true; +} + bool PortsOrch::setHostIntfsOperStatus(sai_object_id_t port_id, bool up) { SWSS_LOG_ENTER(); @@ -947,7 +1031,7 @@ void PortsOrch::updateDbPortOperStatus(sai_object_id_t id, sai_port_oper_status_ } } -bool PortsOrch::addPort(const set &lane_set, uint32_t speed) +bool PortsOrch::addPort(const set &lane_set, uint32_t speed, int an, string fec_mode) { SWSS_LOG_ENTER(); @@ -965,6 +1049,20 @@ bool PortsOrch::addPort(const set &lane_set, uint32_t speed) attr.value.u32list.count = static_cast(lanes.size()); attrs.push_back(attr); + if (an == true) + { + attr.id = SAI_PORT_ATTR_AUTO_NEG_MODE; + attr.value.booldata = true; + attrs.push_back(attr); + } + + if (!fec_mode.empty()) + { + attr.id = SAI_PORT_ATTR_FEC_MODE; + attr.value.u32 = fec_mode_map[fec_mode]; + attrs.push_back(attr); + } + sai_object_id_t port_id; sai_status_t status = sai_port_api->create_port(&port_id, gSwitchId, static_cast(attrs.size()), attrs.data()); if (status != SAI_STATUS_SUCCESS) @@ -984,6 +1082,13 @@ bool PortsOrch::removePort(sai_object_id_t port_id) { SWSS_LOG_ENTER(); + Port p; + if (getPort(port_id, p)) + { + PortUpdate update = {p, false }; + notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); + } + sai_status_t status = sai_port_api->remove_port(port_id); if (status != SAI_STATUS_SUCCESS) { @@ -1053,6 +1158,9 @@ bool PortsOrch::initPort(const string &alias, const set &lane_set) m_flexCounterTable->set(key, fields); + PortUpdate update = {p, true }; + notify(SUBJECT_TYPE_PORT_CHANGE, static_cast(&update)); + SWSS_LOG_NOTICE("Initialized port %s", alias.c_str()); } else @@ -1071,6 +1179,57 @@ bool PortsOrch::initPort(const string &alias, const set &lane_set) return true; } +bool PortsOrch::bake() +{ + SWSS_LOG_ENTER(); + + // Check the APP_DB port table for warm reboot + vector tuples; + bool foundPortConfigDone = m_portTable->get("PortConfigDone", tuples); + SWSS_LOG_NOTICE("foundPortConfigDone = %d", foundPortConfigDone); + + bool foundPortInitDone = m_portTable->get("PortInitDone", tuples); + SWSS_LOG_NOTICE("foundPortInitDone = %d", foundPortInitDone); + + vector keys; + m_portTable->getKeys(keys); + SWSS_LOG_NOTICE("m_portTable->getKeys %zd", keys.size()); + + if (!foundPortConfigDone || !foundPortInitDone) + { + SWSS_LOG_NOTICE("No port table, fallback to cold start"); + cleanPortTable(keys); + return false; + } + + if (m_portCount != keys.size() - 2) + { + // Invalid port table + SWSS_LOG_ERROR("Invalid port table: m_portCount, expecting %u, got %lu", + m_portCount, keys.size() - 2); + + cleanPortTable(keys); + return false; + } + + addExistingData(m_portTable.get()); + addExistingData(APP_LAG_TABLE_NAME); + addExistingData(APP_LAG_MEMBER_TABLE_NAME); + addExistingData(APP_VLAN_TABLE_NAME); + addExistingData(APP_VLAN_MEMBER_TABLE_NAME); + + return true; +} + +// Clean up port table +void PortsOrch::cleanPortTable(const vector& keys) +{ + for (auto& key : keys) + { + m_portTable->del(key); + } +} + void PortsOrch::doPortTask(Consumer &consumer) { SWSS_LOG_ENTER(); @@ -1124,6 +1283,7 @@ void PortsOrch::doPortTask(Consumer &consumer) string fec_mode; uint32_t mtu = 0; uint32_t speed = 0; + int an = -1; for (auto i : kfvFieldsValues(t)) { @@ -1138,6 +1298,7 @@ void PortsOrch::doPortTask(Consumer &consumer) int lane = stoi(lane_str); lane_set.insert(lane); } + } /* Set port admin status */ @@ -1150,19 +1311,33 @@ void PortsOrch::doPortTask(Consumer &consumer) /* Set port speed */ if (fvField(i) == "speed") + { speed = (uint32_t)stoul(fvValue(i)); + } /* Set port fec */ if (fvField(i) == "fec") fec_mode = fvValue(i); + + /* Set autoneg and ignore the port speed setting */ + if (fvField(i) == "autoneg") + { + an = (int)stoul(fvValue(i)); + } } /* Collect information about all received ports */ if (lane_set.size()) { - m_lanesAliasSpeedMap[lane_set] = make_tuple(alias, speed); + m_lanesAliasSpeedMap[lane_set] = make_tuple(alias, speed, an, fec_mode); } + // TODO: + // Fix the issue below + // After PortConfigDone, while waiting for "PortInitDone" and the first gBufferOrch->isPortReady(alias), + // the complete m_lanesAliasSpeedMap may be populated again, so initPort() will be called more than once + // for the same port. + /* Once all ports received, go through the each port and perform appropriate actions: * 1. Remove ports which don't exist anymore * 2. Create new ports @@ -1175,7 +1350,7 @@ void PortsOrch::doPortTask(Consumer &consumer) if (m_lanesAliasSpeedMap.find(it->first) == m_lanesAliasSpeedMap.end()) { char *platform = getenv("platform"); - if (platform && strstr(platform, MLNX_PLATFORM_SUBSTRING)) + if (platform && (strstr(platform, BFN_PLATFORM_SUBSTRING) || strstr(platform, MLNX_PLATFORM_SUBSTRING))) { if (!removePort(it->second)) { @@ -1204,9 +1379,9 @@ void PortsOrch::doPortTask(Consumer &consumer) // work around to avoid syncd termination on SAI error due missing create_port SAI API // can be removed when SAI redis return NotImplemented error char *platform = getenv("platform"); - if (platform && strstr(platform, MLNX_PLATFORM_SUBSTRING)) + if (platform && (strstr(platform, BFN_PLATFORM_SUBSTRING) || strstr(platform, MLNX_PLATFORM_SUBSTRING))) { - if (!addPort(it->first, get<1>(it->second))) + if (!addPort(it->first, get<1>(it->second), get<2>(it->second), get<3>(it->second))) { throw runtime_error("PortsOrch initialization failure."); } @@ -1236,19 +1411,58 @@ void PortsOrch::doPortTask(Consumer &consumer) } if (!m_portConfigDone) + { + // Not yet receive PortConfigDone. Save it for future retry + it++; + continue; + } + + if (alias == "PortConfigDone") { it = consumer.m_toSync.erase(it); continue; } + if (!gBufferOrch->isPortReady(alias)) + { + // buffer configuration hasn't been applied yet. save it for future retry + it++; + continue; + } + Port p; - if (!getPort(alias, p) && alias != "PortConfigDone") + if (!getPort(alias, p)) { SWSS_LOG_ERROR("Failed to get port id by alias:%s", alias.c_str()); } else { - /* Set port speed + if (an != -1 && an != p.m_autoneg) + { + if (setPortAutoNeg(p.m_port_id, an)) + { + SWSS_LOG_NOTICE("Set port %s AutoNeg to %u", alias.c_str(), an); + p.m_autoneg = an; + m_portList[alias] = p; + + /* Once AN is changed, need to reset the port speed or + port adv speed accordingly */ + if (speed == 0 && p.m_speed != 0) + { + speed = p.m_speed; + } + } + else + { + SWSS_LOG_ERROR("Failed to set port %s AN to %u", alias.c_str(), an); + it++; + continue; + } + } + + /* + * When AN is enabled, set the port adv speed, otherwise change the port speed. + * * 1. Get supported speed list and validate if the target speed is within the list * 2. Get the current port speed and check if it is the same as the target speed * 3. Set port admin status to DOWN before changing the speed @@ -1256,74 +1470,98 @@ void PortsOrch::doPortTask(Consumer &consumer) */ if (speed != 0) { - sai_uint32_t current_speed; + p.m_speed = speed; + m_portList[alias] = p; - if (!isSpeedSupported(alias, p.m_port_id, speed)) + if (p.m_autoneg) { - it++; - continue; + if (setPortAdvSpeed(p.m_port_id, speed)) + { + SWSS_LOG_NOTICE("Set port %s advertised speed to %u", alias.c_str(), speed); + } + else + { + SWSS_LOG_ERROR("Failed to set port %s advertised speed to %u", alias.c_str(), speed); + it++; + continue; + } } - - if (getPortSpeed(p.m_port_id, current_speed)) + else { - if (speed != current_speed) + sai_uint32_t current_speed; + + if (!isSpeedSupported(alias, p.m_port_id, speed)) { - if (setPortAdminStatus(p.m_port_id, false)) + it++; + continue; + } + + if (getPortSpeed(p.m_port_id, current_speed)) + { + if (speed != current_speed) { - if (setPortSpeed(p.m_port_id, speed)) + if (setPortAdminStatus(p.m_port_id, false)) { - SWSS_LOG_NOTICE("Set port %s speed to %u", alias.c_str(), speed); + if (setPortSpeed(p.m_port_id, speed)) + { + SWSS_LOG_NOTICE("Set port %s speed to %u", alias.c_str(), speed); + } + else + { + SWSS_LOG_ERROR("Failed to set port %s speed to %u", alias.c_str(), speed); + it++; + continue; + } } else { - SWSS_LOG_ERROR("Failed to set port %s speed to %u", alias.c_str(), speed); - it++; - continue; + SWSS_LOG_ERROR("Failed to set port admin status DOWN to set speed"); } } - else - { - SWSS_LOG_ERROR("Failed to set port admin status DOWN to set speed"); - } } - } - else - { - SWSS_LOG_ERROR("Failed to get current speed for port %s", alias.c_str()); + else + { + SWSS_LOG_ERROR("Failed to get current speed for port %s", alias.c_str()); + } } } - if (admin_status != "") + if (mtu != 0) { - if (setPortAdminStatus(p.m_port_id, admin_status == "up")) + if (setPortMtu(p.m_port_id, mtu)) { - SWSS_LOG_NOTICE("Set port %s admin status to %s", alias.c_str(), admin_status.c_str()); + p.m_mtu = mtu; + m_portList[alias] = p; + SWSS_LOG_NOTICE("Set port %s MTU to %u", alias.c_str(), mtu); + if (p.m_rif_id) + { + gIntfsOrch->setRouterIntfsMtu(p); + } } else { - SWSS_LOG_ERROR("Failed to set port %s admin status to %s", alias.c_str(), admin_status.c_str()); + SWSS_LOG_ERROR("Failed to set port %s MTU to %u", alias.c_str(), mtu); it++; continue; } } - if (mtu != 0) + if (!admin_status.empty()) { - if (setPortMtu(p.m_port_id, mtu)) + if (setPortAdminStatus(p.m_port_id, admin_status == "up")) { - p.m_mtu = mtu; - m_portList[alias] = p; - SWSS_LOG_NOTICE("Set port %s MTU to %u", alias.c_str(), mtu); + SWSS_LOG_NOTICE("Set port %s admin status to %s", alias.c_str(), admin_status.c_str()); } else { - SWSS_LOG_ERROR("Failed to set port %s MTU to %u", alias.c_str(), mtu); + SWSS_LOG_ERROR("Failed to set port %s admin status to %s", alias.c_str(), admin_status.c_str()); it++; continue; } } - if (fec_mode != "") + + if (!fec_mode.empty()) { if (fec_mode_map.find(fec_mode) != fec_mode_map.end()) { @@ -1348,7 +1586,6 @@ void PortsOrch::doPortTask(Consumer &consumer) { SWSS_LOG_ERROR("Unknown fec mode %s", fec_mode.c_str()); } - } } } @@ -1475,8 +1712,8 @@ void PortsOrch::doVlanMemberTask(Consumer &consumer) if (!getPort(port_alias, port)) { - SWSS_LOG_ERROR("Failed to locate port %s", port_alias.c_str()); - it = consumer.m_toSync.erase(it); + SWSS_LOG_DEBUG("%s is not not yet created, delaying", port_alias.c_str()); + it++; continue; } @@ -1769,61 +2006,6 @@ void PortsOrch::initializeQueues(Port &port) } SWSS_LOG_INFO("Get queues for port %s", port.m_alias.c_str()); - - /* Create the Queue map in the Counter DB */ - /* Add stat counters to flex_counter */ - vector queueVector; - vector queuePortVector; - vector queueIndexVector; - vector queueTypeVector; - - for (size_t queueIndex = 0; queueIndex < port.m_queue_ids.size(); ++queueIndex) - { - std::ostringstream name; - name << port.m_alias << ":" << queueIndex; - FieldValueTuple tuple(name.str(), sai_serialize_object_id(port.m_queue_ids[queueIndex])); - queueVector.push_back(tuple); - - FieldValueTuple queuePortTuple( - sai_serialize_object_id(port.m_queue_ids[queueIndex]), - sai_serialize_object_id(port.m_port_id)); - queuePortVector.push_back(queuePortTuple); - - FieldValueTuple queueIndexTuple( - sai_serialize_object_id(port.m_queue_ids[queueIndex]), - to_string(queueIndex)); - queueIndexVector.push_back(queueIndexTuple); - - - string queueType; - if (getQueueType(port.m_queue_ids[queueIndex], queueType)) - { - FieldValueTuple queueTypeTuple( - sai_serialize_object_id(port.m_queue_ids[queueIndex]), - queueType); - queueTypeVector.push_back(queueTypeTuple); - } - - string key = getQueueFlexCounterTableKey(sai_serialize_object_id(port.m_queue_ids[queueIndex])); - - std::string delimiter = ""; - std::ostringstream counters_stream; - for (auto it = queueStatIds.begin(); it != queueStatIds.end(); it++) - { - counters_stream << delimiter << sai_serialize_queue_stat(*it); - delimiter = ","; - } - - vector fieldValues; - fieldValues.emplace_back(QUEUE_COUNTER_ID_LIST, counters_stream.str()); - - m_flexCounterTable->set(key, fieldValues); - } - - m_queueTable->set("", queueVector); - m_queuePortTable->set("", queuePortVector); - m_queueIndexTable->set("", queueIndexVector); - m_queueTypeTable->set("", queueTypeVector); } void PortsOrch::initializePriorityGroups(Port &port) @@ -1882,20 +2064,43 @@ bool PortsOrch::initializePort(Port &p) } #endif - /* Set default port admin status to DOWN */ - /* FIXME: Do we need this? The default port admin status is false */ - setPortAdminStatus(p.m_port_id, false); + /* Check warm start states */ + vector tuples; + bool exist = m_portTable->get(p.m_alias, tuples); + string adminStatus, operStatus; + if (exist) + { + for (auto i : tuples) + { + if (fvField(i) == "admin_status") + { + adminStatus = fvValue(i); + } + else if (fvField(i) == "oper_status") + { + operStatus = fvValue(i); + } + } + } + SWSS_LOG_DEBUG("initializePort %s with admin %s and oper %s", p.m_alias.c_str(), adminStatus.c_str(), operStatus.c_str()); + + /* Set port admin status to DOWN if attr missing */ + if (adminStatus != "up") + { + setPortAdminStatus(p.m_port_id, false); + } /** - * Create default database port oper status as DOWN + * Create database port oper status as DOWN if attr missing * This status will be updated when receiving port_oper_status_notification. */ - vector vector; - FieldValueTuple tuple("oper_status", "down"); - vector.push_back(tuple); - m_portTable->set(p.m_alias, vector); - - CounterCheckOrch::getInstance().addPort(p); + if (operStatus != "up") + { + vector vector; + FieldValueTuple tuple("oper_status", "down"); + vector.push_back(tuple); + m_portTable->set(p.m_alias, vector); + } return true; } @@ -2380,6 +2585,74 @@ bool PortsOrch::removeLagMember(Port &lag, Port &port) return true; } +void PortsOrch::generateQueueMap() +{ + if (m_isQueueMapGenerated) + { + return; + } + + for (const auto& it: m_portList) + { + if (it.second.m_type == Port::PHY) + { + generateQueueMapPerPort(it.second); + } + } + + m_isQueueMapGenerated = true; +} + +void PortsOrch::generateQueueMapPerPort(const Port& port) +{ + /* Create the Queue map in the Counter DB */ + /* Add stat counters to flex_counter */ + vector queueVector; + vector queuePortVector; + vector queueIndexVector; + vector queueTypeVector; + + for (size_t queueIndex = 0; queueIndex < port.m_queue_ids.size(); ++queueIndex) + { + std::ostringstream name; + name << port.m_alias << ":" << queueIndex; + + const auto id = sai_serialize_object_id(port.m_queue_ids[queueIndex]); + + queueVector.emplace_back(name.str(), id); + queuePortVector.emplace_back(id, sai_serialize_object_id(port.m_port_id)); + queueIndexVector.emplace_back(id, to_string(queueIndex)); + + string queueType; + if (getQueueType(port.m_queue_ids[queueIndex], queueType)) + { + queueTypeVector.emplace_back(id, queueType); + } + + string key = getQueueFlexCounterTableKey(id); + + std::string delimiter = ""; + std::ostringstream counters_stream; + for (const auto& it: queueStatIds) + { + counters_stream << delimiter << sai_serialize_queue_stat(it); + delimiter = ","; + } + + vector fieldValues; + fieldValues.emplace_back(QUEUE_COUNTER_ID_LIST, counters_stream.str()); + + m_flexCounterTable->set(key, fieldValues); + } + + m_queueTable->set("", queueVector); + m_queuePortTable->set("", queuePortVector); + m_queueIndexTable->set("", queueIndexVector); + m_queueTypeTable->set("", queueTypeVector); + + CounterCheckOrch::getInstance().addPort(port); +} + void PortsOrch::doTask(NotificationConsumer &consumer) { SWSS_LOG_ENTER(); diff --git a/orchagent/portsorch.h b/orchagent/portsorch.h index 6abb31f626..fc1d2ae52b 100644 --- a/orchagent/portsorch.h +++ b/orchagent/portsorch.h @@ -18,6 +18,7 @@ typedef std::vector PortSupportedSpeeds; + static const map oper_status_strings = { { SAI_PORT_OPER_STATUS_UNKNOWN, "unknown" }, @@ -27,6 +28,12 @@ static const map oper_status_strings = { SAI_PORT_OPER_STATUS_NOT_PRESENT, "not present" } }; +struct PortUpdate +{ + Port port; + bool add; +}; + struct LagMemberUpdate { Port lag; @@ -49,6 +56,8 @@ class PortsOrch : public Orch, public Subject bool isInitDone(); map& getAllPorts(); + bool bake() override; + void cleanPortTable(const vector& keys); bool getBridgePort(sai_object_id_t id, Port &port); bool getPort(string alias, Port &port); bool getPort(sai_object_id_t id, Port &port); @@ -56,10 +65,13 @@ class PortsOrch : public Orch, public Subject void setPort(string alias, Port port); void getCpuPort(Port &port); bool getVlanByVlanId(sai_vlan_id_t vlan_id, Port &vlan); + bool getAclBindPortId(string alias, sai_object_id_t &port_id); bool setHostIntfsOperStatus(sai_object_id_t id, bool up); void updateDbPortOperStatus(sai_object_id_t id, sai_port_oper_status_t status); bool bindAclTable(sai_object_id_t id, sai_object_id_t table_oid, sai_object_id_t &group_member_oid, acl_stage_type_t acl_stage = ACL_STAGE_INGRESS); + + void generateQueueMap(); private: unique_ptr m_counterTable; unique_ptr
m_portTable; @@ -87,7 +99,7 @@ class PortsOrch : public Orch, public Subject bool m_portConfigDone = false; sai_uint32_t m_portCount; map, sai_object_id_t> m_portListLaneMap; - map, tuple> m_lanesAliasSpeedMap; + map, tuple> m_lanesAliasSpeedMap; map m_portList; NotificationConsumer* m_portStatusNotificationConsumer; @@ -125,7 +137,7 @@ class PortsOrch : public Orch, public Subject bool removeLagMember(Port &lag, Port &port); void getLagMember(Port &lag, vector &portv); - bool addPort(const set &lane_set, uint32_t speed); + bool addPort(const set &lane_set, uint32_t speed, int an=0, string fec=""); bool removePort(sai_object_id_t port_id); bool initPort(const string &alias, const set &lane_set); @@ -141,7 +153,15 @@ class PortsOrch : public Orch, public Subject bool setPortSpeed(sai_object_id_t port_id, sai_uint32_t speed); bool getPortSpeed(sai_object_id_t port_id, sai_uint32_t &speed); + bool setPortAdvSpeed(sai_object_id_t port_id, sai_uint32_t speed); + bool getQueueType(sai_object_id_t queue_id, string &type); + + bool m_isQueueMapGenerated = false; + void generateQueueMapPerPort(const Port& port); + + bool setPortAutoNeg(sai_object_id_t id, int an); + bool setPortFecMode(sai_object_id_t id, int fec); }; #endif /* SWSS_PORTSORCH_H */ diff --git a/orchagent/qosorch.cpp b/orchagent/qosorch.cpp index 23f0e9f0c9..e4c5c96d16 100644 --- a/orchagent/qosorch.cpp +++ b/orchagent/qosorch.cpp @@ -34,6 +34,12 @@ map ecn_map = { {"ecn_all", SAI_ECN_MARK_MODE_ALL} }; +enum { + GREEN_DROP_PROBABILITY_SET = (1U << 0), + YELLOW_DROP_PROBABILITY_SET = (1U << 1), + RED_DROP_PROBABILITY_SET = (1U << 2) +}; + map qos_to_attr_map = { {dscp_to_tc_field_name, SAI_PORT_ATTR_QOS_DSCP_TO_TC_MAP}, {tc_to_queue_field_name, SAI_PORT_ATTR_QOS_TC_TO_QUEUE_MAP}, @@ -329,6 +335,24 @@ bool WredMapHandler::convertFieldValuesToAttributes(KeyOpFieldsValuesTuple &tupl attr.value.s32 = stoi(fvValue(*i)); attribs.push_back(attr); } + else if (fvField(*i) == green_drop_probability_field_name) + { + attr.id = SAI_WRED_ATTR_GREEN_DROP_PROBABILITY; + attr.value.s32 = stoi(fvValue(*i)); + attribs.push_back(attr); + } + else if (fvField(*i) == yellow_drop_probability_field_name) + { + attr.id = SAI_WRED_ATTR_YELLOW_DROP_PROBABILITY; + attr.value.s32 = stoi(fvValue(*i)); + attribs.push_back(attr); + } + else if (fvField(*i) == red_drop_probability_field_name) + { + attr.id = SAI_WRED_ATTR_RED_DROP_PROBABILITY; + attr.value.s32 = stoi(fvValue(*i)); + attribs.push_back(attr); + } else if (fvField(*i) == wred_green_enable_field_name) { attr.id = SAI_WRED_ATTR_GREEN_ENABLE; @@ -394,14 +418,7 @@ sai_object_id_t WredMapHandler::addQosItem(const vector &attrib sai_object_id_t sai_object; sai_attribute_t attr; vector attrs; - - attr.id = SAI_WRED_ATTR_GREEN_DROP_PROBABILITY; - attr.value.s32 = 100; - attrs.push_back(attr); - - attr.id = SAI_WRED_ATTR_YELLOW_DROP_PROBABILITY; - attr.value.s32 = 100; - attrs.push_back(attr); + uint8_t drop_prob_set = 0; attr.id = SAI_WRED_ATTR_WEIGHT; attr.value.s32 = 0; @@ -410,7 +427,39 @@ sai_object_id_t WredMapHandler::addQosItem(const vector &attrib for(auto attrib : attribs) { attrs.push_back(attrib); + + if (attrib.id == SAI_WRED_ATTR_GREEN_DROP_PROBABILITY) + { + drop_prob_set |= GREEN_DROP_PROBABILITY_SET; + } + else if (attrib.id == SAI_WRED_ATTR_YELLOW_DROP_PROBABILITY) + { + drop_prob_set |= YELLOW_DROP_PROBABILITY_SET; + } + else if (attrib.id == SAI_WRED_ATTR_RED_DROP_PROBABILITY) + { + drop_prob_set |= RED_DROP_PROBABILITY_SET; + } + } + if (!(drop_prob_set & GREEN_DROP_PROBABILITY_SET)) + { + attr.id = SAI_WRED_ATTR_GREEN_DROP_PROBABILITY; + attr.value.s32 = 100; + attrs.push_back(attr); + } + if (!(drop_prob_set & YELLOW_DROP_PROBABILITY_SET)) + { + attr.id = SAI_WRED_ATTR_YELLOW_DROP_PROBABILITY; + attr.value.s32 = 100; + attrs.push_back(attr); + } + if (!(drop_prob_set & RED_DROP_PROBABILITY_SET)) + { + attr.id = SAI_WRED_ATTR_RED_DROP_PROBABILITY; + attr.value.s32 = 100; + attrs.push_back(attr); } + sai_status = sai_wred_api->create_wred(&sai_object, gSwitchId, (uint32_t)attrs.size(), attrs.data()); if (sai_status != SAI_STATUS_SUCCESS) { @@ -1133,13 +1182,28 @@ task_process_status QosOrch::handleQueueTable(Consumer& consumer) } else if (resolve_result != ref_resolve_status::field_not_found) { - if(ref_resolve_status::not_resolved == resolve_result) + if (ref_resolve_status::empty == resolve_result) + { + SWSS_LOG_INFO("Missing wred reference. Unbind wred profile from queue"); + // NOTE: The wred profile is un-bound from the port. But the wred profile itself still exists + // and stays untouched. + result = applyWredProfileToQueue(port, queue_ind, SAI_NULL_OBJECT_ID); + if (!result) + { + SWSS_LOG_ERROR("Failed unbinding field:%s from port:%s, queue:%zd, line:%d", wred_profile_field_name.c_str(), port.m_alias.c_str(), queue_ind, __LINE__); + return task_process_status::task_failed; + } + } + else if (ref_resolve_status::not_resolved == resolve_result) { - SWSS_LOG_INFO("Missing or invalid wred reference"); + SWSS_LOG_INFO("Invalid wred reference"); return task_process_status::task_need_retry; } - SWSS_LOG_ERROR("Resolving wred reference failed"); - return task_process_status::task_failed; + else + { + SWSS_LOG_ERROR("Resolving wred reference failed"); + return task_process_status::task_failed; + } } } } diff --git a/orchagent/qosorch.h b/orchagent/qosorch.h index 03b70a7dbc..0d9d22cd35 100644 --- a/orchagent/qosorch.h +++ b/orchagent/qosorch.h @@ -7,41 +7,44 @@ #include "orch.h" #include "portsorch.h" -const string dscp_to_tc_field_name = "dscp_to_tc_map"; -const string pfc_to_pg_map_name = "pfc_to_pg_map"; -const string pfc_to_queue_map_name = "pfc_to_queue_map"; -const string pfc_enable_name = "pfc_enable"; -const string tc_to_pg_map_field_name = "tc_to_pg_map"; -const string tc_to_queue_field_name = "tc_to_queue_map"; -const string scheduler_field_name = "scheduler"; -const string red_max_threshold_field_name = "red_max_threshold"; -const string red_min_threshold_field_name = "red_min_threshold"; -const string yellow_max_threshold_field_name = "yellow_max_threshold"; -const string yellow_min_threshold_field_name = "yellow_min_threshold"; -const string green_max_threshold_field_name = "green_max_threshold"; -const string green_min_threshold_field_name = "green_min_threshold"; - -const string wred_profile_field_name = "wred_profile"; -const string wred_red_enable_field_name = "wred_red_enable"; -const string wred_yellow_enable_field_name = "wred_yellow_enable"; -const string wred_green_enable_field_name = "wred_green_enable"; - -const string scheduler_algo_type_field_name = "type"; -const string scheduler_algo_DWRR = "DWRR"; -const string scheduler_algo_WRR = "WRR"; -const string scheduler_algo_STRICT = "STRICT"; -const string scheduler_weight_field_name = "weight"; -const string scheduler_priority_field_name = "priority"; - -const string ecn_field_name = "ecn"; -const string ecn_none = "ecn_none"; -const string ecn_red = "ecn_red"; -const string ecn_yellow = "ecn_yellow"; -const string ecn_yellow_red = "ecn_yellow_red"; -const string ecn_green = "ecn_green"; -const string ecn_green_red = "ecn_green_red"; -const string ecn_green_yellow = "ecn_green_yellow"; -const string ecn_all = "ecn_all"; +const string dscp_to_tc_field_name = "dscp_to_tc_map"; +const string pfc_to_pg_map_name = "pfc_to_pg_map"; +const string pfc_to_queue_map_name = "pfc_to_queue_map"; +const string pfc_enable_name = "pfc_enable"; +const string tc_to_pg_map_field_name = "tc_to_pg_map"; +const string tc_to_queue_field_name = "tc_to_queue_map"; +const string scheduler_field_name = "scheduler"; +const string red_max_threshold_field_name = "red_max_threshold"; +const string red_min_threshold_field_name = "red_min_threshold"; +const string yellow_max_threshold_field_name = "yellow_max_threshold"; +const string yellow_min_threshold_field_name = "yellow_min_threshold"; +const string green_max_threshold_field_name = "green_max_threshold"; +const string green_min_threshold_field_name = "green_min_threshold"; +const string red_drop_probability_field_name = "red_drop_probability"; +const string yellow_drop_probability_field_name = "yellow_drop_probability"; +const string green_drop_probability_field_name = "green_drop_probability"; + +const string wred_profile_field_name = "wred_profile"; +const string wred_red_enable_field_name = "wred_red_enable"; +const string wred_yellow_enable_field_name = "wred_yellow_enable"; +const string wred_green_enable_field_name = "wred_green_enable"; + +const string scheduler_algo_type_field_name = "type"; +const string scheduler_algo_DWRR = "DWRR"; +const string scheduler_algo_WRR = "WRR"; +const string scheduler_algo_STRICT = "STRICT"; +const string scheduler_weight_field_name = "weight"; +const string scheduler_priority_field_name = "priority"; + +const string ecn_field_name = "ecn"; +const string ecn_none = "ecn_none"; +const string ecn_red = "ecn_red"; +const string ecn_yellow = "ecn_yellow"; +const string ecn_yellow_red = "ecn_yellow_red"; +const string ecn_green = "ecn_green"; +const string ecn_green_red = "ecn_green_red"; +const string ecn_green_yellow = "ecn_green_yellow"; +const string ecn_all = "ecn_all"; class QosMapHandler { diff --git a/orchagent/saihelper.cpp b/orchagent/saihelper.cpp index 58e9eb48c4..7381e74430 100644 --- a/orchagent/saihelper.cpp +++ b/orchagent/saihelper.cpp @@ -1,4 +1,5 @@ extern "C" { + #include "sai.h" #include "saistatus.h" } @@ -37,6 +38,7 @@ sai_buffer_api_t* sai_buffer_api; sai_acl_api_t* sai_acl_api; sai_mirror_api_t* sai_mirror_api; sai_fdb_api_t* sai_fdb_api; +sai_dtel_api_t* sai_dtel_api; extern sai_object_id_t gSwitchId; extern bool gSairedisRecord; @@ -124,6 +126,7 @@ void initSaiApi() sai_api_query(SAI_API_BUFFER, (void **)&sai_buffer_api); sai_api_query(SAI_API_SCHEDULER_GROUP, (void **)&sai_scheduler_group_api); sai_api_query(SAI_API_ACL, (void **)&sai_acl_api); + sai_api_query(SAI_API_DTEL, (void **)&sai_dtel_api); sai_log_set(SAI_API_SWITCH, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_BRIDGE, SAI_LOG_LEVEL_NOTICE); @@ -148,6 +151,7 @@ void initSaiApi() sai_log_set(SAI_API_BUFFER, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_SCHEDULER_GROUP, SAI_LOG_LEVEL_NOTICE); sai_log_set(SAI_API_ACL, SAI_LOG_LEVEL_NOTICE); + sai_log_set(SAI_API_DTEL, SAI_LOG_LEVEL_NOTICE); } void initSaiRedis(const string &record_location) diff --git a/orchagent/timer.h b/orchagent/timer.h index e30a973d2f..732f8f4ebf 100644 --- a/orchagent/timer.h +++ b/orchagent/timer.h @@ -8,8 +8,8 @@ namespace swss { class ExecutableTimer : public Executor { public: - ExecutableTimer(SelectableTimer *timer, Orch *orch) - : Executor(timer, orch) + ExecutableTimer(SelectableTimer *timer, Orch *orch, const string &name) + : Executor(timer, orch, name) { } diff --git a/orchagent/tunneldecaporch.cpp b/orchagent/tunneldecaporch.cpp index 23bcf356ea..78470775aa 100644 --- a/orchagent/tunneldecaporch.cpp +++ b/orchagent/tunneldecaporch.cpp @@ -36,6 +36,7 @@ void TunnelDecapOrch::doTask(Consumer& consumer) IpAddresses ip_addresses; IpAddress src_ip; + IpAddress* p_src_ip = nullptr; string tunnel_type; string dscp_mode; string ecn_mode; @@ -82,6 +83,7 @@ void TunnelDecapOrch::doTask(Consumer& consumer) try { src_ip = IpAddress(fvValue(i)); + p_src_ip = &src_ip; } catch (const std::invalid_argument &e) { @@ -141,7 +143,7 @@ void TunnelDecapOrch::doTask(Consumer& consumer) // create new tunnel if it doesn't exists already if (valid && !exists) { - if (addDecapTunnel(key, tunnel_type, ip_addresses, src_ip, dscp_mode, ecn_mode, ttl_mode)) + if (addDecapTunnel(key, tunnel_type, ip_addresses, p_src_ip, dscp_mode, ecn_mode, ttl_mode)) { SWSS_LOG_NOTICE("Tunnel(s) added to ASIC_DB."); } @@ -175,7 +177,7 @@ void TunnelDecapOrch::doTask(Consumer& consumer) * Arguments: * @param[in] type - type of tunnel * @param[in] dst_ip - destination ip address to decap - * @param[in] src_ip - source ip address to decap + * @param[in] p_src_ip - source ip address for encap (nullptr to skip this) * @param[in] dscp - dscp mode (uniform/pipe) * @param[in] ecn - ecn mode (copy_from_outer/standard) * @param[in] ttl - ttl mode (uniform/pipe) @@ -183,7 +185,7 @@ void TunnelDecapOrch::doTask(Consumer& consumer) * Return Values: * @return true on success and false if there's an error */ -bool TunnelDecapOrch::addDecapTunnel(string key, string type, IpAddresses dst_ip, IpAddress src_ip, string dscp, string ecn, string ttl) +bool TunnelDecapOrch::addDecapTunnel(string key, string type, IpAddresses dst_ip, IpAddress* p_src_ip, string dscp, string ecn, string ttl) { SWSS_LOG_ENTER(); @@ -228,9 +230,12 @@ bool TunnelDecapOrch::addDecapTunnel(string key, string type, IpAddresses dst_ip tunnel_attrs.push_back(attr); // tunnel src ip - attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; - copy(attr.value.ipaddr, src_ip.to_string()); - tunnel_attrs.push_back(attr); + if (p_src_ip != nullptr) + { + attr.id = SAI_TUNNEL_ATTR_ENCAP_SRC_IP; + copy(attr.value.ipaddr, p_src_ip->to_string()); + tunnel_attrs.push_back(attr); + } // decap ecn mode (copy from outer/standard) attr.id = SAI_TUNNEL_ATTR_DECAP_ECN_MODE; @@ -277,7 +282,7 @@ bool TunnelDecapOrch::addDecapTunnel(string key, string type, IpAddresses dst_ip return false; } - tunnelTable[key] = { tunnel_id, {} }; + tunnelTable[key] = { tunnel_id, overlayIfId, {} }; // TODO: // there should also be "business logic" for netbouncer in the "tunnel application" code, which is a different source file and daemon process @@ -523,6 +528,15 @@ bool TunnelDecapOrch::removeDecapTunnel(string key) SWSS_LOG_ERROR("Failed to remove tunnel: %lu", tunnel_info->tunnel_id); return false; } + + // delete overlay loopback interface + status = sai_router_intfs_api->remove_router_interface(tunnel_info->overlay_intf_id); + if (status != SAI_STATUS_SUCCESS) + { + SWSS_LOG_ERROR("Failed to remove tunnel overlay interface: %lu", tunnel_info->overlay_intf_id); + return false; + } + tunnelTable.erase(key); return true; } diff --git a/orchagent/tunneldecaporch.h b/orchagent/tunneldecaporch.h index 892cebafff..dbd3126df9 100644 --- a/orchagent/tunneldecaporch.h +++ b/orchagent/tunneldecaporch.h @@ -18,6 +18,7 @@ struct TunnelTermEntry struct TunnelEntry { sai_object_id_t tunnel_id; // tunnel id + sai_object_id_t overlay_intf_id; // overlay interface id vector tunnel_term_info; // tunnel_entry ids related to the tunnel abd ips related to the tunnel (all ips for tunnel entries that refer to this tunnel) }; @@ -36,7 +37,7 @@ class TunnelDecapOrch : public Orch TunnelTable tunnelTable; ExistingIps existingIps; - bool addDecapTunnel(string key, string type, IpAddresses dst_ip, IpAddress src_ip, string dscp, string ecn, string ttl); + bool addDecapTunnel(string key, string type, IpAddresses dst_ip, IpAddress* p_src_ip, string dscp, string ecn, string ttl); bool removeDecapTunnel(string key); bool addDecapTunnelTermEntries(string tunnelKey, IpAddresses dst_ip, sai_object_id_t tunnel_id); diff --git a/portsyncd/linksync.cpp b/portsyncd/linksync.cpp index fa64b68711..58ba56dc3c 100644 --- a/portsyncd/linksync.cpp +++ b/portsyncd/linksync.cpp @@ -110,7 +110,6 @@ void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj) unsigned int flags = rtnl_link_get_flags(link); bool admin = flags & IFF_UP; bool oper = flags & IFF_LOWER_UP; - unsigned int mtu = rtnl_link_get_mtu(link); char addrStr[MAX_ADDR_SIZE+1] = {0}; nl_addr2str(rtnl_link_get_addr(link), addrStr, MAX_ADDR_SIZE); @@ -155,9 +154,7 @@ void LinkSync::onMsg(int nlmsg_type, struct nl_object *obj) vector fvVector; FieldValueTuple a("admin_status", admin ? "up" : "down"); - FieldValueTuple m("mtu", to_string(mtu)); fvVector.push_back(a); - fvVector.push_back(m); /* front panel interfaces: Check if the port is in the PORT_TABLE * non-front panel interfaces such as eth0, lo which are not in the diff --git a/portsyncd/portsyncd.cpp b/portsyncd/portsyncd.cpp index f76e0dfc47..3d5d99b40a 100644 --- a/portsyncd/portsyncd.cpp +++ b/portsyncd/portsyncd.cpp @@ -199,7 +199,7 @@ void handlePortConfigFile(ProducerStateTable &p, string file) throw "Port configuration file not found!"; } - list header = {"name", "lanes", "alias", "speed"}; + list header = {"name", "lanes", "alias", "speed", "autoneg", "fec"}; string line; while (getline(infile, line)) { @@ -252,6 +252,18 @@ void handlePortConfigFile(ProducerStateTable &p, string file) attrs.push_back(speed_attr); } + if ((entry.find("autoneg") != entry.end()) && (entry["autoneg"] != "")) + { + FieldValueTuple autoneg_attr("autoneg", entry["autoneg"]); + attrs.push_back(autoneg_attr); + } + + if ((entry.find("fec") != entry.end()) && (entry["fec"] != "")) + { + FieldValueTuple fec_attr("fec", entry["fec"]); + attrs.push_back(fec_attr); + } + p.set(entry["name"], attrs); g_portSet.insert(entry["name"]); @@ -279,19 +291,6 @@ void handlePortConfig(ProducerStateTable &p, map if (op == SET_COMMAND) { p.set(key, values); - for (auto fv : values) - { - string field = fvField(fv); - string value = fvValue(fv); - - /* Update the mtu field on host interface */ - if (field == "mtu") - { - string cmd, res; - cmd = "ip link set " + key + " mtu " + value; - swss::exec(cmd, res); - } - } } it = port_cfg_map.erase(it); diff --git a/tests/README.md b/tests/README.md index 7c5ef03a68..e4ebaa40ea 100644 --- a/tests/README.md +++ b/tests/README.md @@ -8,7 +8,7 @@ SWSS Integration tests runs on docker-sonic-vs which runs on top of SAI virtual - Install docker and pytest on your dev machine ``` - sudo pip install --system docker==2.6.1 + sudo pip install --system docker==3.5.0 sudo pip install --system pytest==3.3.0 ``` - 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. @@ -35,6 +35,8 @@ SWSS Integration tests runs on docker-sonic-vs which runs on top of SAI virtual sudo pytest -v ``` +\* If you meet the error: client is newer than server, please edit the file `/usr/local/lib/python2.7/dist-packages/docker/constants.py` to update the `DEFAULT_DOCKER_API_VERSION` to mitigate this issue. + # How to setup test development env To develop new swss features or swss integration tests, you need to setup a virtual switch docker container which @@ -43,7 +45,7 @@ persists. - Create virtual switch container (name:vs). ```create_vnet.sh``` can be found at [here](https://github.com/Azure/sonic-buildimage/blob/master/platform/vs/create_vnet.sh). ``` - docker run -id --name sw debian bash + docker run --privileged -id --name sw debian bash sudo ./create_vnet.sh sw docker run --privileged -v /var/run/redis-vs:/var/run/redis --network container:sw -d --name vs docker-sonic-vs ``` diff --git a/tests/conftest.py b/tests/conftest.py index d7388125bc..8acaed5e90 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,13 @@ import os +import os.path import re import time import docker import pytest import commands +import tarfile +import StringIO +import subprocess from swsscommon import swsscommon def pytest_addoption(parser): @@ -50,8 +54,8 @@ def __init__(self, dvs): atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() - assert len(keys) == 1 - self.default_acl_table = keys[0] + assert len(keys) >= 1 + self.default_acl_tables = keys atbl = swsscommon.Table(self.adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") keys = atbl.getKeys() @@ -79,17 +83,26 @@ def __init__(self, ctn_name, pid, i): # bring up link in the virtual server os.system("ip netns exec %s ip link set dev %s name eth0" % (self.nsname, self.nsname[0:12])) os.system("ip netns exec %s ip link set dev eth0 up" % (self.nsname)) + os.system("ip netns exec %s ethtool -K eth0 tx off" % (self.nsname)) # bring up link in the virtual switch os.system("nsenter -t %d -n ip link set dev %s up" % (pid, self.vifname)) def __del__(self): if self.cleanup: + pids = subprocess.check_output("ip netns pids %s" % (self.nsname), shell=True) + if pids: + for pid in pids.split('\n'): + if len(pid) > 0: + os.system("kill %s" % int(pid)) os.system("ip netns delete %s" % self.nsname) def runcmd(self, cmd): os.system("ip netns exec %s %s" % (self.nsname, cmd)) + def runcmd_async(self, cmd): + return subprocess.Popen("ip netns exec %s %s" % (self.nsname, cmd), shell=True) + class DockerVirtualSwitch(object): def __init__(self, name=None): self.pnames = ['fpmsyncd', @@ -153,8 +166,13 @@ def __init__(self, name=None): network_mode="container:%s" % self.ctn_sw.name, volumes={ self.mount: { 'bind': '/var/run/redis', 'mode': 'rw' } }) - self.check_ready() - self.init_asicdb_validator() + try: + self.ctn.exec_run("sysctl -w net.ipv6.conf.all.disable_ipv6=0") + self.check_ready() + self.init_asicdb_validator() + except: + self.destroy() + raise def destroy(self): if self.cleanup: @@ -172,7 +190,11 @@ def check_ready(self, timeout=30): started = 0 while True: # get process status - out = self.ctn.exec_run("supervisorctl status") + res = self.ctn.exec_run("supervisorctl status") + try: + out = res.output + except AttributeError: + out = res for l in out.split('\n'): fds = re_space.split(l) if len(fds) < 2: @@ -204,7 +226,23 @@ def init_asicdb_validator(self): self.asicdb = AsicDbValidator(self) def runcmd(self, cmd): - return self.ctn.exec_run(cmd) + res = self.ctn.exec_run(cmd) + try: + exitcode = res.exit_code + out = res.output + except AttributeError: + exitcode = 0 + out = res + return (exitcode, out) + + def copy_file(self, path, filename): + tarstr = StringIO.StringIO() + tar = tarfile.open(fileobj=tarstr, mode="w") + tar.add(filename, os.path.basename(filename)) + tar.close() + self.ctn.exec_run("mkdir -p %s" % path) + self.ctn.put_archive(path, tarstr.getvalue()) + tarstr.close() @pytest.yield_fixture(scope="module") def dvs(request): diff --git a/tests/test_acl.py b/tests/test_acl.py index cf7a0ce114..efb4d06373 100644 --- a/tests/test_acl.py +++ b/tests/test_acl.py @@ -7,75 +7,101 @@ class TestAcl(object): def get_acl_table_id(self, dvs, adb): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() - assert dvs.asicdb.default_acl_table in keys - acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_table] - - assert len(acl_tables) == 1 + for k in dvs.asicdb.default_acl_tables: + assert k in keys + acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_tables] + + assert len(acl_tables) == 1 return acl_tables[0] - - def test_AclTableCreation(self, dvs): - - db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + def verify_if_any_acl_table_created(self, dvs, adb): + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + keys = atbl.getKeys() + for k in dvs.asicdb.default_acl_tables: + assert k in keys + acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_tables] + + if len(acl_tables) != 0: + return True + + return False + + def clean_up_left_over(self, dvs): adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - - bind_ports = ["Ethernet0", "Ethernet4"] - # create ACL_TABLE in config db - tbl = swsscommon.Table(db, "ACL_TABLE") - fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) - tbl.set("test", 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") + keys = atbl.getKeys() + for key in keys: + atbl._del(key) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + keys = atbl.getKeys() + assert len(keys) == 0 + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER") + keys = atbl.getKeys() + for key in keys: + atbl._del(key) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER") + keys = atbl.getKeys() + assert len(keys) == 0 + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + keys = atbl.getKeys() + for key in keys: + atbl._del(key) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + keys = atbl.getKeys() + assert len(keys) == 0 + + def verify_acl_group_num(self, adb, expt): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") acl_table_groups = atbl.getKeys() - assert len(acl_table_groups) == 2 - + assert len(acl_table_groups) == expt + 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" + assert (fv[1] == "1:SAI_ACL_BIND_POINT_TYPE_PORT" or fv[1] == "1:SAI_ACL_BIND_POINT_TYPE_LAG") 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 + + def verify_acl_group_member(self, adb, acl_group_ids, acl_table_id): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP_MEMBER") keys = atbl.getKeys() - assert len(keys) == 2 - + 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 + assert fv[1] in acl_group_ids member_groups.append(fv[1]) elif fv[0] == "SAI_ACL_TABLE_GROUP_MEMBER_ATTR_ACL_TABLE_ID": - assert fv[1] == test_acl_table_id + assert fv[1] == 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 + + assert set(member_groups) == set(acl_group_ids) + + def verify_acl_port_binding(self, dvs, adb, bind_ports): + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_table_groups = atbl.getKeys() + assert len(acl_table_groups) == len(bind_ports) + 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) @@ -83,33 +109,80 @@ def test_AclTableCreation(self, dvs): if fv[0] == "SAI_PORT_ATTR_INGRESS_ACL": assert fv[1] in acl_table_groups port_groups.append(fv[1]) - + + assert len(port_groups) == len(bind_ports) + assert set(port_groups) == set(acl_table_groups) + + def verify_acl_lag_binding(self, adb, lag_ids): + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_table_groups = atbl.getKeys() + assert len(acl_table_groups) == len(lag_ids) + + atbl_lag = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + port_groups = [] + for lag_id in lag_ids: + (status, lagfvs) = atbl_lag.get(lag_id) + for lagfv in lagfvs: + if lagfv[0] == "SAI_LAG_ATTR_INGRESS_ACL": + assert lagfv[1] in acl_table_groups + port_groups.append(lagfv[1]) + + assert len(port_groups) == len(lag_ids) assert set(port_groups) == set(acl_table_groups) - + + def test_AclTableCreation(self, dvs): + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create ACL_TABLE in config db + bind_ports = ["Ethernet0", "Ethernet4"] + tbl = swsscommon.Table(db, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + tbl.set("test", fvs) + time.sleep(1) + + # check acl table in asic db + test_acl_table_id = self.get_acl_table_id(dvs, adb) + assert test_acl_table_id + + # check acl table group in asic db + self.verify_acl_group_num(adb, 2) + + # get acl table group ids and verify the id numbers + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_group_ids = atbl.getKeys() + assert len(acl_group_ids) == 2 + + # check acl table group member + self.verify_acl_group_member(adb, acl_group_ids, test_acl_table_id) + + # check port binding + self.verify_acl_port_binding(dvs, adb, bind_ports) + def test_AclRuleL4SrcPort(self, dvs): """ hmset ACL_RULE|test|acl_test_rule priority 55 PACKET_ACTION FORWARD L4_SRC_PORT 65000 """ - + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) - - # create acl rule + + # create 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) - + 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 @@ -128,20 +201,20 @@ def test_AclRuleL4SrcPort(self, dvs): assert fv[1] == "SAI_PACKET_ACTION_FORWARD" else: assert False - + # remove acl rule tbl._del("test|acl_test_rule") - + time.sleep(1) - + (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") @@ -150,34 +223,34 @@ def test_AclTableDeletion(self, dvs): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() - # only the default table was left - assert len(keys) == 1 - + # only the default table was left along with DTel tables + 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" @@ -187,18 +260,18 @@ def test_V6AclTableCreation(self, dvs): 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": @@ -210,12 +283,12 @@ def test_V6AclTableCreation(self, dvs): assert True else: assert False - + assert set(member_groups) == set(acl_table_groups) - - # check port binding + + # 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) @@ -223,33 +296,33 @@ def test_V6AclTableCreation(self, dvs): 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 + + # 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 @@ -268,12 +341,12 @@ def test_V6AclRuleIPv6Any(self, dvs): 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 @@ -281,26 +354,26 @@ 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 + + # 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 @@ -319,12 +392,12 @@ def test_V6AclRuleIPv6AnyDrop(self, dvs): 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 @@ -332,26 +405,26 @@ 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 + + # 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 @@ -370,12 +443,12 @@ def test_V6AclRuleIpProtocol(self, dvs): 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 @@ -383,26 +456,26 @@ 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 + + # 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 @@ -421,12 +494,12 @@ def test_V6AclRuleSrcIPv6(self, dvs): 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 @@ -434,26 +507,26 @@ 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 + + # 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 @@ -472,12 +545,12 @@ def test_V6AclRuleDstIPv6(self, dvs): 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 @@ -485,26 +558,26 @@ 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 + + # 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 @@ -523,12 +596,12 @@ def test_V6AclRuleL4SrcPort(self, dvs): 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 @@ -536,26 +609,26 @@ 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 + + # 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 @@ -574,12 +647,12 @@ def test_V6AclRuleL4DstPort(self, dvs): 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 @@ -587,26 +660,26 @@ 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 + + # 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 @@ -625,12 +698,12 @@ def test_V6AclRuleTCPFlags(self, dvs): 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 @@ -638,23 +711,23 @@ 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 + + # 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 @@ -679,7 +752,7 @@ def test_V6AclRuleL4SrcPortRange(self, dvs): 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 @@ -693,9 +766,9 @@ def test_V6AclRuleL4SrcPortRange(self, dvs): # remove acl rule tbl._del("test-aclv6|test_rule9") - + time.sleep(1) - + (status, fvs) = atbl.get(acl_entry[0]) assert status == False @@ -703,26 +776,26 @@ 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 + + # 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 @@ -744,7 +817,7 @@ def test_V6AclRuleL4DstPortRange(self, dvs): 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 @@ -758,17 +831,17 @@ def test_V6AclRuleL4DstPortRange(self, dvs): # 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") @@ -778,7 +851,7 @@ def test_V6AclTableDeletion(self, dvs): atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") keys = atbl.getKeys() # only the default table was left - assert len(keys) == 1 + assert len(keys) >= 1 #helper function to verify if rule exists def check_rule_existence(self, entry, rules, verifs): @@ -810,13 +883,13 @@ def test_InsertAclRuleBetweenPriorities(self, dvs): num_rules = 0 #create ACL rules tbl = swsscommon.Table(db, "ACL_RULE") - rules = [ [("PRIORITY", "10"), ("PACKET_ACTION", "DROP"), ("SRC_IP", "10.0.0.0/32")], + rules = [ [("PRIORITY", "10"), ("PACKET_ACTION", "DROP"), ("SRC_IP", "10.0.0.0/32")], [("PRIORITY", "20"), ("PACKET_ACTION", "DROP"), ("DST_IP", "104.44.94.0/23")], [("PRIORITY", "30"), ("PACKET_ACTION", "DROP"), ("DST_IP", "192.168.0.16/32")], [("PRIORITY", "40"), ("PACKET_ACTION", "FORWARD"), ("DST_IP", "100.64.0.0/10")] ] #used to verify how ACL rules are programmed in ASICDB - verifs = [ {'SAI_ACL_ENTRY_ATTR_PRIORITY': '10', - 'SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP': '10.0.0.0&mask:255.255.255.255', + verifs = [ {'SAI_ACL_ENTRY_ATTR_PRIORITY': '10', + 'SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP': '10.0.0.0&mask:255.255.255.255', 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_DROP'}, {'SAI_ACL_ENTRY_ATTR_PRIORITY': '20', 'SAI_ACL_ENTRY_ATTR_FIELD_DST_IP': '104.44.94.0&mask:255.255.254.0', @@ -840,22 +913,22 @@ def test_InsertAclRuleBetweenPriorities(self, dvs): #assert that first set of rules are programmed acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] - assert len(acl_entry) == num_rules - + assert len(acl_entry) == num_rules + #insert new rule with odd priority tbl = swsscommon.Table(db, "ACL_RULE") insertrule = [("PRIORITY", "21"), ("PACKET_ACTION", "DROP"), ("ETHER_TYPE", "4660")] #create verification for that rule verifs.append({'SAI_ACL_ENTRY_ATTR_PRIORITY': '21', - 'SAI_ACL_ENTRY_ATTR_FIELD_ETHER_TYPE': '4660&mask:0xffff', + 'SAI_ACL_ENTRY_ATTR_FIELD_ETHER_TYPE': '4660&mask:0xffff', 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_DROP'}) rules.append(insertrule) fvs = swsscommon.FieldValuePairs(insertrule) num_rules += 1 tbl.set("test_insert|acl_test_rule%s" % num_rules, fvs) - + time.sleep(1) - + #assert all rules are programmed keys = atbl.getKeys() acl_entry = [k for k in keys if k not in dvs.asicdb.default_acl_entries] @@ -882,13 +955,255 @@ def test_InsertAclRuleBetweenPriorities(self, dvs): (status, fvs) = atbl.get(acl_entry[0]) assert status == False - + tbl = swsscommon.Table(db, "ACL_TABLE") tbl._del("test_insert") - + 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 + assert len(keys) >= 1 + + def test_RulesWithDiffMaskLengths(self, dvs): + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + bind_ports = ["Ethernet0", "Ethernet4"] + # create ACL_TABLE in config db + tbl = swsscommon.Table(db, "ACL_TABLE") + fvs = swsscommon.FieldValuePairs([("policy_desc", "test"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + tbl.set("test_subnet", fvs) + + time.sleep(2) + + subnet_mask_rules = 0 + #create ACL rules + tbl = swsscommon.Table(db, "ACL_RULE") + rules = [ [("PRIORITY", "10"), ("PACKET_ACTION", "FORWARD"), ("SRC_IP", "23.103.0.0/18")], + [("PRIORITY", "20"), ("PACKET_ACTION", "FORWARD"), ("SRC_IP", "104.44.94.0/23")], + [("PRIORITY", "30"), ("PACKET_ACTION", "FORWARD"), ("DST_IP", "172.16.0.0/12")], + [("PRIORITY", "40"), ("PACKET_ACTION", "FORWARD"), ("DST_IP", "100.64.0.0/10")], + [("PRIORITY", "50"), ("PACKET_ACTION", "FORWARD"), ("DST_IP", "104.146.32.0/19")], + [("PRIORITY", "60"), ("PACKET_ACTION", "FORWARD"), ("SRC_IP", "21.0.0.0/8")] ] + #used to verify how ACL rules are programmed in ASICDB + #order must match the list of rules + verifs = [ {'SAI_ACL_ENTRY_ATTR_PRIORITY': '10', + 'SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP': '23.103.0.0&mask:255.255.192.0', + 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_FORWARD'}, + {'SAI_ACL_ENTRY_ATTR_PRIORITY': '20', + 'SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP': '104.44.94.0&mask:255.255.254.0', + 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_FORWARD'}, + {'SAI_ACL_ENTRY_ATTR_PRIORITY': '30', + 'SAI_ACL_ENTRY_ATTR_FIELD_DST_IP': '172.16.0.0&mask:255.240.0.0', + 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_FORWARD'}, + {'SAI_ACL_ENTRY_ATTR_PRIORITY': '40', + 'SAI_ACL_ENTRY_ATTR_FIELD_DST_IP': '100.64.0.0&mask:255.192.0.0', + 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_FORWARD'}, + {'SAI_ACL_ENTRY_ATTR_PRIORITY': '50', + 'SAI_ACL_ENTRY_ATTR_FIELD_DST_IP': '104.146.32.0&mask:255.255.224.0', + 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_FORWARD'}, + {'SAI_ACL_ENTRY_ATTR_PRIORITY': '60', + 'SAI_ACL_ENTRY_ATTR_FIELD_SRC_IP': '21.0.0.0&mask:255.0.0.0', + 'SAI_ACL_ENTRY_ATTR_ACTION_PACKET_ACTION': 'SAI_PACKET_ACTION_FORWARD'} ] + #insert rules + for rule in rules: + fvs = swsscommon.FieldValuePairs(rule) + subnet_mask_rules += 1 + tbl.set( "test_subnet|acl_test_rule%s" % subnet_mask_rules, fvs ) + + time.sleep(1) + + 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) == subnet_mask_rules + + #match each entry to its corresponding verification + matched_masks = 0 + for entry in acl_entry: + (status, fvs) = atbl.get(entry) + assert status == True + assert len(fvs) == 6 + #helper function + if self.check_rule_existence(dict(fvs), rules, verifs): + matched_masks += 1 + + assert matched_masks == subnet_mask_rules + + while subnet_mask_rules > 0: + tbl._del("test_subnet|acl_test_rule%s" % subnet_mask_rules) + subnet_mask_rules -= 1 + + time.sleep(1) + + (status, fvs) = atbl.get(acl_entry[0]) + assert status == False + + tbl = swsscommon.Table(db, "ACL_TABLE") + tbl._del("test_subnet") + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + keys = atbl.getKeys() + assert len(keys) >= 1 + + def test_AclTableCreationOnLAGMember(self, dvs): + # prepare db and tables + self.clean_up_left_over(dvs) + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + apldb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + + # create port channel + ps = swsscommon.ProducerStateTable(apldb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin", "up"), ("mtu", "1500")]) + ps.set("PortChannel0001", fvs) + + # create port channel member + ps = swsscommon.ProducerStateTable(apldb, "LAG_MEMBER_TABLE") + fvs = swsscommon.FieldValuePairs([("status", "enabled")]) + ps.set("PortChannel0001:Ethernet12", fvs) + time.sleep(1) + + # create acl table + tbl = swsscommon.Table(db, "ACL_TABLE") + bind_ports = ["Ethernet12"] + fvs = swsscommon.FieldValuePairs([("policy_desc", "test_negative"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + tbl.set("test_negative", fvs) + time.sleep(1) + + # verify test result - ACL table creation should fail + assert self.verify_if_any_acl_table_created(dvs, adb) == False + + def test_AclTableCreationOnLAG(self, dvs): + # prepare db and tables + self.clean_up_left_over(dvs) + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + apldb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + + #create port channel + ps = swsscommon.ProducerStateTable(apldb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin", "up"), ("mtu", "1500")]) + ps.set("PortChannel0002", fvs) + + # create port channel member + ps = swsscommon.ProducerStateTable(apldb, "LAG_MEMBER_TABLE") + fvs = swsscommon.FieldValuePairs([("status", "enabled")]) + ps.set("PortChannel0002:Ethernet16", fvs) + time.sleep(1) + + # create acl table + tbl = swsscommon.Table(db, "ACL_TABLE") + bind_ports = ["PortChannel0002"] + fvs = swsscommon.FieldValuePairs([("policy_desc", "test_negative"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + tbl.set("test_LAG", 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 + self.verify_acl_group_num(adb, 1) + + # get acl table group ids and verify the id numbers + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_group_ids = atbl.getKeys() + assert len(acl_group_ids) == 1 + + # check acl table group member + self.verify_acl_group_member(adb, acl_group_ids, test_acl_table_id) + + # get lad ids + atbl_lag = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + lag_ids = atbl_lag.getKeys(); + assert len(lag_ids) == 1 + + # check lag binding + self.verify_acl_lag_binding(adb, lag_ids) + + tbl = swsscommon.Table(db, "ACL_TABLE") + tbl._del("test_LAG") + + def test_AclTableCreationBeforeLAG(self, dvs): + # prepare db and tables + self.clean_up_left_over(dvs) + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + apldb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + + # create acl table + tbl = swsscommon.Table(db, "ACL_TABLE") + bind_ports = ["PortChannel0003"] + fvs = swsscommon.FieldValuePairs([("policy_desc", "test_negative"), ("type", "L3"), ("ports", ",".join(bind_ports))]) + tbl.set("test_LAG_2", 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 + self.verify_acl_group_num(adb, 0) + + # get acl table group ids and verify the id numbers + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_group_ids = atbl.getKeys() + assert len(acl_group_ids) == 0 + + # check acl table group member + self.verify_acl_group_member(adb, acl_group_ids, test_acl_table_id) + + # get lad ids + atbl_lag = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + lag_ids = atbl_lag.getKeys() + assert len(lag_ids) == 0 + + # check port binding + self.verify_acl_lag_binding(adb, lag_ids) + + # create port channel + ps = swsscommon.ProducerStateTable(apldb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("admin", "up"), ("mtu", "1500")]) + ps.set("PortChannel0003", fvs) + + # create port channel member + ps = swsscommon.ProducerStateTable(apldb, "LAG_MEMBER_TABLE") + fvs = swsscommon.FieldValuePairs([("status", "enabled")]) + ps.set("PortChannel0003:Ethernet20", fvs) + time.sleep(1) + + # notify aclorch that port channel configured + stdb = swsscommon.DBConnector(6, dvs.redis_sock, 0) + tbl = swsscommon.Table(stdb, "LAG_TABLE") + fvs = swsscommon.FieldValuePairs([("state", "ok")]) + tbl.set("PortChannel0003", 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 + self.verify_acl_group_num(adb, 1) + + # get acl table group ids and verify the id numbers + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_group_ids = atbl.getKeys() + assert len(acl_group_ids) == 1 + + # check acl table group member + self.verify_acl_group_member(adb, acl_group_ids, test_acl_table_id) + + # get lad ids + atbl_lag = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_LAG") + lag_ids = atbl_lag.getKeys() + assert len(lag_ids) == 1 + + # check port binding + self.verify_acl_lag_binding(adb, lag_ids) + + tbl = swsscommon.Table(db, "ACL_TABLE") + tbl._del("test_LAG_2") diff --git a/tests/test_crm.py b/tests/test_crm.py index fa859de63b..dca20ad5e0 100644 --- a/tests/test_crm.py +++ b/tests/test_crm.py @@ -499,7 +499,7 @@ def test_CrmAcl(dvs): # 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] + acl_tables = [k for k in atbl.getKeys() if k not in dvs.asicdb.default_acl_tables] key = "ACL_TABLE_STATS:{0}".format(acl_tables[0].replace('oid:', '')) entry_used_counter = getCrmCounterValue(dvs, key, 'crm_stats_acl_entry_used') diff --git a/tests/test_dtel.py b/tests/test_dtel.py new file mode 100644 index 0000000000..8cdebbf425 --- /dev/null +++ b/tests/test_dtel.py @@ -0,0 +1,296 @@ +from swsscommon import swsscommon + + +import time +import re +import json + +class TestDtel(object): + def test_DtelGlobalAttribs(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create DTel global attributes in config db + tbl = swsscommon.Table(db, "DTEL") + + fvs = swsscommon.FieldValuePairs([("SWITCH_ID", "1")]) + tbl.set("SWITCH_ID", fvs) + + fvs = swsscommon.FieldValuePairs([("FLOW_STATE_CLEAR_CYCLE", "10")]) + tbl.set("FLOW_STATE_CLEAR_CYCLE", fvs) + + fvs = swsscommon.FieldValuePairs([("LATENCY_SENSITIVITY", "100")]) + tbl.set("LATENCY_SENSITIVITY", fvs) + + fvs = swsscommon.FieldValuePairs([("INT_ENDPOINT", "TRUE")]) + tbl.set("INT_ENDPOINT", fvs) + + fvs = swsscommon.FieldValuePairs([("INT_TRANSIT", "TRUE")]) + tbl.set("INT_TRANSIT", fvs) + + fvs = swsscommon.FieldValuePairs([("POSTCARD", "TRUE")]) + tbl.set("POSTCARD", fvs) + + fvs = swsscommon.FieldValuePairs([("DROP_REPORT", "TRUE")]) + tbl.set("DROP_REPORT", fvs) + + fvs = swsscommon.FieldValuePairs([("QUEUE_REPORT", "TRUE")]) + tbl.set("QUEUE_REPORT", fvs) + + fvs = swsscommon.FieldValuePairs([("Ethernet0", "Ethernet0"), ("Ethernet4", "Ethernet4")]) + tbl.set("SINK_PORT_LIST", fvs) + + fvs = swsscommon.FieldValuePairs([("INT_L4_DSCP_VALUE", "128"), ("INT_L4_DSCP_MASK", "255")]) + tbl.set("INT_L4_DSCP", fvs) + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_DTEL") + keys = atbl.getKeys() + assert len(keys) > 0 + + for k in keys: + (status, fvs) = atbl.get(k) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_DTEL_ATTR_SWITCH_ID": + assert fv[1] == "1" + elif fv[0] == "SAI_DTEL_ATTR_FLOW_STATE_CLEAR_CYCLE": + assert fv[1] == "10" + elif fv[0] == "SAI_DTEL_ATTR_LATENCY_SENSITIVITY": + assert fv[1] == "100" + elif fv[0] == "SAI_DTEL_ATTR_INT_ENDPOINT_ENABLE": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_ATTR_INT_TRANSIT_ENABLE": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_ATTR_POSTCARD_ENABLE": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_ATTR_DROP_REPORT_ENABLE": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_ATTR_QUEUE_REPORT_ENABLE": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_ATTR_INT_L4_DSCP": + assert fv[1] == "128&mask:0xff" + elif fv[0] == "SAI_DTEL_ATTR_SINK_PORT_LIST": + assert True + + tbl._del("SWITCH_ID") + tbl._del("FLOW_STATE_CLEAR_CYCLE") + tbl._del("LATENCY_SENSITIVITY") + tbl._del("INT_ENDPOINT") + tbl._del("INT_TRANSIT") + tbl._del("POSTCARD") + tbl._del("DROP_REPORT") + tbl._del("QUEUE_REPORT") + tbl._del("SINK_PORT_LIST") + + def test_DtelReportSessionAttribs(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create DTel report session attributes in config db + tbl = swsscommon.Table(db, "DTEL_REPORT_SESSION") + + fvs = swsscommon.FieldValuePairs([("SRC_IP", "10.10.10.1"), + ("DST_IP_LIST", "20.20.20.1;20.20.20.2;20.20.20.3"), + ("VRF", "default"), + ("TRUNCATE_SIZE", "256"), + ("UDP_DEST_PORT", "2000")]) + tbl.set("RS-1", fvs) + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_DTEL_REPORT_SESSION") + keys = atbl.getKeys() + assert len(keys) > 0 + + for k in keys: + (status, fvs) = atbl.get(k) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_DTEL_REPORT_SESSION_ATTR_SRC_IP": + assert fv[1] == "10.10.10.1" + elif fv[0] == "SAI_DTEL_REPORT_SESSION_ATTR_DST_IP_LIST": + assert fv[1] == "3:20.20.20.1,20.20.20.2,20.20.20.3" + elif fv[0] == "SAI_DTEL_REPORT_SESSION_ATTR_VIRTUAL_ROUTER_ID": + assert True + elif fv[0] == "SAI_DTEL_REPORT_SESSION_ATTR_TRUNCATE_SIZE": + assert fv[1] == "256" + elif fv[0] == "SAI_DTEL_REPORT_SESSION_ATTR_UDP_DST_PORT": + assert fv[1] == "2000" + else: + assert False + + tbl._del("RS-1") + + def test_DtelINTSessionAttribs(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create DTel INT session attributes in config db + tbl = swsscommon.Table(db, "DTEL_INT_SESSION") + + fvs = swsscommon.FieldValuePairs([("MAX_HOP_COUNT", "50"), + ("COLLECT_SWITCH_ID", "TRUE"), + ("COLLECT_INGRESS_TIMESTAMP", "TRUE"), + ("COLLECT_EGRESS_TIMESTAMP", "TRUE"), + ("COLLECT_SWITCH_PORTS", "TRUE"), + ("COLLECT_QUEUE_INFO", "TRUE")]) + tbl.set("INT-1", fvs) + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_DTEL_INT_SESSION") + keys = atbl.getKeys() + assert len(keys) > 0 + + for k in keys: + (status, fvs) = atbl.get(k) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_DTEL_INT_SESSION_ATTR_MAX_HOP_COUNT": + assert fv[1] == "50" + elif fv[0] == "SAI_DTEL_INT_SESSION_ATTR_COLLECT_SWITCH_ID": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_INT_SESSION_ATTR_COLLECT_INGRESS_TIMESTAMP": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_INT_SESSION_ATTR_COLLECT_EGRESS_TIMESTAMP": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_INT_SESSION_ATTR_COLLECT_SWITCH_PORTS": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_INT_SESSION_ATTR_COLLECT_QUEUE_INFO": + assert fv[1] == "true" + else: + assert False + + tbl._del("INT-1") + + def test_DtelQueueReportAttribs(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # create DTel queue report attributes in config db + tbl = swsscommon.Table(db, "DTEL_QUEUE_REPORT") + + fvs = swsscommon.FieldValuePairs([("QUEUE_DEPTH_THRESHOLD", "1000"), + ("QUEUE_LATENCY_THRESHOLD", "2000"), + ("THRESHOLD_BREACH_QUOTA", "3000"), + ("REPORT_TAIL_DROP", "TRUE")]) + tbl.set("Ethernet0|0", fvs) + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_DTEL_QUEUE_REPORT") + keys = atbl.getKeys() + assert len(keys) > 0 + + for k in keys: + (status, fvs) = atbl.get(k) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_DTEL_QUEUE_REPORT_ATTR_DEPTH_THRESHOLD": + assert fv[1] == "1000" + elif fv[0] == "SAI_DTEL_QUEUE_REPORT_ATTR_LATENCY_THRESHOLD": + assert fv[1] == "2000" + elif fv[0] == "SAI_DTEL_QUEUE_REPORT_ATTR_BREACH_QUOTA": + assert fv[1] == "3000" + elif fv[0] == "SAI_DTEL_QUEUE_REPORT_ATTR_TAIL_DROP": + assert fv[1] == "true" + elif fv[0] == "SAI_DTEL_QUEUE_REPORT_ATTR_QUEUE_ID": + assert True + else: + assert False + + tbl._del("Ethernet0|0") + + + def test_DtelEventAttribs(self, dvs): + + db = swsscommon.DBConnector(4, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + # first create DTel report session in config db + rtbl = swsscommon.Table(db, "DTEL_REPORT_SESSION") + + fvs = swsscommon.FieldValuePairs([("SRC_IP", "10.10.10.1"), + ("DST_IP_LIST", "20.20.20.1;20.20.20.2;20.20.20.3"), + ("VRF", "default"), + ("TRUNCATE_SIZE", "256"), + ("UDP_DEST_PORT", "2000")]) + rtbl.set("RS-1", fvs) + + # create DTel event attributes in config db + tbl = swsscommon.Table(db, "DTEL_EVENT") + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "65")]) + tbl.set("EVENT_TYPE_FLOW_STATE", fvs) + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "64")]) + tbl.set("EVENT_TYPE_FLOW_REPORT_ALL_PACKETS", fvs) + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "63")]) + tbl.set("EVENT_TYPE_FLOW_TCPFLAG", fvs) + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "62")]) + tbl.set("EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH", fvs) + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "61")]) + tbl.set("EVENT_TYPE_QUEUE_REPORT_TAIL_DROP", fvs) + + fvs = swsscommon.FieldValuePairs([("EVENT_REPORT_SESSION", "RS-1"), + ("EVENT_DSCP_VALUE", "60")]) + tbl.set("EVENT_TYPE_DROP_REPORT", fvs) + + time.sleep(1) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_DTEL_EVENT") + keys = atbl.getKeys() + assert len(keys) > 0 + + for k in keys: + (status, fvs) = atbl.get(k) + assert status == True + + expected_dscp = None + actual_dscp = None + for fv in fvs: + if fv[0] == "SAI_DTEL_EVENT_ATTR_TYPE": + if fv[1] == "SAI_DTEL_EVENT_TYPE_QUEUE_REPORT_TAIL_DROP": + expected_dscp = "61" + elif fv[1] == "SAI_DTEL_EVENT_TYPE_DROP_REPORT": + expected_dscp = "60" + elif fv[1] == "SAI_DTEL_EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH": + expected_dscp = "62" + elif fv[1] == "SAI_DTEL_EVENT_TYPE_FLOW_TCPFLAG": + expected_dscp = "63" + elif fv[1] == "SAI_DTEL_EVENT_TYPE_FLOW_REPORT_ALL_PACKETS": + expected_dscp = "64" + elif fv[1] == "SAI_DTEL_EVENT_TYPE_FLOW_STATE": + expected_dscp = "65" + elif fv[0] == "SAI_DTEL_EVENT_ATTR_REPORT_SESSION": + assert True + elif fv[0] == "SAI_DTEL_EVENT_ATTR_DSCP_VALUE": + actual_dscp = fv[1] + + assert actual_dscp == expected_dscp + + rtbl._del("RS-1") + tbl._del("EVENT_TYPE_FLOW_STATE") + tbl._del("EVENT_TYPE_FLOW_REPORT_ALL_PACKETS") + tbl._del("EVENT_TYPE_FLOW_TCPFLAG") + tbl._del("EVENT_TYPE_QUEUE_REPORT_THRESHOLD_BREACH") + tbl._del("EVENT_TYPE_QUEUE_REPORT_TAIL_DROP") + tbl._del("EVENT_TYPE_DROP_REPORT") diff --git a/tests/test_interface.py b/tests/test_interface.py index 32202cae79..701ad0cc7c 100644 --- a/tests/test_interface.py +++ b/tests/test_interface.py @@ -1,8 +1,118 @@ from swsscommon import swsscommon + import time -import re import json +class TestInterfaceIpv4Addresses(object): + def test_InterfaceAddIpv4Address(self, dvs): + pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + # assign IP to interface + tbl = swsscommon.Table(cdb, "INTERFACE") + fvs = swsscommon.FieldValuePairs([("NULL", "NULL")]) + tbl.set("Ethernet8|10.0.0.4/31", fvs) + time.sleep(1) + + # check application database + tbl = swsscommon.Table(pdb, "INTF_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 1 + assert intf_entries[0] == "10.0.0.4/31" + + (status, fvs) = tbl.get(tbl.getKeys()[0]) + assert status == True + assert len(fvs) == 2 + for fv in fvs: + if fv[0] == "scope": + assert fv[1] == "global" + elif fv[0] == "family": + assert fv[1] == "IPv4" + else: + assert False + + # check ASIC router interface database + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one port based router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "9100" + + # check ASIC route database + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "10.0.0.4/31": + subnet_found = True + if route["dest"] == "10.0.0.4/32": + ip2me_found = True + + assert subnet_found and ip2me_found + + def test_InterfaceChangeMtu(self, dvs): + pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + tbl = swsscommon.ProducerStateTable(pdb, "PORT_TABLE") + fvs = swsscommon.FieldValuePairs([("mtu", "8888")]) + tbl.set("Ethernet8", fvs) + + time.sleep(1) + + # check ASIC router interface database + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE") + intf_entries = tbl.getKeys() + # one loopback router interface one port based router interface + assert len(intf_entries) == 2 + + for key in intf_entries: + (status, fvs) = tbl.get(key) + assert status == True + # a port based router interface has five field/value tuples + if len(fvs) == 5: + for fv in fvs: + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_TYPE": + assert fv[1] == "SAI_ROUTER_INTERFACE_TYPE_PORT" + if fv[0] == "SAI_ROUTER_INTERFACE_ATTR_MTU": + assert fv[1] == "8888" + + def test_InterfaceRemoveIpv4Address(self, dvs): + pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + # remove IP from interface + tbl = swsscommon.Table(cdb, "INTERFACE") + tbl._del("Ethernet8|10.0.0.4/31") + time.sleep(1) + + # check application database + tbl = swsscommon.Table(pdb, "INTF_TABLE:Ethernet8") + intf_entries = tbl.getKeys() + assert len(intf_entries) == 0 + + # check ASIC database + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ROUTE_ENTRY") + for key in tbl.getKeys(): + route = json.loads(key) + if route["dest"] == "10.0.0.4/31": + assert False + if route["dest"] == "10.0.0.4/32": + assert False + + def test_InterfaceIpChange(dvs): dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") diff --git a/tests/test_mirror.py b/tests/test_mirror.py new file mode 100644 index 0000000000..2ea299b0e8 --- /dev/null +++ b/tests/test_mirror.py @@ -0,0 +1,233 @@ +# This test suite covers the functionality of mirror feature in SwSS + +import time + +from swsscommon import swsscommon + + +class TestMirror(object): + def get_acl_table_id(self, dvs): + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + keys = tbl.getKeys() + for k in dvs.asicdb.default_acl_tables: + assert k in keys + + acl_tables = [k for k in keys if k not in dvs.asicdb.default_acl_tables] + assert len(acl_tables) == 1 + + return acl_tables[0] + + def get_mirror_session_id(self, dvs): + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + mirror_sessions = tbl.getKeys() + assert len(mirror_sessions) == 1 + + return mirror_sessions[0] + + def test_AclMirrorTableCreation(self, dvs): + pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + bind_ports = ["Ethernet0", "Ethernet4"] + tbl = swsscommon.Table(cdb, "ACL_TABLE") + + fvs = swsscommon.FieldValuePairs([("POLICY_DESC", "MIRROR_TEST"), + ("TYPE", "MIRROR"), + ("PORTS", ",".join(bind_ports))]) + # create the ACL table + tbl.set("EVERFLOW_TABLE", fvs) + time.sleep(1) + + # assert the table is created + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE_GROUP") + acl_table_ids = tbl.getKeys() + assert len(acl_table_ids) == 2 + + tbl = swsscommon.Table(cdb, "MIRROR_SESSION") + fvs = swsscommon.FieldValuePairs([("src_ip", "10.1.0.32"), + ("dst_ip", "10.20.30.40")]) + # create the mirror session + tbl.set("EVERFLOW_SESSION", fvs) + time.sleep(1) + + # assert the mirror session is created + tbl = swsscommon.Table(pdb, "MIRROR_SESSION") + mirror_sessions = tbl.getKeys() + assert len(mirror_sessions) == 1 + + # assert the mirror session is inactive + (status, fvs) = tbl.get(mirror_sessions[0]) + assert status == True + assert len(fvs) == 1 + for fv in fvs: + if fv[0] == "status": + assert fv[1] == "inactive" + else: + assert False + + def test_MirrorSessionActivation(self, dvs): + # assign the IP address to Ethernet0 + dvs.runcmd("ifconfig Ethernet0 10.0.0.0/24 up") + time.sleep(1) + + pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + tbl = swsscommon.ProducerStateTable(pdb, "NEIGH_TABLE") + fvs = swsscommon.FieldValuePairs([("NEIGH", "02:04:06:08:10:12"), + ("FAMILY", "IPv4")]) + # create the neighbor entry associated with Ethernet0 + tbl.set("Ethernet0:10.0.0.1", fvs) + time.sleep(1) + + # add the route of mirror session destination via the neighbor + dvs.runcmd("ip route add 10.20.30.40/32 via 10.0.0.1") + time.sleep(1) + + # assert the mirror session is active + tbl = swsscommon.Table(pdb, "MIRROR_SESSION") + (status, fvs) = tbl.get("EVERFLOW_SESSION") + assert status == True + assert len(fvs) == 1 + for fv in fvs: + if fv[0] == "status": + fv[1] == "active" + + # assert the mirror session is created + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + mirror_session_ids = tbl.getKeys() + assert len(mirror_session_ids) == 1 + + def test_AclRuleDscpWithoutMask(self, dvs): + + """ + hmset ACL_RULE|EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_1 priority 1000 PACKET_ACTION FORWARD DSCP 48 + """ + + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + tbl = swsscommon.Table(cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("PRIORITY", "1000"), + ("MIRROR_ACTION", "EVERFLOW_SESSION"), + ("DSCP", "48")]) + # create the ACL rule contains DSCP match field with a mask + tbl.set("EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_1", fvs) + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs) + test_mirror_session_id = self.get_mirror_session_id(dvs) + + # assert the ACL rule is created + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + acl_entries = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entries) == 1 + + # assert the ACL rule content is correct + (status, fvs) = atbl.get(acl_entries[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] == "1000" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_DSCP": + assert fv[1] == "48&mask:0x3f" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS": + assert fv[1] == "1:" + test_mirror_session_id + else: + assert False + + # remove the ACL rule + tbl._del("EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_1") + time.sleep(1) + + # assert the ACL rule is removed + (status, fvs) = atbl.get(acl_entries[0]) + assert status == False + + def test_AclRuleDscpWithMask(self, dvs): + + """ + hmset ACL_RULE|EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_2 priority 1000 PACKET_ACTION FORWARD DSCP 16/16 + """ + + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + tbl = swsscommon.Table(cdb, "ACL_RULE") + fvs = swsscommon.FieldValuePairs([("PRIORITY", "1000"), + ("MIRROR_ACTION", "EVERFLOW_SESSION"), + ("DSCP", "16/16")]) + # create the ACL rule contains DSCP match field with a mask + tbl.set("EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_2", fvs) + time.sleep(1) + + test_acl_table_id = self.get_acl_table_id(dvs) + test_mirror_session_id = self.get_mirror_session_id(dvs) + + # assert the ACL rule is created + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_ENTRY") + keys = atbl.getKeys() + acl_entries = [k for k in keys if k not in dvs.asicdb.default_acl_entries] + assert len(acl_entries) == 1 + + # assert the ACL rule content is correct + (status, fvs) = atbl.get(acl_entries[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] == "1000" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_COUNTER": + assert True + elif fv[0] == "SAI_ACL_ENTRY_ATTR_FIELD_DSCP": + assert fv[1] == "16&mask:0x10" + elif fv[0] == "SAI_ACL_ENTRY_ATTR_ACTION_MIRROR_INGRESS": + assert fv[1] == "1:" + test_mirror_session_id + else: + assert False + + # remove the ACL rule + tbl._del("EVERFLOW_TABLE|EVERFLOW_DSCP_TEST_RULE_2") + time.sleep(1) + + # assert the ACL rule is removed + (status, fvs) = atbl.get(acl_entries[0]) + assert status == False + + def test_AclMirrorTableDeletion(self, dvs): + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + tbl = swsscommon.Table(cdb, "ACL_TABLE") + # remove the ACL table + tbl._del("EVERFLOW_TABLE") + time.sleep(1) + + # assert the ACL table is removed + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_ACL_TABLE") + acl_table_ids = tbl.getKeys() + assert len(acl_table_ids) == 3 + + tbl = swsscommon.Table(cdb, "MIRROR_SESSION") + # remove the mirror session + tbl._del("EVERFLOW_SESSION") + time.sleep(1) + + # assert the mirror session is created + tbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_MIRROR_SESSION") + mirror_session_ids = tbl.getKeys() + assert len(mirror_session_ids) == 0 diff --git a/tests/test_nhg.py b/tests/test_nhg.py index bf41c793d2..75f336d027 100644 --- a/tests/test_nhg.py +++ b/tests/test_nhg.py @@ -10,9 +10,9 @@ def test_route_nhg(dvs): 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.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 @@ -67,7 +67,7 @@ def test_route_nhg(dvs): 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 @@ -103,7 +103,7 @@ def test_route_nhg(dvs): time.sleep(1) tbl = swsscommon.Table(db, "PORT_TABLE") - (status, fvs) = tbl.get("Ethernet0") + (status, fvs) = tbl.get("Ethernet%d" % (i * 4)) assert status == True diff --git a/tests/test_port.py b/tests/test_port.py index 9a1e3f61ce..ce841800c3 100644 --- a/tests/test_port.py +++ b/tests/test_port.py @@ -1,7 +1,28 @@ from swsscommon import swsscommon + import time import os +class TestPort(object): + def test_PortMtu(self, dvs): + pdb = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + cdb = swsscommon.DBConnector(4, dvs.redis_sock, 0) + + # set MTU to port + tbl = swsscommon.Table(cdb, "PORT") + fvs = swsscommon.FieldValuePairs([("MTU", "9100")]) + tbl.set("Ethernet8", fvs) + time.sleep(1) + + # check application database + tbl = swsscommon.Table(pdb, "PORT_TABLE") + (status, fvs) = tbl.get("Ethernet8") + assert status == True + for fv in fvs: + if fv[0] == "mtu": + assert fv[1] == "9100" + def test_PortNotification(dvs): dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") == 0 @@ -44,3 +65,63 @@ def test_PortNotification(dvs): break assert oper_status == "up" + +def test_PortFec(dvs): + + dvs.runcmd("ifconfig Ethernet0 10.0.0.0/31 up") == 0 + dvs.runcmd("ifconfig Ethernet4 10.0.0.2/31 up") == 0 + + dvs.servers[0].runcmd("ip link set down dev eth0") == 0 + + time.sleep(1) + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + tbl = swsscommon.Table(db, "PORT_TABLE") + ptbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + + (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 == "down" + + dvs.servers[0].runcmd("ip link set up dev eth0") == 0 + + time.sleep(1) + + (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" + + # set fec + fvs = swsscommon.FieldValuePairs([("fec","rs"), ("speed", "1000")]) + ptbl.set("Ethernet0", fvs) + + time.sleep(1) + + # get fec + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_FEC_MODE": + assert fv[1] == "SAI_PORT_FEC_MODE_RS" diff --git a/tests/test_port_an.py b/tests/test_port_an.py new file mode 100644 index 0000000000..b9912caf17 --- /dev/null +++ b/tests/test_port_an.py @@ -0,0 +1,85 @@ +from swsscommon import swsscommon +import time +import os + +def test_PortAutoNeg(dvs): + + db = swsscommon.DBConnector(0, dvs.redis_sock, 0) + + tbl = swsscommon.ProducerStateTable(db, "PORT_TABLE") + + # set autoneg = false and speed = 1000 + fvs = swsscommon.FieldValuePairs([("autoneg","1"), ("speed", "1000")]) + + tbl.set("Ethernet0", fvs) + + time.sleep(1) + + adb = swsscommon.DBConnector(1, dvs.redis_sock, 0) + + atbl = swsscommon.Table(adb, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + assert "SAI_PORT_ATTR_AUTO_NEG_MODE" in [fv[0] for fv in fvs] + assert "SAI_PORT_ATTR_ADVERTISED_SPEED" in [fv[0] for fv in fvs] + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "true" + elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": + assert fv[1] == "1:1000" + + # set speed = 100 + fvs = swsscommon.FieldValuePairs([("speed", "100")]) + + tbl.set("Ethernet0", fvs) + + time.sleep(1) + + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "true" + elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": + assert fv[1] == "1:100" + + # change autoneg to false + fvs = swsscommon.FieldValuePairs([("autoneg","0")]) + + tbl.set("Ethernet0", fvs) + + time.sleep(1) + + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + assert "SAI_PORT_ATTR_AUTO_NEG_MODE" in [fv[0] for fv in fvs] + assert "SAI_PORT_ATTR_ADVERTISED_SPEED" in [fv[0] for fv in fvs] + assert "SAI_PORT_ATTR_SPEED" in [fv[0] for fv in fvs] + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "false" + elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": + assert fv[1] == "1:100" + elif fv[0] == "SAI_PORT_ATTR_SPEED": + assert fv[1] == "100" + + # set speed = 1000 + fvs = swsscommon.FieldValuePairs([("speed", "1000")]) + + tbl.set("Ethernet0", fvs) + + time.sleep(1) + + (status, fvs) = atbl.get(dvs.asicdb.portnamemap["Ethernet0"]) + assert status == True + + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_AUTO_NEG_MODE": + assert fv[1] == "false" + elif fv[0] == "SAI_PORT_ATTR_ADVERTISED_SPEED": + assert fv[1] == "1:100" + elif fv[0] == "SAI_PORT_ATTR_SPEED": + assert fv[1] == "1000" diff --git a/tests/test_port_buffer_rel.py b/tests/test_port_buffer_rel.py new file mode 100644 index 0000000000..d8c3497121 --- /dev/null +++ b/tests/test_port_buffer_rel.py @@ -0,0 +1,33 @@ +from swsscommon import swsscommon +import time + +# The test check that the ports will be up, when the admin state is UP by conf db. + +def test_PortsAreUpAfterBuffers(dvs): + num_ports = 32 + asic_db = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + + conf_port_table = swsscommon.Table(conf_db, "PORT") + asic_port_table = swsscommon.Table(asic_db, "ASIC_STATE:SAI_OBJECT_TYPE_PORT") + + # enable all ports + fvs = swsscommon.FieldValuePairs([("admin_status", "up")]) + for i in range(0, num_ports): + conf_port_table.set("Ethernet%d" % (i*4), fvs) + + time.sleep(5) + + # check that the ports are enabled in ASIC + asic_port_records = asic_port_table.getKeys() + assert len(asic_port_records) == (num_ports + 1), "Number of port records doesn't match number of ports" # +CPU port + num_set = 0 + for k in asic_port_records: + status, fvs = asic_port_table.get(k) + assert status, "Got an error when get a key" + for fv in fvs: + if fv[0] == "SAI_PORT_ATTR_ADMIN_STATE": + assert fv[1] == "true", "The port isn't UP as expected" + num_set += 1 + # make sure that state is set for all "num_ports" ports + assert num_set == num_ports, "Not all ports are up" diff --git a/tests/test_tunnel.py b/tests/test_tunnel.py new file mode 100644 index 0000000000..5b21b9e993 --- /dev/null +++ b/tests/test_tunnel.py @@ -0,0 +1,227 @@ +from swsscommon import swsscommon +import time + +def create_fvs(**kwargs): + return swsscommon.FieldValuePairs(kwargs.items()) + +class TestTunnelBase(object): + APP_TUNNEL_DECAP_TABLE_NAME = "TUNNEL_DECAP_TABLE" + ASIC_TUNNEL_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL" + ASIC_TUNNEL_TERM_ENTRIES = "ASIC_STATE:SAI_OBJECT_TYPE_TUNNEL_TERM_TABLE_ENTRY" + ASIC_RIF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_ROUTER_INTERFACE" + ASIC_VRF_TABLE = "ASIC_STATE:SAI_OBJECT_TYPE_VIRTUAL_ROUTER" + + ecn_modes_map = { + "standard" : "SAI_TUNNEL_DECAP_ECN_MODE_STANDARD", + "copy_from_outer": "SAI_TUNNEL_DECAP_ECN_MODE_COPY_FROM_OUTER" + } + + dscp_modes_map = { + "pipe" : "SAI_TUNNEL_DSCP_MODE_PIPE_MODEL", + "uniform" : "SAI_TUNNEL_DSCP_MODE_UNIFORM_MODEL" + } + + ttl_modes_map = { + "pipe" : "SAI_TUNNEL_TTL_MODE_PIPE_MODEL", + "uniform" : "SAI_TUNNEL_TTL_MODE_UNIFORM_MODEL" + } + + + def check_interface_exists_in_asicdb(self, asicdb, sai_oid): + if_table = swsscommon.Table(asicdb, self.ASIC_RIF_TABLE) + status, fvs = if_table.get(sai_oid) + return status + + def check_vr_exists_in_asicdb(self, asicdb, sai_oid): + vfr_table = swsscommon.Table(asicdb, self.ASIC_VRF_TABLE) + status, fvs = vfr_table.get(sai_oid) + return status + + def check_tunnel_termination_entry_exists_in_asicdb(self, asicdb, tunnel_sai_oid, dst_ips): + tunnel_term_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TERM_ENTRIES) + + tunnel_term_entries = tunnel_term_table.getKeys() + assert len(tunnel_term_entries) == len(dst_ips) + + for term_entry in tunnel_term_entries: + status, fvs = tunnel_term_table.get(term_entry) + + assert status == True + assert len(fvs) == 5 + + for field, value in fvs: + if field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_VR_ID": + assert self.check_vr_exists_in_asicdb(asicdb, value) + elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TYPE": + assert value == "SAI_TUNNEL_TERM_TABLE_ENTRY_TYPE_P2MP" + elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_TUNNEL_TYPE": + assert value == "SAI_TUNNEL_TYPE_IPINIP" + elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_ACTION_TUNNEL_ID": + assert value == tunnel_sai_oid + elif field == "SAI_TUNNEL_TERM_TABLE_ENTRY_ATTR_DST_IP": + assert value in dst_ips + else: + assert False, "Field %s is not tested" % field + + def create_and_test_tunnel(self, db, asicdb, tunnel_name, **kwargs): + """ Create tunnel and verify all needed enties in ASIC DB exists """ + + is_symmetric_tunnel = "src_ip" in kwargs; + + # create tunnel entry in DB + ps = swsscommon.ProducerStateTable(db, self.APP_TUNNEL_DECAP_TABLE_NAME) + + fvs = create_fvs(**kwargs) + + ps.set(tunnel_name, fvs) + + # wait till config will be applied + time.sleep(1) + + # check asic db table + tunnel_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TABLE) + + tunnels = tunnel_table.getKeys() + assert len(tunnels) == 1 + + tunnel_sai_obj = tunnels[0] + + status, fvs = tunnel_table.get(tunnel_sai_obj) + + assert status == True + # 6 parameters to check in case of decap tunnel + # + 1 (SAI_TUNNEL_ATTR_ENCAP_SRC_IP) in case of symmetric tunnel + assert len(fvs) == 7 if is_symmetric_tunnel else 6 + + expected_ecn_mode = self.ecn_modes_map[kwargs["ecn_mode"]] + expected_dscp_mode = self.dscp_modes_map[kwargs["dscp_mode"]] + expected_ttl_mode = self.ttl_modes_map[kwargs["ttl_mode"]] + + for field, value in fvs: + if field == "SAI_TUNNEL_ATTR_TYPE": + assert value == "SAI_TUNNEL_TYPE_IPINIP" + elif field == "SAI_TUNNEL_ATTR_ENCAP_SRC_IP": + assert value == kwargs["src_ip"] + elif field == "SAI_TUNNEL_ATTR_DECAP_ECN_MODE": + assert value == expected_ecn_mode + elif field == "SAI_TUNNEL_ATTR_DECAP_TTL_MODE": + assert value == expected_ttl_mode + elif field == "SAI_TUNNEL_ATTR_DECAP_DSCP_MODE": + assert value == expected_dscp_mode + elif field == "SAI_TUNNEL_ATTR_OVERLAY_INTERFACE": + assert self.check_interface_exists_in_asicdb(asicdb, value) + elif field == "SAI_TUNNEL_ATTR_UNDERLAY_INTERFACE": + assert self.check_interface_exists_in_asicdb(asicdb, value) + else: + assert False, "Field %s is not tested" % field + + self.check_tunnel_termination_entry_exists_in_asicdb(asicdb, tunnel_sai_obj, kwargs["dst_ip"].split(",")) + + def remove_and_test_tunnel(self, db, asicdb, tunnel_name): + """ Removes tunnel and checks that ASIC db is clear""" + + tunnel_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TABLE) + tunnel_term_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TERM_ENTRIES) + tunnel_app_table = swsscommon.Table(asicdb, self.APP_TUNNEL_DECAP_TABLE_NAME) + + tunnels = tunnel_table.getKeys() + tunnel_sai_obj = tunnels[0] + + status, fvs = tunnel_table.get(tunnel_sai_obj) + + # get overlay loopback interface oid to check if it is deleted with the tunnel + overlay_infs_id = {f:v for f,v in fvs}["SAI_TUNNEL_ATTR_OVERLAY_INTERFACE"] + + ps = swsscommon.ProducerStateTable(db, self.APP_TUNNEL_DECAP_TABLE_NAME) + ps.set(tunnel_name, create_fvs(), 'DEL') + + # wait till config will be applied + time.sleep(1) + + assert len(tunnel_table.getKeys()) == 0 + assert len(tunnel_term_table.getKeys()) == 0 + assert len(tunnel_app_table.getKeys()) == 0 + assert not self.check_interface_exists_in_asicdb(asicdb, overlay_infs_id) + + + def cleanup_left_over(self, db, asicdb): + """ Cleanup APP and ASIC tables """ + + tunnel_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TABLE) + for key in tunnel_table.getKeys(): + tunnel_table._del(key) + + tunnel_term_table = swsscommon.Table(asicdb, self.ASIC_TUNNEL_TERM_ENTRIES) + for key in tunnel_term_table.getKeys(): + tunnel_term_table._del(key) + + tunnel_app_table = swsscommon.Table(asicdb, self.APP_TUNNEL_DECAP_TABLE_NAME) + for key in tunnel_app_table.getKeys(): + tunnel_table._del(key) + + +class TestDecapTunnel(TestTunnelBase): + """ Tests for decap tunnel creation and removal """ + + def test_TunnelDecap_v4(self, dvs): + """ test IPv4 tunnel creation """ + + db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + asicdb = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + self.cleanup_left_over(db, asicdb) + + # create tunnel IPv4 tunnel + self.create_and_test_tunnel(db, asicdb, tunnel_name="IPINIPv4Decap", tunnel_type="IPINIP", + dst_ip="2.2.2.2,3.3.3.3", dscp_mode="uniform", + ecn_mode="standard", ttl_mode="pipe") + self.remove_and_test_tunnel(db, asicdb, "IPINIPv4Decap") + + def test_TunnelDecap_v6(self, dvs): + """ test IPv6 tunnel creation """ + + db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + asicdb = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + self.cleanup_left_over(db, asicdb) + + # create tunnel IPv6 tunnel + self.create_and_test_tunnel(db, asicdb, tunnel_name="IPINIPv6Decap", tunnel_type="IPINIP", + dst_ip="2::2,3::3", dscp_mode="pipe", + ecn_mode="copy_from_outer", ttl_mode="uniform") + self.remove_and_test_tunnel(db, asicdb,"IPINIPv6Decap") + + +class TestSymmetricTunnel(TestTunnelBase): + """ Tests for symmetric tunnel creation and removal """ + + def test_TunnelSymmetric_v4(self, dvs): + """ test IPv4 tunnel creation """ + + db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + asicdb = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + self.cleanup_left_over(db, asicdb) + + # create tunnel IPv4 tunnel + self.create_and_test_tunnel(db, asicdb, tunnel_name="IPINIPv4Symmetric", tunnel_type="IPINIP", + src_ip="1.1.1.1", + dst_ip="2.2.2.2,3.3.3.3", dscp_mode="pipe", + ecn_mode="copy_from_outer", ttl_mode="uniform") + self.remove_and_test_tunnel(db, asicdb, "IPINIPv4Symmetric") + + def test_TunnelSymmetric_v6(self, dvs): + """ test IPv6 tunnel creation """ + + db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + asicdb = swsscommon.DBConnector(swsscommon.ASIC_DB, dvs.redis_sock, 0) + + self.cleanup_left_over(db, asicdb) + + # create tunnel IPv6 tunnel + self.create_and_test_tunnel(db, asicdb, tunnel_name="IPINIPv6Symmetric", tunnel_type="IPINIP", + src_ip="1::1", + dst_ip="2::2,3::3", dscp_mode="uniform", + ecn_mode="standard", ttl_mode="pipe") + self.remove_and_test_tunnel(db, asicdb, "IPINIPv6Symmetric") + diff --git a/tests/test_warm_reboot.py b/tests/test_warm_reboot.py new file mode 100644 index 0000000000..87499139ca --- /dev/null +++ b/tests/test_warm_reboot.py @@ -0,0 +1,202 @@ +from swsscommon import swsscommon +import os +import re +import time +import json + +# Get restart count of all processes supporting warm restart +def swss_get_RestartCount(state_db): + restart_count = {} + warmtbl = swsscommon.Table(state_db, swsscommon.STATE_WARM_RESTART_TABLE_NAME) + keys = warmtbl.getKeys() + assert len(keys) != 0 + for key in keys: + (status, fvs) = warmtbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "restart_count": + restart_count[key] = int(fv[1]) + print(restart_count) + return restart_count + +# function to check the restart count incremented by 1 for all processes supporting warm restart +def swss_check_RestartCount(state_db, restart_count): + warmtbl = swsscommon.Table(state_db, swsscommon.STATE_WARM_RESTART_TABLE_NAME) + keys = warmtbl.getKeys() + print(keys) + assert len(keys) > 0 + for key in keys: + (status, fvs) = warmtbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "restart_count": + assert int(fv[1]) == restart_count[key] + 1 + elif fv[0] == "state": + assert fv[1] == "reconciled" + +def check_port_oper_status(appl_db, port_name, state): + portTbl = swsscommon.Table(appl_db, swsscommon.APP_PORT_TABLE_NAME) + (status, fvs) = portTbl.get(port_name) + assert status == True + + oper_status = "unknown" + for v in fvs: + if v[0] == "oper_status": + oper_status = v[1] + break + assert oper_status == state + +# function to check the restart count incremented by 1 for a single process +def swss_app_check_RestartCount_single(state_db, restart_count, name): + warmtbl = swsscommon.Table(state_db, swsscommon.STATE_WARM_RESTART_TABLE_NAME) + keys = warmtbl.getKeys() + print(keys) + print(restart_count) + assert len(keys) > 0 + for key in keys: + if key != name: + continue + (status, fvs) = warmtbl.get(key) + assert status == True + for fv in fvs: + if fv[0] == "restart_count": + assert int(fv[1]) == restart_count[key] + 1 + elif fv[0] == "state": + assert fv[1] == "reconciled" +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 del_entry_tbl(db, table, key): + tbl = swsscommon.Table(db, table) + tbl._del(key) + +def create_entry_pst(db, table, key, pairs): + tbl = swsscommon.ProducerStateTable(db, table) + create_entry(tbl, key, pairs) + +def how_many_entries_exist(db, table): + tbl = swsscommon.Table(db, table) + return len(tbl.getKeys()) + + +def test_VlanMgrdWarmRestart(dvs): + + conf_db = swsscommon.DBConnector(swsscommon.CONFIG_DB, dvs.redis_sock, 0) + appl_db = swsscommon.DBConnector(swsscommon.APPL_DB, dvs.redis_sock, 0) + state_db = swsscommon.DBConnector(swsscommon.STATE_DB, dvs.redis_sock, 0) + + dvs.runcmd("ifconfig Ethernet16 0") + dvs.runcmd("ifconfig Ethernet20 0") + + dvs.runcmd("ifconfig Ethernet16 up") + dvs.runcmd("ifconfig Ethernet20 up") + + time.sleep(1) + + # enable warm restart + # TODO: use cfg command to config it + create_entry_tbl( + conf_db, + swsscommon.CFG_WARM_RESTART_TABLE_NAME, "swss", + [ + ("enable", "true"), + ] + ) + + # create vlan + create_entry_tbl( + conf_db, + "VLAN", "Vlan16", + [ + ("vlanid", "16"), + ] + ) + # create vlan + create_entry_tbl( + conf_db, + "VLAN", "Vlan20", + [ + ("vlanid", "20"), + ] + ) + # create vlan member entry in config db. Don't use Ethernet0/4/8/12 as IP configured on them in previous testing. + create_entry_tbl( + conf_db, + "VLAN_MEMBER", "Vlan16|Ethernet16", + [ + ("tagging_mode", "untagged"), + ] + ) + + create_entry_tbl( + conf_db, + "VLAN_MEMBER", "Vlan20|Ethernet20", + [ + ("tagging_mode", "untagged"), + ] + ) + + time.sleep(1) + + dvs.runcmd("ifconfig Vlan16 11.0.0.1/29 up") + dvs.runcmd("ifconfig Vlan20 11.0.0.9/29 up") + + dvs.servers[4].runcmd("ifconfig eth0 11.0.0.2/29") + dvs.servers[4].runcmd("ip route add default via 11.0.0.1") + + dvs.servers[5].runcmd("ifconfig eth0 11.0.0.10/29") + dvs.servers[5].runcmd("ip route add default via 11.0.0.9") + + time.sleep(1) + + # Ping should work between servers via vs vlan interfaces + ping_stats = dvs.servers[4].runcmd("ping -c 1 11.0.0.10") + time.sleep(1) + + tbl = swsscommon.Table(appl_db, "NEIGH_TABLE") + (status, fvs) = tbl.get("Vlan16:11.0.0.2") + assert status == True + + (status, fvs) = tbl.get("Vlan20:11.0.0.10") + assert status == True + + + (exitcode, bv_before) = dvs.runcmd("bridge vlan") + print(bv_before) + + restart_count = swss_get_RestartCount(state_db) + + dvs.runcmd(['sh', '-c', 'pkill -x vlanmgrd; cp /var/log/swss/sairedis.rec /var/log/swss/sairedis.rec.b; echo > /var/log/swss/sairedis.rec']) + dvs.runcmd(['sh', '-c', 'supervisorctl start vlanmgrd']) + time.sleep(2) + + (exitcode, bv_after) = dvs.runcmd("bridge vlan") + assert bv_after == bv_before + + # No create/set/remove operations should be passed down to syncd for vlanmgr warm restart + (exitcode, num) = dvs.runcmd(['sh', '-c', 'grep \|c\| /var/log/swss/sairedis.rec | wc -l']) + assert num == '0\n' + (exitcode, num) = dvs.runcmd(['sh', '-c', 'grep \|s\| /var/log/swss/sairedis.rec | wc -l']) + assert num == '0\n' + (exitcode, num) = dvs.runcmd(['sh', '-c', 'grep \|r\| /var/log/swss/sairedis.rec | wc -l']) + assert num == '0\n' + + #new ip on server 5 + dvs.servers[5].runcmd("ifconfig eth0 11.0.0.11/29") + + # Ping should work between servers via vs vlan interfaces + ping_stats = dvs.servers[4].runcmd("ping -c 1 11.0.0.11") + + # new neighbor learn on VS + (status, fvs) = tbl.get("Vlan20:11.0.0.11") + assert status == True + + swss_app_check_RestartCount_single(state_db, restart_count, "vlanmgrd") diff --git a/warmrestart/warm_restart.cpp b/warmrestart/warm_restart.cpp new file mode 100644 index 0000000000..d249142fb4 --- /dev/null +++ b/warmrestart/warm_restart.cpp @@ -0,0 +1,115 @@ +#include +#include "logger.h" +#include "schema.h" +#include "warm_restart.h" + +namespace swss { + +const WarmStart::WarmStartStateNameMap WarmStart::warmStartStateNameMap = +{ + {INIT, "init"}, + {RESTORED, "restored"}, + {RECONCILED, "reconciled"} +}; + +WarmStart &WarmStart::getInstance() +{ + static WarmStart m_warmStart; + return m_warmStart; +} + +/* + * <1> Upon system reboot, the system enable knob will be checked. + * If enabled, database data will be preserved, if not, database will be flushed. + * No need to check docker level knobs in this case since the whole system is being rebooted . + + * <2> Upon docker service start, first to check system knob. + * if enabled, docker warm start should be performed, otherwise system warm reboot will be ruined. + * If system knob disabled, while docker knob enabled, this is likely an individual docker warm restart request. + + * Within each application which should take care warm start case, + * when the system knob or docker knob enabled, we do further check on the + * actual warm start state ( restart_count), if no warm start state data available, + * the database has been flushed, do cold start. Otherwise warm start. + */ + +// Check warm start flag at the very begining of application, do it once for each process. +bool WarmStart::checkWarmStart(const std::string &app_name, const std::string &docker_name) +{ + auto& warmStart = getInstance(); + + if (warmStart.m_stateDb) + { + return true; + } + + warmStart.m_stateDb = std::make_shared(STATE_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0); + warmStart.m_stateWarmRestartTable = std::unique_ptr
(new Table(warmStart.m_stateDb.get(), STATE_WARM_RESTART_TABLE_NAME)); + + warmStart.m_cfgDb = std::make_shared(CONFIG_DB, swss::DBConnector::DEFAULT_UNIXSOCKET, 0); + warmStart.m_cfgWarmRestartTable = std::unique_ptr
(new Table(warmStart.m_cfgDb.get(), CFG_WARM_RESTART_TABLE_NAME)); + + warmStart.enabled = false; + + std::string value; + // Check system level warm restart config first + warmStart.m_cfgWarmRestartTable->hget("system", "enable", value); + if (value == "true") + { + warmStart.enabled = true; + } + + // docker level warm restart configuration + warmStart.m_cfgWarmRestartTable->hget(docker_name, "enable", value); + if (value == "true") + { + warmStart.enabled = true; + } + + // For cold start, the whole state db will be flushed including warm start table. + // Create the entry for this app here. + if (!warmStart.enabled) + { + warmStart.m_stateWarmRestartTable->hset(app_name, "restart_count", "0"); + return true; + } + + uint32_t restart_count = 0; + warmStart.m_stateWarmRestartTable->hget(app_name, "restart_count", value); + if (value == "") + { + SWSS_LOG_WARN("%s doing warm start, but restart_count not found in stateDB %s table, fall back to cold start", + app_name.c_str(), STATE_WARM_RESTART_TABLE_NAME); + warmStart.enabled = false; + warmStart.m_stateWarmRestartTable->hset(app_name, "restart_count", "0"); + return true; + } + else + { + restart_count = (uint32_t)stoul(value); + } + + restart_count++; + warmStart.m_stateWarmRestartTable->hset(app_name, "restart_count", std::to_string(restart_count)); + SWSS_LOG_NOTICE("%s doing warm start, restart count %d", app_name.c_str(), restart_count); + + return true; +} + +bool WarmStart::isWarmStart() +{ + auto& warmStart = getInstance(); + + return warmStart.enabled; +} + +// Set the state restored flag +void WarmStart::setWarmStartState(const std::string &app_name, WarmStartState state) +{ + auto& warmStart = getInstance(); + + warmStart.m_stateWarmRestartTable->hset(app_name, "state", warmStartStateNameMap.at(state).c_str()); + SWSS_LOG_NOTICE("%s warm start state changed to %s", app_name.c_str(), warmStartStateNameMap.at(state).c_str()); +} + +} diff --git a/warmrestart/warm_restart.h b/warmrestart/warm_restart.h new file mode 100644 index 0000000000..32f6865e5d --- /dev/null +++ b/warmrestart/warm_restart.h @@ -0,0 +1,38 @@ +#ifndef SWSS_WARM_RESTART_H +#define SWSS_WARM_RESTART_H + +#include +#include "dbconnector.h" +#include "table.h" + +namespace swss { + +class WarmStart +{ +public: + enum WarmStartState + { + INIT, + RESTORED, + RECONCILED, + }; + + typedef std::map WarmStartStateNameMap; + static const WarmStartStateNameMap warmStartStateNameMap; + + static WarmStart &getInstance(); + + static bool checkWarmStart(const std::string &app_name, const std::string &docker_name = "swss"); + static bool isWarmStart(); + static void setWarmStartState(const std::string &app_name, WarmStartState state); +private: + std::shared_ptr m_stateDb; + std::shared_ptr m_cfgDb; + std::unique_ptr
m_stateWarmRestartTable; + std::unique_ptr
m_cfgWarmRestartTable; + bool enabled; +}; + +} + +#endif