sonic接口管理处理流程同步config_db.json中接口IP信息到内核该部分通过redis的键空间机制订阅键config_db的键事件,将其同步到内核,本进程存在swss docker中。
相关文件:
intfmgrd.cpp
intfmgrd.h
intfmgr.cpp
IntfMgrclass IntfMgr : public Orch{public: IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector<string> &tableNames); using Orch::doTask;private: ProducerStateTable m_appIntfTableProducer;//作为appdb的生产者 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);};}实现//该进程的功能是根据配置文件中的接口地址信息配置linux内核。//使用如下命令进行配置://ip address add 10.254.229.226/27 dev Ethernet20//不支持在加载配置后使用ip address add 命令修改地址,想要修改数据口地址需要更改config_db//后重新reload配置。//本进程的目标是将配置文件的关于接口IP的信息同步到内核。int main(int argc, char **argv){ Logger::linkToDbNative("intfmgrd"); SWSS_LOG_ENTER(); SWSS_LOG_NOTICE("--- Starting intfmgrd ---"); try { //订阅了db4中的三个表格 vector<string> cfg_intf_tables = { CFG_INTF_TABLE_NAME, CFG_LAG_INTF_TABLE_NAME, CFG_VLAN_INTF_TABLE_NAME, }; //连接CONFIG_DB,APPL_DB,STATE_DB,用于读取信息 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<Orch *> cfgOrchList = {&intfmgr}; swss::Select s; for (Orch *o : cfgOrchList)//运行epoll,监听数据库信息 { s.addSelectables(o->getSelectables()); } SWSS_LOG_NOTICE("starting main loop"); while (true) { Selectable *sel; int ret; //一秒超时,即使订阅的三个数据库没有事件发生也要周期性的处理事件,类似于实现了一个1秒定时器 ret = s.select(&sel, SELECT_TIMEOUT); if (ret == Select::ERROR) { SWSS_LOG_NOTICE("Error: %s!", strerror(errno)); continue; } //超时依然需要运行该函数 if (ret == Select::TIMEOUT) { intfmgr.doTask(); continue; } auto *c = (Executor *)sel; c->execute();//执行do-task } } catch(const std::exception &e) { SWSS_LOG_ERROR("Runtime error: %s", e.what()); } return -1;}#define VLAN_PREFIX "Vlan"#define LAG_PREFIX "PortChannel"IntfMgr::IntfMgr(DBConnector *cfgDb, DBConnector *appDb, DBConnector *stateDb, const vector<string> &tableNames) : Orch(cfgDb, tableNames), m_cfgIntfTable(cfgDb, CFG_INTF_TABLE_NAME), m_cfgVlanIntfTable(cfgDb, CFG_VLAN_INTF_TABLE_NAME), m_statePortTable(stateDb, STATE_PORT_TABLE_NAME),//读取port状态信息 m_stateLagTable(stateDb, STATE_LAG_TABLE_NAME),//读取lag状态信息 m_stateVlanTable(stateDb, STATE_VLAN_TABLE_NAME),//读取vlan状态信息 m_appIntfTableProducer(appDb, APP_INTF_TABLE_NAME)//作为appdb的生产者,将配置文件的接口配置信息写入APP_INTF_TABLE_NAME表{}//调用ip address add 10.254.229.226/27 dev Ethernet20命令设置接口IPbool 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);}//从db 6中获取端口状态信息bool IntfMgr::isIntfStateOk(const string &alias){ vector<FieldValueTuple> temp; if (!alias.compare(0, strlen(VLAN_PREFIX), VLAN_PREFIX))//vlan接口 { 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))//lag接口 { 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))//port接口 { SWSS_LOG_DEBUG("Port %s is ready", alias.c_str()); return true; } return false;}//"INTERFACE|Ethernet20|10.254.229.226/27"//"PORTCHANNEL_INTERFACE|PortChannel1|10.8.8.200/24"//该程序会处理INTERFACE,PORTCHANNEL_INTERFACE,VLAN_INTERFACE三类接口信息//配置了IP,interface表示一个三层的概念,包括子接口,vlanif,lag-if,phy-ifvoid 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<string> keys = tokenize(kfvKey(t), keySeparator[0]); string alias(keys[0]);//keys[0]为INTERFACE //vlan接口的ip地址到此结束 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; } //提取key中的10.254.229.226/27部分 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. * 由定时器超时1秒触发一次,只有没有up的端口将会由定时器持续检查。 */ if (!isIntfStateOk(alias))//判断端口是否已经ok { SWSS_LOG_DEBUG("Interface is not ready, skipping %s", kfvKey(t).c_str()); it++; continue; } string opCmd("add"); string ipPrefixStr = ip_prefix.to_string(); //设置接口IP地址 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; }}响应内核接口IP信息变化到APP_DB前面分析过sonic对netlink的相关处理,此处不再赘述。
...