diff --git a/.gitignore b/.gitignore index 2ac0196812af..f7d5b46ee66d 100644 --- a/.gitignore +++ b/.gitignore @@ -43,6 +43,7 @@ deps/ teamsyncd/teamsyncd fpmsyncd/fpmsyncd intfsyncd/intfsyncd +cfgmgr/intfmgrd cfgmgr/vlanmgrd neighsyncd/neighsyncd portsyncd/portsyncd diff --git a/cfgmgr/Makefile.am b/cfgmgr/Makefile.am index c72672fedc28..4bcb57ade2fa 100644 --- a/cfgmgr/Makefile.am +++ b/cfgmgr/Makefile.am @@ -1,7 +1,7 @@ INCLUDES = -I $(top_srcdir) -I $(top_srcdir)/orchagent CFLAGS_SAI = -I /usr/include/sai -bin_PROGRAMS = vlanmgrd +bin_PROGRAMS = vlanmgrd intfmgrd if DEBUG DBGFLAGS = -ggdb -DDEBUG @@ -14,3 +14,7 @@ vlanmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vlanmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) vlanmgrd_LDADD = -lswsscommon +intfmgrd_SOURCES = intfmgrd.cpp intfmgr.cpp $(top_srcdir)/orchagent/orch.cpp shellcmd.h +intfmgrd_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) +intfmgrd_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON) $(CFLAGS_SAI) +intfmgrd_LDADD = -lswsscommon \ No newline at end of file diff --git a/cfgmgr/intfmgr.cpp b/cfgmgr/intfmgr.cpp new file mode 100644 index 000000000000..c56dee07e939 --- /dev/null +++ b/cfgmgr/intfmgr.cpp @@ -0,0 +1,126 @@ +#include +#include "logger.h" +#include "dbconnector.h" +#include "producerstatetable.h" +#include "tokenize.h" +#include "ipprefix.h" +#include "intfmgr.h" +#include "exec.h" +#include "shellcmd.h" + +using namespace std; +using namespace swss; + +#define VLAN_PREFIX "Vlan" +#define LAG_PREFIX "PortChannel" + +IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames) : + Orch(cfgDb, tableNames), + m_cfgIntfTable(cfgDb, CFG_INTF_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), + m_cfgVlanIntfTable(cfgDb, CFG_VLAN_INTF_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), + m_statePortTable(stateDb, STATE_PORT_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), + m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), + m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME, CONFIGDB_TABLE_NAME_SEPARATOR), + m_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME) +{ +} + +bool IntfMgr::setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr) +{ + stringstream cmd; + string res; + + cmd << IP_CMD << " address " << opCmd << " " << ipPrefixStr << " dev " << alias;; + int ret = swss::exec(cmd.str(), res); + return (ret == 0); +} + +bool IntfMgr::isIntfStateOk(const string &alias) +{ + vector temp; + + if (!alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) + { + if (m_stateVlanTable.get(alias, temp)) + { + SWSS_LOG_DEBUG("Vlan %s is ready", alias.c_str()); + return true; + } + } + else if (!alias.compare(0, strlen(LAG_PREFIX), LAG_PREFIX)) + { + if (m_stateLagTable.get(alias, temp)) + { + SWSS_LOG_DEBUG("Lag %s is ready", alias.c_str()); + return true; + } + } + else if (m_statePortTable.get(alias, temp)) + { + SWSS_LOG_DEBUG("Port %s is ready", alias.c_str()); + return true; + } + + return false; +} +void IntfMgr::doTask(Consumer &consumer) +{ + SWSS_LOG_ENTER(); + + auto it = consumer.m_toSync.begin(); + while (it != consumer.m_toSync.end()) + { + KeyOpFieldsValuesTuple t = it->second; + + string keySeparator = CONFIGDB_KEY_SEPARATOR; + vector keys = tokenize(kfvKey(t), keySeparator[0]); + string alias(keys[0]); + + if (alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX)) + { + /* handle IP over vlan Only for now, skip the rest */ + 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 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. + * TODO: Subscribe to stateDB for port/lag/VLAN state and retry + * pending tasks immediately upon state change. + */ + if (!isIntfStateOk(alias)) + { + SWSS_LOG_DEBUG("Interface is not ready, skipping %s", kfvKey(t).c_str()); + it++; + continue; + } + string opCmd("add"); + string ipPrefixStr = ip_prefix.to_string(); + setIntfIp(alias, opCmd, ipPrefixStr); + } + else if (op == DEL_COMMAND) + { + string opCmd("del"); + string ipPrefixStr = ip_prefix.to_string(); + setIntfIp(alias, opCmd, ipPrefixStr); + } + + it = consumer.m_toSync.erase(it); + continue; + } +} diff --git a/cfgmgr/intfmgr.h b/cfgmgr/intfmgr.h new file mode 100644 index 000000000000..a2c89d0cc241 --- /dev/null +++ b/cfgmgr/intfmgr.h @@ -0,0 +1,30 @@ +#ifndef __INTFMGR__ +#define __INTFMGR__ + +#include "dbconnector.h" +#include "producerstatetable.h" +#include "orch.h" + +#include +#include + +namespace swss { + +class IntfMgr : public Orch +{ +public: + IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector &tableNames); + +private: + ProducerStateTable m_appIntfTableProducer; + Table m_cfgIntfTable, m_cfgVlanIntfTable; + Table m_statePortTable, m_stateLagTable, m_stateVlanTable; + + bool setIntfIp(const string &alias, const string &opCmd, const string &ipPrefixStr); + void doTask(Consumer &consumer); + bool isIntfStateOk(const string &alias); +}; + +} + +#endif diff --git a/cfgmgr/intfmgrd.cpp b/cfgmgr/intfmgrd.cpp new file mode 100644 index 000000000000..fe72d3c5b799 --- /dev/null +++ b/cfgmgr/intfmgrd.cpp @@ -0,0 +1,97 @@ +#include +#include +#include +#include "dbconnector.h" +#include "select.h" +#include "exec.h" +#include "schema.h" +#include "intfmgr.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("intfmgrd"); + SWSS_LOG_ENTER(); + + SWSS_LOG_NOTICE("--- Starting intfmgrd ---"); + + try + { + vector cfg_intf_tables = { + CFG_INTF_TABLE_NAME, + CFG_LAG_INTF_TABLE_NAME, + CFG_VLAN_INTF_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); + + IntfMgr intfmgr(&cfgDb, &appDb, &stateDb, cfg_intf_tables); + + // TODO: add tables in stateDB which interface depends on to monitor list + std::vector cfgOrchList = {&intfmgr}; + + swss::Select s; + for (Orch *o : cfgOrchList) + { + s.addSelectables(o->getSelectables()); + } + + SWSS_LOG_NOTICE("starting main loop"); + while (true) + { + Selectable *sel; + int fd, ret; + + ret = s.select(&sel, &fd, SELECT_TIMEOUT); + if (ret == Select::ERROR) + { + SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); + continue; + } + if (ret == Select::TIMEOUT) + { + ((Orch *)&intfmgr)->doTask(); + continue; + } + + for (Orch *o : cfgOrchList) + { + TableConsumable *c = (TableConsumable *)sel; + if (o->hasSelectable(c)) + { + o->execute(c->getTableName()); + } + } + } + } + catch(const std::exception &e) + { + SWSS_LOG_ERROR("Runtime error: %s", e.what()); + } + return -1; +}